proposalsModule.ts 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743
  1. import { Api, WorkingGroups } from '../Api'
  2. import { v4 as uuid } from 'uuid'
  3. import BN from 'bn.js'
  4. import { ProposalId } from '@joystream/types/proposals'
  5. import { BaseFixture } from '../Fixture'
  6. import { assert } from 'chai'
  7. import { ApplicationId, OpeningId } from '@joystream/types/hiring'
  8. import { WorkerId } from '@joystream/types/working-group'
  9. import { Utils } from '../utils'
  10. import { EventRecord } from '@polkadot/types/interfaces'
  11. export class CreateWorkingGroupLeaderOpeningFixture extends BaseFixture {
  12. private proposer: string
  13. private applicationStake: BN
  14. private roleStake: BN
  15. private workingGroup: string
  16. private result: ProposalId | undefined
  17. constructor(api: Api, proposer: string, applicationStake: BN, roleStake: BN, workingGroup: string) {
  18. super(api)
  19. this.proposer = proposer
  20. this.applicationStake = applicationStake
  21. this.roleStake = roleStake
  22. this.workingGroup = workingGroup
  23. }
  24. public getCreatedProposalId(): ProposalId | undefined {
  25. return this.result
  26. }
  27. public async execute(): Promise<void> {
  28. // Setup
  29. const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8)
  30. const description: string = 'Testing working group lead opening proposal ' + uuid().substring(0, 8)
  31. // Proposal stake calculation
  32. const proposalStake: BN = new BN(100000)
  33. const proposalFee: BN = this.api.estimateProposeCreateWorkingGroupLeaderOpeningFee()
  34. this.api.treasuryTransferBalance(this.proposer, proposalFee.add(proposalStake))
  35. // Proposal creation
  36. const result = await this.api.proposeCreateWorkingGroupLeaderOpening({
  37. account: this.proposer,
  38. title: proposalTitle,
  39. description: description,
  40. proposalStake: proposalStake,
  41. actiavteAt: 'CurrentBlock',
  42. maxActiveApplicants: new BN(10),
  43. maxReviewPeriodLength: new BN(32),
  44. applicationStakingPolicyAmount: this.applicationStake,
  45. applicationCrowdedOutUnstakingPeriodLength: new BN(1),
  46. applicationReviewPeriodExpiredUnstakingPeriodLength: new BN(1),
  47. roleStakingPolicyAmount: this.roleStake,
  48. roleCrowdedOutUnstakingPeriodLength: new BN(1),
  49. roleReviewPeriodExpiredUnstakingPeriodLength: new BN(1),
  50. slashableMaxCount: new BN(1),
  51. slashableMaxPercentPtsPerTime: new BN(100),
  52. fillOpeningSuccessfulApplicantApplicationStakeUnstakingPeriod: new BN(1),
  53. fillOpeningFailedApplicantApplicationStakeUnstakingPeriod: new BN(1),
  54. fillOpeningFailedApplicantRoleStakeUnstakingPeriod: new BN(1),
  55. terminateApplicationStakeUnstakingPeriod: new BN(1),
  56. terminateRoleStakeUnstakingPeriod: new BN(1),
  57. exitRoleApplicationStakeUnstakingPeriod: new BN(1),
  58. exitRoleStakeUnstakingPeriod: new BN(1),
  59. text: uuid().substring(0, 8),
  60. workingGroup: this.workingGroup,
  61. })
  62. this.result = this.api.findProposalCreatedEvent(result.events)
  63. }
  64. }
  65. export class BeginWorkingGroupLeaderApplicationReviewFixture extends BaseFixture {
  66. private proposer: string
  67. private openingId: OpeningId
  68. private workingGroup: string
  69. private result: ProposalId | undefined
  70. constructor(api: Api, proposer: string, openingId: OpeningId, workingGroup: string) {
  71. super(api)
  72. this.proposer = proposer
  73. this.openingId = openingId
  74. this.workingGroup = workingGroup
  75. }
  76. public getCreatedProposalId(): ProposalId | undefined {
  77. return this.result
  78. }
  79. public async execute(): Promise<void> {
  80. // Setup
  81. const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8)
  82. const description: string = 'Testing begin working group lead application review proposal ' + uuid().substring(0, 8)
  83. // Proposal stake calculation
  84. const proposalStake: BN = new BN(25000)
  85. const proposalFee: BN = this.api.estimateProposeBeginWorkingGroupLeaderApplicationReviewFee()
  86. this.api.treasuryTransferBalance(this.proposer, proposalFee.add(proposalStake))
  87. // Proposal creation
  88. const result = await this.api.proposeBeginWorkingGroupLeaderApplicationReview(
  89. this.proposer,
  90. proposalTitle,
  91. description,
  92. proposalStake,
  93. this.openingId,
  94. this.workingGroup
  95. )
  96. this.result = this.api.findProposalCreatedEvent(result.events)
  97. }
  98. }
  99. export class FillLeaderOpeningProposalFixture extends BaseFixture {
  100. private proposer: string
  101. private applicationId: ApplicationId
  102. private firstRewardInterval: BN
  103. private rewardInterval: BN
  104. private payoutAmount: BN
  105. private openingId: OpeningId
  106. private workingGroup: WorkingGroups
  107. private result: ProposalId | undefined
  108. constructor(
  109. api: Api,
  110. proposer: string,
  111. applicationId: ApplicationId,
  112. firstRewardInterval: BN,
  113. rewardInterval: BN,
  114. payoutAmount: BN,
  115. openingId: OpeningId,
  116. workingGroup: WorkingGroups
  117. ) {
  118. super(api)
  119. this.proposer = proposer
  120. this.applicationId = applicationId
  121. this.firstRewardInterval = firstRewardInterval
  122. this.rewardInterval = rewardInterval
  123. this.payoutAmount = payoutAmount
  124. this.openingId = openingId
  125. this.workingGroup = workingGroup
  126. }
  127. public getCreatedProposalId(): ProposalId | undefined {
  128. return this.result
  129. }
  130. public async execute(): Promise<void> {
  131. // Setup
  132. const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8)
  133. const description: string = 'Testing fill opening proposal ' + uuid().substring(0, 8)
  134. const workingGroupString: string = this.api.getWorkingGroupString(this.workingGroup)
  135. // Proposal stake calculation
  136. const proposalStake: BN = new BN(50000)
  137. const proposalFee: BN = this.api.estimateProposeFillLeaderOpeningFee()
  138. this.api.treasuryTransferBalance(this.proposer, proposalFee.add(proposalStake))
  139. const now: BN = await this.api.getBestBlock()
  140. // Proposal creation
  141. const result = await this.api.proposeFillLeaderOpening({
  142. account: this.proposer,
  143. title: proposalTitle,
  144. description: description,
  145. proposalStake: proposalStake,
  146. openingId: this.openingId,
  147. successfulApplicationId: this.applicationId,
  148. amountPerPayout: this.payoutAmount,
  149. nextPaymentAtBlock: now.add(this.firstRewardInterval),
  150. payoutInterval: this.rewardInterval,
  151. workingGroup: workingGroupString,
  152. })
  153. this.result = this.api.findProposalCreatedEvent(result.events)
  154. }
  155. }
  156. export class TerminateLeaderRoleProposalFixture extends BaseFixture {
  157. private proposer: string
  158. private slash: boolean
  159. private workingGroup: WorkingGroups
  160. private result: ProposalId | undefined
  161. constructor(api: Api, proposer: string, slash: boolean, workingGroup: WorkingGroups) {
  162. super(api)
  163. this.proposer = proposer
  164. this.slash = slash
  165. this.workingGroup = workingGroup
  166. }
  167. public getCreatedProposalId(): ProposalId | undefined {
  168. return this.result
  169. }
  170. public async execute(): Promise<void> {
  171. // Setup
  172. const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8)
  173. const description: string = 'Testing begin working group lead application review proposal ' + uuid().substring(0, 8)
  174. const rationale: string = 'Testing leader termination ' + uuid().substring(0, 8)
  175. const workingGroupString: string = this.api.getWorkingGroupString(this.workingGroup)
  176. // assert worker exists
  177. const workerId: WorkerId = (await this.api.getLeadWorkerId(this.workingGroup))!
  178. // Proposal stake calculation
  179. const proposalStake: BN = new BN(100000)
  180. const proposalFee: BN = this.api.estimateProposeTerminateLeaderRoleFee()
  181. this.api.treasuryTransferBalance(this.proposer, proposalFee.add(proposalStake))
  182. // Proposal creation
  183. const result = await this.api.proposeTerminateLeaderRole(
  184. this.proposer,
  185. proposalTitle,
  186. description,
  187. proposalStake,
  188. workerId,
  189. rationale,
  190. this.slash,
  191. workingGroupString
  192. )
  193. this.result = this.api.findProposalCreatedEvent(result.events)
  194. }
  195. }
  196. export class SetLeaderRewardProposalFixture extends BaseFixture {
  197. private proposer: string
  198. private payoutAmount: BN
  199. private workingGroup: WorkingGroups
  200. private result: ProposalId | undefined
  201. constructor(api: Api, proposer: string, payoutAmount: BN, workingGroup: WorkingGroups) {
  202. super(api)
  203. this.proposer = proposer
  204. this.payoutAmount = payoutAmount
  205. this.workingGroup = workingGroup
  206. }
  207. public getCreatedProposalId(): ProposalId | undefined {
  208. return this.result
  209. }
  210. public async execute(): Promise<void> {
  211. // Setup
  212. const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8)
  213. const description: string = 'Testing set leader reward proposal ' + uuid().substring(0, 8)
  214. const workingGroupString: string = this.api.getWorkingGroupString(this.workingGroup)
  215. // assert worker exists?
  216. const workerId: WorkerId = (await this.api.getLeadWorkerId(this.workingGroup))!
  217. // Proposal stake calculation
  218. const proposalStake: BN = new BN(50000)
  219. const proposalFee: BN = this.api.estimateProposeLeaderRewardFee()
  220. this.api.treasuryTransferBalance(this.proposer, proposalFee.add(proposalStake))
  221. // Proposal creation
  222. const result = await this.api.proposeLeaderReward(
  223. this.proposer,
  224. proposalTitle,
  225. description,
  226. proposalStake,
  227. workerId,
  228. this.payoutAmount,
  229. workingGroupString
  230. )
  231. this.result = this.api.findProposalCreatedEvent(result.events)
  232. }
  233. }
  234. export class DecreaseLeaderStakeProposalFixture extends BaseFixture {
  235. private proposer: string
  236. private stakeDecrement: BN
  237. private workingGroup: WorkingGroups
  238. private result: ProposalId | undefined
  239. constructor(api: Api, proposer: string, stakeDecrement: BN, workingGroup: WorkingGroups) {
  240. super(api)
  241. this.proposer = proposer
  242. this.stakeDecrement = stakeDecrement
  243. this.workingGroup = workingGroup
  244. }
  245. public getCreatedProposalId(): ProposalId | undefined {
  246. return this.result
  247. }
  248. public async execute(): Promise<void> {
  249. // Setup
  250. const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8)
  251. const description: string = 'Testing decrease leader stake proposal ' + uuid().substring(0, 8)
  252. const workingGroupString: string = this.api.getWorkingGroupString(this.workingGroup)
  253. // assert worker exists ?
  254. const workerId: WorkerId = (await this.api.getLeadWorkerId(this.workingGroup))!
  255. // Proposal stake calculation
  256. const proposalStake: BN = new BN(50000)
  257. const proposalFee: BN = this.api.estimateProposeDecreaseLeaderStakeFee()
  258. this.api.treasuryTransferBalance(this.proposer, proposalFee.add(proposalStake))
  259. // Proposal creation
  260. const result = await this.api.proposeDecreaseLeaderStake(
  261. this.proposer,
  262. proposalTitle,
  263. description,
  264. proposalStake,
  265. workerId,
  266. this.stakeDecrement,
  267. workingGroupString
  268. )
  269. this.result = this.api.findProposalCreatedEvent(result.events)
  270. }
  271. }
  272. export class SlashLeaderProposalFixture extends BaseFixture {
  273. private proposer: string
  274. private slashAmount: BN
  275. private workingGroup: WorkingGroups
  276. private result: ProposalId | undefined
  277. constructor(api: Api, proposer: string, slashAmount: BN, workingGroup: WorkingGroups) {
  278. super(api)
  279. this.proposer = proposer
  280. this.slashAmount = slashAmount
  281. this.workingGroup = workingGroup
  282. }
  283. public getCreatedProposalId(): ProposalId | undefined {
  284. return this.result
  285. }
  286. public async execute(): Promise<void> {
  287. // Setup
  288. const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8)
  289. const description: string = 'Testing slash leader stake proposal ' + uuid().substring(0, 8)
  290. const workingGroupString: string = this.api.getWorkingGroupString(this.workingGroup)
  291. const workerId: WorkerId = (await this.api.getLeadWorkerId(this.workingGroup))!
  292. // Proposal stake calculation
  293. const proposalStake: BN = new BN(50000)
  294. const proposalFee: BN = this.api.estimateProposeSlashLeaderStakeFee()
  295. this.api.treasuryTransferBalance(this.proposer, proposalFee.add(proposalStake))
  296. // Proposal creation
  297. const result = await this.api.proposeSlashLeaderStake(
  298. this.proposer,
  299. proposalTitle,
  300. description,
  301. proposalStake,
  302. workerId,
  303. this.slashAmount,
  304. workingGroupString
  305. )
  306. this.result = this.api.findProposalCreatedEvent(result.events)
  307. }
  308. }
  309. export class WorkingGroupMintCapacityProposalFixture extends BaseFixture {
  310. private proposer: string
  311. private mintCapacity: BN
  312. private workingGroup: WorkingGroups
  313. private result: ProposalId | undefined
  314. constructor(api: Api, proposer: string, mintCapacity: BN, workingGroup: WorkingGroups) {
  315. super(api)
  316. this.proposer = proposer
  317. this.mintCapacity = mintCapacity
  318. this.workingGroup = workingGroup
  319. }
  320. public getCreatedProposalId(): ProposalId | undefined {
  321. return this.result
  322. }
  323. public async execute(): Promise<void> {
  324. // Setup
  325. const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8)
  326. const description: string = 'Testing working group mint capacity proposal ' + uuid().substring(0, 8)
  327. const workingGroupString: string = this.api.getWorkingGroupString(this.workingGroup)
  328. // Proposal stake calculation
  329. const proposalStake: BN = new BN(50000)
  330. const proposalFee: BN = this.api.estimateProposeWorkingGroupMintCapacityFee()
  331. this.api.treasuryTransferBalance(this.proposer, proposalFee.add(proposalStake))
  332. // Proposal creation
  333. const result = await this.api.proposeWorkingGroupMintCapacity(
  334. this.proposer,
  335. proposalTitle,
  336. description,
  337. proposalStake,
  338. this.mintCapacity,
  339. workingGroupString
  340. )
  341. this.result = this.api.findProposalCreatedEvent(result.events)
  342. }
  343. }
  344. export class ElectionParametersProposalFixture extends BaseFixture {
  345. private proposerAccount: string
  346. constructor(api: Api, proposerAccount: string) {
  347. super(api)
  348. this.proposerAccount = proposerAccount
  349. }
  350. public async execute(): Promise<void> {
  351. // Setup
  352. const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8)
  353. const description: string = 'Testing Election Parameters proposal ' + uuid().substring(0, 8)
  354. const announcingPeriod: BN = new BN(28800)
  355. const votingPeriod: BN = new BN(14400)
  356. const revealingPeriod: BN = new BN(14400)
  357. const councilSize: BN = await this.api.getCouncilSize()
  358. const candidacyLimit: BN = await this.api.getCandidacyLimit()
  359. const newTermDuration: BN = new BN(144000)
  360. const minCouncilStake: BN = await this.api.getMinCouncilStake()
  361. const minVotingStake: BN = await this.api.getMinVotingStake()
  362. // Proposal stake calculation
  363. // Required stake is hardcoded in runtime-module (but not available as const)
  364. const proposalStake: BN = new BN(1000000)
  365. const proposalFee: BN = this.api.estimateProposeElectionParametersFee(
  366. description,
  367. description,
  368. proposalStake,
  369. announcingPeriod,
  370. votingPeriod,
  371. revealingPeriod,
  372. councilSize,
  373. candidacyLimit,
  374. newTermDuration,
  375. minCouncilStake,
  376. minVotingStake
  377. )
  378. this.api.treasuryTransferBalance(this.proposerAccount, proposalFee.add(proposalStake))
  379. // Proposal creation
  380. const proposedAnnouncingPeriod: BN = announcingPeriod.subn(1)
  381. const proposedVotingPeriod: BN = votingPeriod.addn(1)
  382. const proposedRevealingPeriod: BN = revealingPeriod.addn(1)
  383. const proposedCouncilSize: BN = councilSize.addn(1)
  384. const proposedCandidacyLimit: BN = new BN(51) // candidacyLimit.addn(1)
  385. // assert they are different
  386. assert(!candidacyLimit.eq(proposedCandidacyLimit))
  387. const proposedNewTermDuration: BN = newTermDuration.addn(1)
  388. const proposedMinCouncilStake: BN = minCouncilStake.addn(1)
  389. const proposedMinVotingStake: BN = minVotingStake.addn(1)
  390. const proposalCreationResult = await this.api.proposeElectionParameters(
  391. this.proposerAccount,
  392. proposalTitle,
  393. description,
  394. proposalStake,
  395. proposedAnnouncingPeriod,
  396. proposedVotingPeriod,
  397. proposedRevealingPeriod,
  398. proposedCouncilSize,
  399. proposedCandidacyLimit,
  400. proposedNewTermDuration,
  401. proposedMinCouncilStake,
  402. proposedMinVotingStake
  403. )
  404. const proposalNumber = this.api.findProposalCreatedEvent(proposalCreationResult.events) as ProposalId
  405. assert.notEqual(proposalNumber, undefined)
  406. const approveProposalFixture = new VoteForProposalFixture(this.api, proposalNumber)
  407. await approveProposalFixture.execute()
  408. assert(approveProposalFixture.proposalExecuted)
  409. // Assertions
  410. const newAnnouncingPeriod: BN = await this.api.getAnnouncingPeriod()
  411. const newVotingPeriod: BN = await this.api.getVotingPeriod()
  412. const newRevealingPeriod: BN = await this.api.getRevealingPeriod()
  413. const newCouncilSize: BN = await this.api.getCouncilSize()
  414. const newCandidacyLimit: BN = await this.api.getCandidacyLimit()
  415. const newNewTermDuration: BN = await this.api.getNewTermDuration()
  416. const newMinCouncilStake: BN = await this.api.getMinCouncilStake()
  417. const newMinVotingStake: BN = await this.api.getMinVotingStake()
  418. assert(
  419. proposedAnnouncingPeriod.eq(newAnnouncingPeriod),
  420. `Announcing period has unexpected value ${newAnnouncingPeriod}, expected ${proposedAnnouncingPeriod}`
  421. )
  422. assert(
  423. proposedVotingPeriod.eq(newVotingPeriod),
  424. `Voting period has unexpected value ${newVotingPeriod}, expected ${proposedVotingPeriod}`
  425. )
  426. assert(
  427. proposedRevealingPeriod.eq(newRevealingPeriod),
  428. `Revealing has unexpected value ${newRevealingPeriod}, expected ${proposedRevealingPeriod}`
  429. )
  430. assert(
  431. proposedCouncilSize.eq(newCouncilSize),
  432. `Council size has unexpected value ${newCouncilSize}, expected ${proposedCouncilSize}`
  433. )
  434. assert(
  435. proposedCandidacyLimit.eq(newCandidacyLimit),
  436. `Candidacy limit has unexpected value ${newCandidacyLimit}, expected ${proposedCandidacyLimit}`
  437. )
  438. assert(
  439. proposedNewTermDuration.eq(newNewTermDuration),
  440. `New term duration has unexpected value ${newNewTermDuration}, expected ${proposedNewTermDuration}`
  441. )
  442. assert(
  443. proposedMinCouncilStake.eq(newMinCouncilStake),
  444. `Min council stake has unexpected value ${newMinCouncilStake}, expected ${proposedMinCouncilStake}`
  445. )
  446. assert(
  447. proposedMinVotingStake.eq(newMinVotingStake),
  448. `Min voting stake has unexpected value ${newMinVotingStake}, expected ${proposedMinVotingStake}`
  449. )
  450. }
  451. }
  452. export class SpendingProposalFixture extends BaseFixture {
  453. private proposer: string
  454. private spendingBalance: BN
  455. private mintCapacity: BN
  456. constructor(api: Api, proposer: string, spendingBalance: BN, mintCapacity: BN) {
  457. super(api)
  458. this.proposer = proposer
  459. this.spendingBalance = spendingBalance
  460. this.mintCapacity = mintCapacity
  461. }
  462. public async execute(): Promise<void> {
  463. // Setup
  464. const description = 'spending proposal which is used for API network testing with some mock data'
  465. // Topping the balances
  466. const proposalStake: BN = new BN(25000)
  467. const runtimeProposalFee: BN = this.api.estimateProposeSpendingFee(
  468. description,
  469. description,
  470. proposalStake,
  471. this.spendingBalance,
  472. this.proposer
  473. )
  474. this.api.treasuryTransferBalance(this.proposer, runtimeProposalFee.add(proposalStake))
  475. await this.api.sudoSetCouncilMintCapacity(this.mintCapacity)
  476. const fundingRecipient = this.api.createKeyPairs(1)[0].address
  477. // Proposal creation
  478. const result = await this.api.proposeSpending(
  479. this.proposer,
  480. 'testing spending' + uuid().substring(0, 8),
  481. 'spending to test proposal functionality' + uuid().substring(0, 8),
  482. proposalStake,
  483. this.spendingBalance,
  484. fundingRecipient
  485. )
  486. const proposalNumber: ProposalId = this.api.findProposalCreatedEvent(result.events) as ProposalId
  487. assert.notEqual(proposalNumber, undefined)
  488. // Approving spending proposal
  489. const balanceBeforeMinting: BN = await this.api.getBalance(fundingRecipient)
  490. const approveProposalFixture = new VoteForProposalFixture(this.api, proposalNumber)
  491. await approveProposalFixture.execute()
  492. assert(approveProposalFixture.proposalExecuted)
  493. const balanceAfterMinting: BN = await this.api.getBalance(fundingRecipient)
  494. assert(
  495. balanceAfterMinting.eq(balanceBeforeMinting.add(this.spendingBalance)),
  496. `member ${fundingRecipient} has unexpected balance ${balanceAfterMinting}, expected ${balanceBeforeMinting.add(
  497. this.spendingBalance
  498. )}`
  499. )
  500. }
  501. }
  502. export class TextProposalFixture extends BaseFixture {
  503. private proposer: string
  504. constructor(api: Api, proposer: string) {
  505. super(api)
  506. this.proposer = proposer
  507. }
  508. public async execute(): Promise<void> {
  509. // Setup
  510. const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8)
  511. const description: string = 'Testing text proposal ' + uuid().substring(0, 8)
  512. const proposalText: string = 'Text of the testing proposal ' + uuid().substring(0, 8)
  513. // Proposal stake calculation
  514. const proposalStake: BN = new BN(25000)
  515. const runtimeProposalFee: BN = this.api.estimateProposeTextFee(
  516. proposalStake,
  517. description,
  518. description,
  519. proposalText
  520. )
  521. this.api.treasuryTransferBalance(this.proposer, runtimeProposalFee.add(proposalStake))
  522. // Proposal creation
  523. const result = await this.api.proposeText(this.proposer, proposalStake, proposalTitle, description, proposalText)
  524. const proposalNumber = this.api.findProposalCreatedEvent(result.events) as ProposalId
  525. assert.notEqual(proposalNumber, undefined)
  526. // Approving text proposal
  527. const approveProposalFixture = new VoteForProposalFixture(this.api, proposalNumber)
  528. await approveProposalFixture.execute()
  529. assert(approveProposalFixture.proposalExecuted)
  530. }
  531. }
  532. export class ValidatorCountProposalFixture extends BaseFixture {
  533. private proposer: string
  534. private proposedValidatorCount: BN
  535. constructor(api: Api, proposer: string, validatorCount: BN) {
  536. super(api)
  537. this.proposer = proposer
  538. this.proposedValidatorCount = validatorCount
  539. }
  540. public async execute(): Promise<void> {
  541. // Setup
  542. const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8)
  543. const description: string = 'Testing validator count proposal ' + uuid().substring(0, 8)
  544. // Proposal stake calculation
  545. const proposalStake: BN = new BN(500000)
  546. const proposalFee: BN = this.api.estimateProposeValidatorCountFee(description, description, proposalStake)
  547. this.api.treasuryTransferBalance(this.proposer, proposalFee.add(proposalStake))
  548. const currentValidatorCount: BN = await this.api.getValidatorCount()
  549. // Proposal creation
  550. // Make sure proposed is different than current to ensure change is applied.
  551. assert(!currentValidatorCount.eq(this.proposedValidatorCount))
  552. const result = await this.api.proposeValidatorCount(
  553. this.proposer,
  554. proposalTitle,
  555. description,
  556. proposalStake,
  557. this.proposedValidatorCount
  558. )
  559. const proposalNumber: ProposalId = this.api.findProposalCreatedEvent(result.events) as ProposalId
  560. assert.notEqual(proposalNumber, undefined)
  561. // Approving the proposal
  562. const approveProposalFixture = new VoteForProposalFixture(this.api, proposalNumber)
  563. await approveProposalFixture.execute()
  564. assert(approveProposalFixture.proposalExecuted)
  565. const newValidatorCount: BN = await this.api.getValidatorCount()
  566. assert(
  567. this.proposedValidatorCount.eq(newValidatorCount),
  568. `Validator count has unexpeccted value ${newValidatorCount}, expected ${this.proposedValidatorCount}`
  569. )
  570. }
  571. }
  572. export class UpdateRuntimeFixture extends BaseFixture {
  573. private proposer: string
  574. private runtimePath: string
  575. constructor(api: Api, proposer: string, runtimePath: string) {
  576. super(api)
  577. this.proposer = proposer
  578. this.runtimePath = runtimePath
  579. }
  580. public async execute(): Promise<void> {
  581. // Setup
  582. const runtime: string = Utils.readRuntimeFromFile(this.runtimePath)
  583. const description = 'runtime upgrade proposal which is used for API network testing'
  584. // Topping the balances
  585. const proposalStake: BN = new BN(1000000)
  586. const runtimeProposalFee: BN = this.api.estimateProposeRuntimeUpgradeFee(
  587. proposalStake,
  588. description,
  589. description,
  590. runtime
  591. )
  592. this.api.treasuryTransferBalance(this.proposer, runtimeProposalFee.add(proposalStake))
  593. // Proposal creation
  594. const result = await this.api.proposeRuntime(
  595. this.proposer,
  596. proposalStake,
  597. 'testing runtime' + uuid().substring(0, 8),
  598. 'runtime to test proposal functionality' + uuid().substring(0, 8),
  599. runtime
  600. )
  601. const proposalNumber: ProposalId = this.api.findProposalCreatedEvent(result.events) as ProposalId
  602. assert.notEqual(proposalNumber, undefined)
  603. // Approving runtime update proposal
  604. const approveProposalFixture = new VoteForProposalFixture(this.api, proposalNumber)
  605. await approveProposalFixture.execute()
  606. assert(approveProposalFixture.proposalExecuted)
  607. }
  608. }
  609. export class VoteForProposalFixture extends BaseFixture {
  610. private proposalNumber: ProposalId
  611. private _proposalExecuted = false
  612. private _events: EventRecord[] = []
  613. constructor(api: Api, proposalNumber: ProposalId) {
  614. super(api)
  615. this.proposalNumber = proposalNumber
  616. }
  617. get proposalExecuted(): boolean {
  618. return this._proposalExecuted
  619. }
  620. get events(): EventRecord[] {
  621. return this._events
  622. }
  623. public async execute(): Promise<void> {
  624. const proposalVoteFee: BN = this.api.estimateVoteForProposalFee()
  625. const councilAccounts = await this.api.getCouncilAccounts()
  626. this.api.treasuryTransferBalanceToAccounts(councilAccounts, proposalVoteFee)
  627. // Approving the proposal
  628. const onProposalFinalized = this.api.waitForProposalToFinalize(this.proposalNumber)
  629. const approvals = await this.api.batchApproveProposal(this.proposalNumber)
  630. approvals.map((result) => this.expectDispatchSuccess(result, 'Proposal Approval Vote Expected To Be Successful'))
  631. const proposalOutcome = await onProposalFinalized
  632. this._proposalExecuted = proposalOutcome[0]
  633. this._events = proposalOutcome[1]
  634. }
  635. }