Governor.test.js 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821
  1. const { BN, constants, expectEvent, expectRevert, time } = require('@openzeppelin/test-helpers');
  2. const ethSigUtil = require('eth-sig-util');
  3. const Wallet = require('ethereumjs-wallet').default;
  4. const Enums = require('../helpers/enums');
  5. const { EIP712Domain } = require('../helpers/eip712');
  6. const { fromRpcSig } = require('ethereumjs-util');
  7. const {
  8. runGovernorWorkflow,
  9. } = require('./GovernorWorkflow.behavior');
  10. const {
  11. shouldSupportInterfaces,
  12. } = require('../utils/introspection/SupportsInterface.behavior');
  13. const Token = artifacts.require('ERC20VotesMock');
  14. const Governor = artifacts.require('GovernorMock');
  15. const CallReceiver = artifacts.require('CallReceiverMock');
  16. contract('Governor', function (accounts) {
  17. const [ owner, proposer, voter1, voter2, voter3, voter4 ] = accounts;
  18. const name = 'OZ-Governor';
  19. const version = '1';
  20. const tokenName = 'MockToken';
  21. const tokenSymbol = 'MTKN';
  22. const tokenSupply = web3.utils.toWei('100');
  23. beforeEach(async function () {
  24. this.owner = owner;
  25. this.token = await Token.new(tokenName, tokenSymbol);
  26. this.mock = await Governor.new(name, this.token.address, 4, 16, 10);
  27. this.receiver = await CallReceiver.new();
  28. await this.token.mint(owner, tokenSupply);
  29. await this.token.delegate(voter1, { from: voter1 });
  30. await this.token.delegate(voter2, { from: voter2 });
  31. await this.token.delegate(voter3, { from: voter3 });
  32. await this.token.delegate(voter4, { from: voter4 });
  33. });
  34. shouldSupportInterfaces([
  35. 'ERC165',
  36. 'Governor',
  37. ]);
  38. it('deployment check', async function () {
  39. expect(await this.mock.name()).to.be.equal(name);
  40. expect(await this.mock.token()).to.be.equal(this.token.address);
  41. expect(await this.mock.votingDelay()).to.be.bignumber.equal('4');
  42. expect(await this.mock.votingPeriod()).to.be.bignumber.equal('16');
  43. expect(await this.mock.quorum(0)).to.be.bignumber.equal('0');
  44. expect(await this.mock.COUNTING_MODE()).to.be.equal('support=bravo&quorum=for,abstain');
  45. });
  46. describe('scenario', function () {
  47. describe('nominal', function () {
  48. beforeEach(async function () {
  49. this.value = web3.utils.toWei('1');
  50. await web3.eth.sendTransaction({ from: owner, to: this.mock.address, value: this.value });
  51. expect(await web3.eth.getBalance(this.mock.address)).to.be.bignumber.equal(this.value);
  52. expect(await web3.eth.getBalance(this.receiver.address)).to.be.bignumber.equal('0');
  53. this.settings = {
  54. proposal: [
  55. [ this.receiver.address ],
  56. [ this.value ],
  57. [ this.receiver.contract.methods.mockFunction().encodeABI() ],
  58. '<proposal description>',
  59. ],
  60. proposer,
  61. tokenHolder: owner,
  62. voters: [
  63. { voter: voter1, weight: web3.utils.toWei('1'), support: Enums.VoteType.For, reason: 'This is nice' },
  64. { voter: voter2, weight: web3.utils.toWei('7'), support: Enums.VoteType.For },
  65. { voter: voter3, weight: web3.utils.toWei('5'), support: Enums.VoteType.Against },
  66. { voter: voter4, weight: web3.utils.toWei('2'), support: Enums.VoteType.Abstain },
  67. ],
  68. };
  69. this.votingDelay = await this.mock.votingDelay();
  70. this.votingPeriod = await this.mock.votingPeriod();
  71. });
  72. afterEach(async function () {
  73. expect(await this.mock.hasVoted(this.id, owner)).to.be.equal(false);
  74. expect(await this.mock.hasVoted(this.id, voter1)).to.be.equal(true);
  75. expect(await this.mock.hasVoted(this.id, voter2)).to.be.equal(true);
  76. await this.mock.proposalVotes(this.id).then(result => {
  77. for (const [key, value] of Object.entries(Enums.VoteType)) {
  78. expect(result[`${key.toLowerCase()}Votes`]).to.be.bignumber.equal(
  79. Object.values(this.settings.voters).filter(({ support }) => support === value).reduce(
  80. (acc, { weight }) => acc.add(new BN(weight)),
  81. new BN('0'),
  82. ),
  83. );
  84. }
  85. });
  86. expectEvent(
  87. this.receipts.propose,
  88. 'ProposalCreated',
  89. {
  90. proposalId: this.id,
  91. proposer,
  92. targets: this.settings.proposal[0],
  93. // values: this.settings.proposal[1].map(value => new BN(value)),
  94. signatures: this.settings.proposal[2].map(() => ''),
  95. calldatas: this.settings.proposal[2],
  96. startBlock: new BN(this.receipts.propose.blockNumber).add(this.votingDelay),
  97. endBlock: new BN(this.receipts.propose.blockNumber).add(this.votingDelay).add(this.votingPeriod),
  98. description: this.settings.proposal[3],
  99. },
  100. );
  101. this.receipts.castVote.filter(Boolean).forEach(vote => {
  102. const { voter } = vote.logs.find(Boolean).args;
  103. expectEvent(
  104. vote,
  105. 'VoteCast',
  106. this.settings.voters.find(({ address }) => address === voter),
  107. );
  108. });
  109. expectEvent(
  110. this.receipts.execute,
  111. 'ProposalExecuted',
  112. { proposalId: this.id },
  113. );
  114. await expectEvent.inTransaction(
  115. this.receipts.execute.transactionHash,
  116. this.receiver,
  117. 'MockFunctionCalled',
  118. );
  119. expect(await web3.eth.getBalance(this.mock.address)).to.be.bignumber.equal('0');
  120. expect(await web3.eth.getBalance(this.receiver.address)).to.be.bignumber.equal(this.value);
  121. });
  122. runGovernorWorkflow();
  123. });
  124. describe('vote with signature', function () {
  125. beforeEach(async function () {
  126. const chainId = await web3.eth.getChainId();
  127. // generate voter by signature wallet
  128. const voterBySig = Wallet.generate();
  129. this.voter = web3.utils.toChecksumAddress(voterBySig.getAddressString());
  130. // use delegateBySig to enable vote delegation for this wallet
  131. const { v, r, s } = fromRpcSig(ethSigUtil.signTypedMessage(
  132. voterBySig.getPrivateKey(),
  133. {
  134. data: {
  135. types: {
  136. EIP712Domain,
  137. Delegation: [
  138. { name: 'delegatee', type: 'address' },
  139. { name: 'nonce', type: 'uint256' },
  140. { name: 'expiry', type: 'uint256' },
  141. ],
  142. },
  143. domain: { name: tokenName, version: '1', chainId, verifyingContract: this.token.address },
  144. primaryType: 'Delegation',
  145. message: { delegatee: this.voter, nonce: 0, expiry: constants.MAX_UINT256 },
  146. },
  147. },
  148. ));
  149. await this.token.delegateBySig(this.voter, 0, constants.MAX_UINT256, v, r, s);
  150. // prepare signature for vote by signature
  151. const signature = async (message) => {
  152. return fromRpcSig(ethSigUtil.signTypedMessage(
  153. voterBySig.getPrivateKey(),
  154. {
  155. data: {
  156. types: {
  157. EIP712Domain,
  158. Ballot: [
  159. { name: 'proposalId', type: 'uint256' },
  160. { name: 'support', type: 'uint8' },
  161. ],
  162. },
  163. domain: { name, version, chainId, verifyingContract: this.mock.address },
  164. primaryType: 'Ballot',
  165. message,
  166. },
  167. },
  168. ));
  169. };
  170. this.settings = {
  171. proposal: [
  172. [ this.receiver.address ],
  173. [ web3.utils.toWei('0') ],
  174. [ this.receiver.contract.methods.mockFunction().encodeABI() ],
  175. '<proposal description>',
  176. ],
  177. tokenHolder: owner,
  178. voters: [
  179. { voter: this.voter, signature, weight: web3.utils.toWei('10'), support: Enums.VoteType.For },
  180. ],
  181. };
  182. });
  183. afterEach(async function () {
  184. expect(await this.mock.hasVoted(this.id, owner)).to.be.equal(false);
  185. expect(await this.mock.hasVoted(this.id, voter1)).to.be.equal(false);
  186. expect(await this.mock.hasVoted(this.id, voter2)).to.be.equal(false);
  187. expect(await this.mock.hasVoted(this.id, this.voter)).to.be.equal(true);
  188. await this.mock.proposalVotes(this.id).then(result => {
  189. for (const [key, value] of Object.entries(Enums.VoteType)) {
  190. expect(result[`${key.toLowerCase()}Votes`]).to.be.bignumber.equal(
  191. Object.values(this.settings.voters).filter(({ support }) => support === value).reduce(
  192. (acc, { weight }) => acc.add(new BN(weight)),
  193. new BN('0'),
  194. ),
  195. );
  196. }
  197. });
  198. expectEvent(
  199. this.receipts.propose,
  200. 'ProposalCreated',
  201. { proposalId: this.id },
  202. );
  203. expectEvent(
  204. this.receipts.execute,
  205. 'ProposalExecuted',
  206. { proposalId: this.id },
  207. );
  208. await expectEvent.inTransaction(
  209. this.receipts.execute.transactionHash,
  210. this.receiver,
  211. 'MockFunctionCalled',
  212. );
  213. });
  214. runGovernorWorkflow();
  215. });
  216. describe('send ethers', function () {
  217. beforeEach(async function () {
  218. this.receiver = { address: web3.utils.toChecksumAddress(web3.utils.randomHex(20)) };
  219. this.value = web3.utils.toWei('1');
  220. await web3.eth.sendTransaction({ from: owner, to: this.mock.address, value: this.value });
  221. expect(await web3.eth.getBalance(this.mock.address)).to.be.bignumber.equal(this.value);
  222. expect(await web3.eth.getBalance(this.receiver.address)).to.be.bignumber.equal('0');
  223. this.settings = {
  224. proposal: [
  225. [ this.receiver.address ],
  226. [ this.value ],
  227. [ '0x' ],
  228. '<proposal description>',
  229. ],
  230. tokenHolder: owner,
  231. voters: [
  232. { voter: voter1, weight: web3.utils.toWei('5'), support: Enums.VoteType.For },
  233. { voter: voter2, weight: web3.utils.toWei('5'), support: Enums.VoteType.Abstain },
  234. ],
  235. };
  236. });
  237. afterEach(async function () {
  238. expectEvent(
  239. this.receipts.propose,
  240. 'ProposalCreated',
  241. { proposalId: this.id },
  242. );
  243. expectEvent(
  244. this.receipts.execute,
  245. 'ProposalExecuted',
  246. { proposalId: this.id },
  247. );
  248. expect(await web3.eth.getBalance(this.mock.address)).to.be.bignumber.equal('0');
  249. expect(await web3.eth.getBalance(this.receiver.address)).to.be.bignumber.equal(this.value);
  250. });
  251. runGovernorWorkflow();
  252. });
  253. describe('receiver revert without reason', function () {
  254. beforeEach(async function () {
  255. this.settings = {
  256. proposal: [
  257. [ this.receiver.address ],
  258. [ 0 ],
  259. [ this.receiver.contract.methods.mockFunctionRevertsNoReason().encodeABI() ],
  260. '<proposal description>',
  261. ],
  262. tokenHolder: owner,
  263. voters: [
  264. { voter: voter1, weight: web3.utils.toWei('10'), support: Enums.VoteType.For },
  265. ],
  266. steps: {
  267. execute: { error: 'Governor: call reverted without message' },
  268. },
  269. };
  270. });
  271. runGovernorWorkflow();
  272. });
  273. describe('receiver revert with reason', function () {
  274. beforeEach(async function () {
  275. this.settings = {
  276. proposal: [
  277. [ this.receiver.address ],
  278. [ 0 ],
  279. [ this.receiver.contract.methods.mockFunctionRevertsReason().encodeABI() ],
  280. '<proposal description>',
  281. ],
  282. tokenHolder: owner,
  283. voters: [
  284. { voter: voter1, weight: web3.utils.toWei('10'), support: Enums.VoteType.For },
  285. ],
  286. steps: {
  287. execute: { error: 'CallReceiverMock: reverting' },
  288. },
  289. };
  290. });
  291. runGovernorWorkflow();
  292. });
  293. describe('missing proposal', function () {
  294. beforeEach(async function () {
  295. this.settings = {
  296. proposal: [
  297. [ this.receiver.address ],
  298. [ web3.utils.toWei('0') ],
  299. [ this.receiver.contract.methods.mockFunction().encodeABI() ],
  300. '<proposal description>',
  301. ],
  302. tokenHolder: owner,
  303. voters: [
  304. {
  305. voter: voter1,
  306. weight: web3.utils.toWei('5'),
  307. support: Enums.VoteType.For,
  308. error: 'Governor: unknown proposal id',
  309. },
  310. {
  311. voter: voter2,
  312. weight: web3.utils.toWei('5'),
  313. support: Enums.VoteType.Abstain,
  314. error: 'Governor: unknown proposal id',
  315. },
  316. ],
  317. steps: {
  318. propose: { enable: false },
  319. wait: { enable: false },
  320. execute: { error: 'Governor: unknown proposal id' },
  321. },
  322. };
  323. });
  324. runGovernorWorkflow();
  325. });
  326. describe('duplicate pending proposal', function () {
  327. beforeEach(async function () {
  328. this.settings = {
  329. proposal: [
  330. [ this.receiver.address ],
  331. [ web3.utils.toWei('0') ],
  332. [ this.receiver.contract.methods.mockFunction().encodeABI() ],
  333. '<proposal description>',
  334. ],
  335. steps: {
  336. wait: { enable: false },
  337. execute: { enable: false },
  338. },
  339. };
  340. });
  341. afterEach(async function () {
  342. await expectRevert(this.mock.propose(...this.settings.proposal), 'Governor: proposal already exists');
  343. });
  344. runGovernorWorkflow();
  345. });
  346. describe('duplicate executed proposal', function () {
  347. beforeEach(async function () {
  348. this.settings = {
  349. proposal: [
  350. [ this.receiver.address ],
  351. [ web3.utils.toWei('0') ],
  352. [ this.receiver.contract.methods.mockFunction().encodeABI() ],
  353. '<proposal description>',
  354. ],
  355. tokenHolder: owner,
  356. voters: [
  357. { voter: voter1, weight: web3.utils.toWei('5'), support: Enums.VoteType.For },
  358. { voter: voter2, weight: web3.utils.toWei('5'), support: Enums.VoteType.Abstain },
  359. ],
  360. };
  361. });
  362. afterEach(async function () {
  363. await expectRevert(this.mock.propose(...this.settings.proposal), 'Governor: proposal already exists');
  364. });
  365. runGovernorWorkflow();
  366. });
  367. describe('Invalid vote type', function () {
  368. beforeEach(async function () {
  369. this.settings = {
  370. proposal: [
  371. [ this.receiver.address ],
  372. [ web3.utils.toWei('0') ],
  373. [ this.receiver.contract.methods.mockFunction().encodeABI() ],
  374. '<proposal description>',
  375. ],
  376. tokenHolder: owner,
  377. voters: [
  378. {
  379. voter: voter1,
  380. weight: web3.utils.toWei('10'),
  381. support: new BN('255'),
  382. error: 'GovernorVotingSimple: invalid value for enum VoteType',
  383. },
  384. ],
  385. steps: {
  386. wait: { enable: false },
  387. execute: { enable: false },
  388. },
  389. };
  390. });
  391. runGovernorWorkflow();
  392. });
  393. describe('double cast', function () {
  394. beforeEach(async function () {
  395. this.settings = {
  396. proposal: [
  397. [ this.receiver.address ],
  398. [ web3.utils.toWei('0') ],
  399. [ this.receiver.contract.methods.mockFunction().encodeABI() ],
  400. '<proposal description>',
  401. ],
  402. tokenHolder: owner,
  403. voters: [
  404. {
  405. voter: voter1,
  406. weight: web3.utils.toWei('5'),
  407. support: Enums.VoteType.For,
  408. },
  409. {
  410. voter: voter1,
  411. weight: web3.utils.toWei('5'),
  412. support: Enums.VoteType.For,
  413. error: 'GovernorVotingSimple: vote already cast',
  414. },
  415. ],
  416. };
  417. });
  418. runGovernorWorkflow();
  419. });
  420. describe('quorum not reached', function () {
  421. beforeEach(async function () {
  422. this.settings = {
  423. proposal: [
  424. [ this.receiver.address ],
  425. [ web3.utils.toWei('0') ],
  426. [ this.receiver.contract.methods.mockFunction().encodeABI() ],
  427. '<proposal description>',
  428. ],
  429. tokenHolder: owner,
  430. voters: [
  431. { voter: voter1, weight: web3.utils.toWei('5'), support: Enums.VoteType.For },
  432. { voter: voter2, weight: web3.utils.toWei('4'), support: Enums.VoteType.Abstain },
  433. { voter: voter3, weight: web3.utils.toWei('10'), support: Enums.VoteType.Against },
  434. ],
  435. steps: {
  436. execute: { error: 'Governor: proposal not successful' },
  437. },
  438. };
  439. });
  440. runGovernorWorkflow();
  441. });
  442. describe('score not reached', function () {
  443. beforeEach(async function () {
  444. this.settings = {
  445. proposal: [
  446. [ this.receiver.address ],
  447. [ web3.utils.toWei('0') ],
  448. [ this.receiver.contract.methods.mockFunction().encodeABI() ],
  449. '<proposal description>',
  450. ],
  451. tokenHolder: owner,
  452. voters: [
  453. { voter: voter1, weight: web3.utils.toWei('10'), support: Enums.VoteType.Against },
  454. ],
  455. steps: {
  456. execute: { error: 'Governor: proposal not successful' },
  457. },
  458. };
  459. });
  460. runGovernorWorkflow();
  461. });
  462. describe('vote not over', function () {
  463. beforeEach(async function () {
  464. this.settings = {
  465. proposal: [
  466. [ this.receiver.address ],
  467. [ web3.utils.toWei('0') ],
  468. [ this.receiver.contract.methods.mockFunction().encodeABI() ],
  469. '<proposal description>',
  470. ],
  471. tokenHolder: owner,
  472. voters: [
  473. { voter: voter1, weight: web3.utils.toWei('10'), support: Enums.VoteType.For },
  474. ],
  475. steps: {
  476. wait: { enable: false },
  477. execute: { error: 'Governor: proposal not successful' },
  478. },
  479. };
  480. });
  481. runGovernorWorkflow();
  482. });
  483. });
  484. describe('state', function () {
  485. describe('Unset', function () {
  486. beforeEach(async function () {
  487. this.settings = {
  488. proposal: [
  489. [ this.receiver.address ],
  490. [ web3.utils.toWei('0') ],
  491. [ this.receiver.contract.methods.mockFunction().encodeABI() ],
  492. '<proposal description>',
  493. ],
  494. steps: {
  495. propose: { enable: false },
  496. wait: { enable: false },
  497. execute: { enable: false },
  498. },
  499. };
  500. });
  501. afterEach(async function () {
  502. await expectRevert(this.mock.state(this.id), 'Governor: unknown proposal id');
  503. });
  504. runGovernorWorkflow();
  505. });
  506. describe('Pending & Active', function () {
  507. beforeEach(async function () {
  508. this.settings = {
  509. proposal: [
  510. [ this.receiver.address ],
  511. [ web3.utils.toWei('0') ],
  512. [ this.receiver.contract.methods.mockFunction().encodeABI() ],
  513. '<proposal description>',
  514. ],
  515. steps: {
  516. propose: { noadvance: true },
  517. wait: { enable: false },
  518. execute: { enable: false },
  519. },
  520. };
  521. });
  522. afterEach(async function () {
  523. expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Pending);
  524. await time.advanceBlockTo(this.snapshot);
  525. expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Active);
  526. });
  527. runGovernorWorkflow();
  528. });
  529. describe('Defeated', function () {
  530. beforeEach(async function () {
  531. this.settings = {
  532. proposal: [
  533. [ this.receiver.address ],
  534. [ web3.utils.toWei('0') ],
  535. [ this.receiver.contract.methods.mockFunction().encodeABI() ],
  536. '<proposal description>',
  537. ],
  538. steps: {
  539. execute: { enable: false },
  540. },
  541. };
  542. });
  543. afterEach(async function () {
  544. expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Defeated);
  545. });
  546. runGovernorWorkflow();
  547. });
  548. describe('Succeeded', function () {
  549. beforeEach(async function () {
  550. this.settings = {
  551. proposal: [
  552. [ this.receiver.address ],
  553. [ web3.utils.toWei('0') ],
  554. [ this.receiver.contract.methods.mockFunction().encodeABI() ],
  555. '<proposal description>',
  556. ],
  557. tokenHolder: owner,
  558. voters: [
  559. { voter: voter1, weight: web3.utils.toWei('10'), support: Enums.VoteType.For },
  560. ],
  561. steps: {
  562. execute: { enable: false },
  563. },
  564. };
  565. });
  566. afterEach(async function () {
  567. expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Succeeded);
  568. });
  569. runGovernorWorkflow();
  570. });
  571. describe('Executed', function () {
  572. beforeEach(async function () {
  573. this.settings = {
  574. proposal: [
  575. [ this.receiver.address ],
  576. [ web3.utils.toWei('0') ],
  577. [ this.receiver.contract.methods.mockFunction().encodeABI() ],
  578. '<proposal description>',
  579. ],
  580. tokenHolder: owner,
  581. voters: [
  582. { voter: voter1, weight: web3.utils.toWei('10'), support: Enums.VoteType.For },
  583. ],
  584. };
  585. });
  586. afterEach(async function () {
  587. expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Executed);
  588. });
  589. runGovernorWorkflow();
  590. });
  591. });
  592. describe('Cancel', function () {
  593. describe('Before proposal', function () {
  594. beforeEach(async function () {
  595. this.settings = {
  596. proposal: [
  597. [ this.receiver.address ],
  598. [ web3.utils.toWei('0') ],
  599. [ this.receiver.contract.methods.mockFunction().encodeABI() ],
  600. '<proposal description>',
  601. ],
  602. steps: {
  603. propose: { enable: false },
  604. wait: { enable: false },
  605. execute: { enable: false },
  606. },
  607. };
  608. });
  609. afterEach(async function () {
  610. await expectRevert(
  611. this.mock.cancel(...this.settings.proposal.slice(0, -1), this.descriptionHash),
  612. 'Governor: unknown proposal id',
  613. );
  614. });
  615. runGovernorWorkflow();
  616. });
  617. describe('After proposal', function () {
  618. beforeEach(async function () {
  619. this.settings = {
  620. proposal: [
  621. [ this.receiver.address ],
  622. [ web3.utils.toWei('0') ],
  623. [ this.receiver.contract.methods.mockFunction().encodeABI() ],
  624. '<proposal description>',
  625. ],
  626. steps: {
  627. wait: { enable: false },
  628. execute: { enable: false },
  629. },
  630. };
  631. });
  632. afterEach(async function () {
  633. await this.mock.cancel(...this.settings.proposal.slice(0, -1), this.descriptionHash);
  634. expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Canceled);
  635. await expectRevert(
  636. this.mock.castVote(this.id, new BN('100'), { from: voter1 }),
  637. 'Governor: vote not currently active',
  638. );
  639. });
  640. runGovernorWorkflow();
  641. });
  642. describe('After vote', function () {
  643. beforeEach(async function () {
  644. this.settings = {
  645. proposal: [
  646. [ this.receiver.address ],
  647. [ web3.utils.toWei('0') ],
  648. [ this.receiver.contract.methods.mockFunction().encodeABI() ],
  649. '<proposal description>',
  650. ],
  651. tokenHolder: owner,
  652. voters: [
  653. { voter: voter1, weight: web3.utils.toWei('10'), support: Enums.VoteType.For },
  654. ],
  655. steps: {
  656. wait: { enable: false },
  657. execute: { enable: false },
  658. },
  659. };
  660. });
  661. afterEach(async function () {
  662. await this.mock.cancel(...this.settings.proposal.slice(0, -1), this.descriptionHash);
  663. expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Canceled);
  664. await expectRevert(
  665. this.mock.execute(...this.settings.proposal.slice(0, -1), this.descriptionHash),
  666. 'Governor: proposal not successful',
  667. );
  668. });
  669. runGovernorWorkflow();
  670. });
  671. describe('After deadline', function () {
  672. beforeEach(async function () {
  673. this.settings = {
  674. proposal: [
  675. [ this.receiver.address ],
  676. [ web3.utils.toWei('0') ],
  677. [ this.receiver.contract.methods.mockFunction().encodeABI() ],
  678. '<proposal description>',
  679. ],
  680. tokenHolder: owner,
  681. voters: [
  682. { voter: voter1, weight: web3.utils.toWei('10'), support: Enums.VoteType.For },
  683. ],
  684. steps: {
  685. execute: { enable: false },
  686. },
  687. };
  688. });
  689. afterEach(async function () {
  690. await this.mock.cancel(...this.settings.proposal.slice(0, -1), this.descriptionHash);
  691. expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Canceled);
  692. await expectRevert(
  693. this.mock.execute(...this.settings.proposal.slice(0, -1), this.descriptionHash),
  694. 'Governor: proposal not successful',
  695. );
  696. });
  697. runGovernorWorkflow();
  698. });
  699. describe('After execution', function () {
  700. beforeEach(async function () {
  701. this.settings = {
  702. proposal: [
  703. [ this.receiver.address ],
  704. [ web3.utils.toWei('0') ],
  705. [ this.receiver.contract.methods.mockFunction().encodeABI() ],
  706. '<proposal description>',
  707. ],
  708. tokenHolder: owner,
  709. voters: [
  710. { voter: voter1, weight: web3.utils.toWei('10'), support: Enums.VoteType.For },
  711. ],
  712. };
  713. });
  714. afterEach(async function () {
  715. await expectRevert(
  716. this.mock.cancel(...this.settings.proposal.slice(0, -1), this.descriptionHash),
  717. 'Governor: proposal not active',
  718. );
  719. });
  720. runGovernorWorkflow();
  721. });
  722. });
  723. describe('Proposal length', function () {
  724. it('empty', async function () {
  725. await expectRevert(
  726. this.mock.propose(
  727. [],
  728. [],
  729. [],
  730. '<proposal description>',
  731. ),
  732. 'Governor: empty proposal',
  733. );
  734. });
  735. it('missmatch #1', async function () {
  736. await expectRevert(
  737. this.mock.propose(
  738. [ ],
  739. [ web3.utils.toWei('0') ],
  740. [ this.receiver.contract.methods.mockFunction().encodeABI() ],
  741. '<proposal description>',
  742. ),
  743. 'Governor: invalid proposal length',
  744. );
  745. });
  746. it('missmatch #2', async function () {
  747. await expectRevert(
  748. this.mock.propose(
  749. [ this.receiver.address ],
  750. [ ],
  751. [ this.receiver.contract.methods.mockFunction().encodeABI() ],
  752. '<proposal description>',
  753. ),
  754. 'Governor: invalid proposal length',
  755. );
  756. });
  757. it('missmatch #3', async function () {
  758. await expectRevert(
  759. this.mock.propose(
  760. [ this.receiver.address ],
  761. [ web3.utils.toWei('0') ],
  762. [ ],
  763. '<proposal description>',
  764. ),
  765. 'Governor: invalid proposal length',
  766. );
  767. });
  768. });
  769. });