Governor.test.js 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706
  1. const { constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
  2. const { expect } = require('chai');
  3. const ethSigUtil = require('eth-sig-util');
  4. const Wallet = require('ethereumjs-wallet').default;
  5. const { fromRpcSig } = require('ethereumjs-util');
  6. const Enums = require('../helpers/enums');
  7. const { getDomain, domainType } = require('../helpers/eip712');
  8. const { GovernorHelper } = require('../helpers/governance');
  9. const { clockFromReceipt } = require('../helpers/time');
  10. const { shouldSupportInterfaces } = require('../utils/introspection/SupportsInterface.behavior');
  11. const { shouldBehaveLikeEIP6372 } = require('./utils/EIP6372.behavior');
  12. const Governor = artifacts.require('$GovernorMock');
  13. const CallReceiver = artifacts.require('CallReceiverMock');
  14. const ERC721 = artifacts.require('$ERC721');
  15. const ERC1155 = artifacts.require('$ERC1155');
  16. const TOKENS = [
  17. { Token: artifacts.require('$ERC20Votes'), mode: 'blocknumber' },
  18. { Token: artifacts.require('$ERC20VotesTimestampMock'), mode: 'timestamp' },
  19. { Token: artifacts.require('$ERC20VotesLegacyMock'), mode: 'blocknumber' },
  20. ];
  21. contract('Governor', function (accounts) {
  22. const [owner, proposer, voter1, voter2, voter3, voter4] = accounts;
  23. const name = 'OZ-Governor';
  24. const version = '1';
  25. const tokenName = 'MockToken';
  26. const tokenSymbol = 'MTKN';
  27. const tokenSupply = web3.utils.toWei('100');
  28. const votingDelay = web3.utils.toBN(4);
  29. const votingPeriod = web3.utils.toBN(16);
  30. const value = web3.utils.toWei('1');
  31. for (const { mode, Token } of TOKENS) {
  32. describe(`using ${Token._json.contractName}`, function () {
  33. beforeEach(async function () {
  34. this.chainId = await web3.eth.getChainId();
  35. try {
  36. this.token = await Token.new(tokenName, tokenSymbol, tokenName, version);
  37. } catch {
  38. // ERC20VotesLegacyMock has a different construction that uses version='1' by default.
  39. this.token = await Token.new(tokenName, tokenSymbol, tokenName);
  40. }
  41. this.mock = await Governor.new(
  42. name, // name
  43. votingDelay, // initialVotingDelay
  44. votingPeriod, // initialVotingPeriod
  45. 0, // initialProposalThreshold
  46. this.token.address, // tokenAddress
  47. 10, // quorumNumeratorValue
  48. );
  49. this.receiver = await CallReceiver.new();
  50. this.helper = new GovernorHelper(this.mock, mode);
  51. await web3.eth.sendTransaction({ from: owner, to: this.mock.address, value });
  52. await this.token.$_mint(owner, tokenSupply);
  53. await this.helper.delegate({ token: this.token, to: voter1, value: web3.utils.toWei('10') }, { from: owner });
  54. await this.helper.delegate({ token: this.token, to: voter2, value: web3.utils.toWei('7') }, { from: owner });
  55. await this.helper.delegate({ token: this.token, to: voter3, value: web3.utils.toWei('5') }, { from: owner });
  56. await this.helper.delegate({ token: this.token, to: voter4, value: web3.utils.toWei('2') }, { from: owner });
  57. this.proposal = this.helper.setProposal(
  58. [
  59. {
  60. target: this.receiver.address,
  61. data: this.receiver.contract.methods.mockFunction().encodeABI(),
  62. value,
  63. },
  64. ],
  65. '<proposal description>',
  66. );
  67. });
  68. shouldSupportInterfaces(['ERC165', 'ERC1155Receiver', 'Governor', 'GovernorWithParams', 'GovernorCancel']);
  69. shouldBehaveLikeEIP6372(mode);
  70. it('deployment check', async function () {
  71. expect(await this.mock.name()).to.be.equal(name);
  72. expect(await this.mock.token()).to.be.equal(this.token.address);
  73. expect(await this.mock.votingDelay()).to.be.bignumber.equal(votingDelay);
  74. expect(await this.mock.votingPeriod()).to.be.bignumber.equal(votingPeriod);
  75. expect(await this.mock.quorum(0)).to.be.bignumber.equal('0');
  76. expect(await this.mock.COUNTING_MODE()).to.be.equal('support=bravo&quorum=for,abstain');
  77. });
  78. it('nominal workflow', async function () {
  79. // Before
  80. expect(await this.mock.proposalProposer(this.proposal.id)).to.be.equal(constants.ZERO_ADDRESS);
  81. expect(await this.mock.hasVoted(this.proposal.id, owner)).to.be.equal(false);
  82. expect(await this.mock.hasVoted(this.proposal.id, voter1)).to.be.equal(false);
  83. expect(await this.mock.hasVoted(this.proposal.id, voter2)).to.be.equal(false);
  84. expect(await web3.eth.getBalance(this.mock.address)).to.be.bignumber.equal(value);
  85. expect(await web3.eth.getBalance(this.receiver.address)).to.be.bignumber.equal('0');
  86. // Run proposal
  87. const txPropose = await this.helper.propose({ from: proposer });
  88. expectEvent(txPropose, 'ProposalCreated', {
  89. proposalId: this.proposal.id,
  90. proposer,
  91. targets: this.proposal.targets,
  92. // values: this.proposal.values,
  93. signatures: this.proposal.signatures,
  94. calldatas: this.proposal.data,
  95. voteStart: web3.utils.toBN(await clockFromReceipt[mode](txPropose.receipt)).add(votingDelay),
  96. voteEnd: web3.utils
  97. .toBN(await clockFromReceipt[mode](txPropose.receipt))
  98. .add(votingDelay)
  99. .add(votingPeriod),
  100. description: this.proposal.description,
  101. });
  102. await this.helper.waitForSnapshot();
  103. expectEvent(
  104. await this.helper.vote({ support: Enums.VoteType.For, reason: 'This is nice' }, { from: voter1 }),
  105. 'VoteCast',
  106. {
  107. voter: voter1,
  108. support: Enums.VoteType.For,
  109. reason: 'This is nice',
  110. weight: web3.utils.toWei('10'),
  111. },
  112. );
  113. expectEvent(await this.helper.vote({ support: Enums.VoteType.For }, { from: voter2 }), 'VoteCast', {
  114. voter: voter2,
  115. support: Enums.VoteType.For,
  116. weight: web3.utils.toWei('7'),
  117. });
  118. expectEvent(await this.helper.vote({ support: Enums.VoteType.Against }, { from: voter3 }), 'VoteCast', {
  119. voter: voter3,
  120. support: Enums.VoteType.Against,
  121. weight: web3.utils.toWei('5'),
  122. });
  123. expectEvent(await this.helper.vote({ support: Enums.VoteType.Abstain }, { from: voter4 }), 'VoteCast', {
  124. voter: voter4,
  125. support: Enums.VoteType.Abstain,
  126. weight: web3.utils.toWei('2'),
  127. });
  128. await this.helper.waitForDeadline();
  129. const txExecute = await this.helper.execute();
  130. expectEvent(txExecute, 'ProposalExecuted', { proposalId: this.proposal.id });
  131. await expectEvent.inTransaction(txExecute.tx, this.receiver, 'MockFunctionCalled');
  132. // After
  133. expect(await this.mock.proposalProposer(this.proposal.id)).to.be.equal(proposer);
  134. expect(await this.mock.hasVoted(this.proposal.id, owner)).to.be.equal(false);
  135. expect(await this.mock.hasVoted(this.proposal.id, voter1)).to.be.equal(true);
  136. expect(await this.mock.hasVoted(this.proposal.id, voter2)).to.be.equal(true);
  137. expect(await web3.eth.getBalance(this.mock.address)).to.be.bignumber.equal('0');
  138. expect(await web3.eth.getBalance(this.receiver.address)).to.be.bignumber.equal(value);
  139. });
  140. it('vote with signature', async function () {
  141. const voterBySig = Wallet.generate();
  142. const voterBySigAddress = web3.utils.toChecksumAddress(voterBySig.getAddressString());
  143. const signature = (contract, message) =>
  144. getDomain(contract)
  145. .then(domain => ({
  146. primaryType: 'Ballot',
  147. types: {
  148. EIP712Domain: domainType(domain),
  149. Ballot: [
  150. { name: 'proposalId', type: 'uint256' },
  151. { name: 'support', type: 'uint8' },
  152. ],
  153. },
  154. domain,
  155. message,
  156. }))
  157. .then(data => ethSigUtil.signTypedMessage(voterBySig.getPrivateKey(), { data }))
  158. .then(fromRpcSig);
  159. await this.token.delegate(voterBySigAddress, { from: voter1 });
  160. // Run proposal
  161. await this.helper.propose();
  162. await this.helper.waitForSnapshot();
  163. expectEvent(await this.helper.vote({ support: Enums.VoteType.For, signature }), 'VoteCast', {
  164. voter: voterBySigAddress,
  165. support: Enums.VoteType.For,
  166. });
  167. await this.helper.waitForDeadline();
  168. await this.helper.execute();
  169. // After
  170. expect(await this.mock.hasVoted(this.proposal.id, owner)).to.be.equal(false);
  171. expect(await this.mock.hasVoted(this.proposal.id, voter1)).to.be.equal(false);
  172. expect(await this.mock.hasVoted(this.proposal.id, voter2)).to.be.equal(false);
  173. expect(await this.mock.hasVoted(this.proposal.id, voterBySigAddress)).to.be.equal(true);
  174. });
  175. it('send ethers', async function () {
  176. const empty = web3.utils.toChecksumAddress(web3.utils.randomHex(20));
  177. this.proposal = this.helper.setProposal(
  178. [
  179. {
  180. target: empty,
  181. value,
  182. },
  183. ],
  184. '<proposal description>',
  185. );
  186. // Before
  187. expect(await web3.eth.getBalance(this.mock.address)).to.be.bignumber.equal(value);
  188. expect(await web3.eth.getBalance(empty)).to.be.bignumber.equal('0');
  189. // Run proposal
  190. await this.helper.propose();
  191. await this.helper.waitForSnapshot();
  192. await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 });
  193. await this.helper.waitForDeadline();
  194. await this.helper.execute();
  195. // After
  196. expect(await web3.eth.getBalance(this.mock.address)).to.be.bignumber.equal('0');
  197. expect(await web3.eth.getBalance(empty)).to.be.bignumber.equal(value);
  198. });
  199. describe('should revert', function () {
  200. describe('on propose', function () {
  201. it('if proposal already exists', async function () {
  202. await this.helper.propose();
  203. await expectRevert(this.helper.propose(), 'Governor: proposal already exists');
  204. });
  205. });
  206. describe('on vote', function () {
  207. it('if proposal does not exist', async function () {
  208. await expectRevert(
  209. this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }),
  210. 'Governor: unknown proposal id',
  211. );
  212. });
  213. it('if voting has not started', async function () {
  214. await this.helper.propose();
  215. await expectRevert(
  216. this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }),
  217. 'Governor: vote not currently active',
  218. );
  219. });
  220. it('if support value is invalid', async function () {
  221. await this.helper.propose();
  222. await this.helper.waitForSnapshot();
  223. await expectRevert(
  224. this.helper.vote({ support: web3.utils.toBN('255') }),
  225. 'GovernorVotingSimple: invalid value for enum VoteType',
  226. );
  227. });
  228. it('if vote was already casted', async function () {
  229. await this.helper.propose();
  230. await this.helper.waitForSnapshot();
  231. await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 });
  232. await expectRevert(
  233. this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }),
  234. 'GovernorVotingSimple: vote already cast',
  235. );
  236. });
  237. it('if voting is over', async function () {
  238. await this.helper.propose();
  239. await this.helper.waitForDeadline();
  240. await expectRevert(
  241. this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }),
  242. 'Governor: vote not currently active',
  243. );
  244. });
  245. });
  246. describe('on execute', function () {
  247. it('if proposal does not exist', async function () {
  248. await expectRevert(this.helper.execute(), 'Governor: unknown proposal id');
  249. });
  250. it('if quorum is not reached', async function () {
  251. await this.helper.propose();
  252. await this.helper.waitForSnapshot();
  253. await this.helper.vote({ support: Enums.VoteType.For }, { from: voter3 });
  254. await expectRevert(this.helper.execute(), 'Governor: proposal not successful');
  255. });
  256. it('if score not reached', async function () {
  257. await this.helper.propose();
  258. await this.helper.waitForSnapshot();
  259. await this.helper.vote({ support: Enums.VoteType.Against }, { from: voter1 });
  260. await expectRevert(this.helper.execute(), 'Governor: proposal not successful');
  261. });
  262. it('if voting is not over', async function () {
  263. await this.helper.propose();
  264. await this.helper.waitForSnapshot();
  265. await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 });
  266. await expectRevert(this.helper.execute(), 'Governor: proposal not successful');
  267. });
  268. it('if receiver revert without reason', async function () {
  269. this.proposal = this.helper.setProposal(
  270. [
  271. {
  272. target: this.receiver.address,
  273. data: this.receiver.contract.methods.mockFunctionRevertsNoReason().encodeABI(),
  274. },
  275. ],
  276. '<proposal description>',
  277. );
  278. await this.helper.propose();
  279. await this.helper.waitForSnapshot();
  280. await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 });
  281. await this.helper.waitForDeadline();
  282. await expectRevert(this.helper.execute(), 'Governor: call reverted without message');
  283. });
  284. it('if receiver revert with reason', async function () {
  285. this.proposal = this.helper.setProposal(
  286. [
  287. {
  288. target: this.receiver.address,
  289. data: this.receiver.contract.methods.mockFunctionRevertsReason().encodeABI(),
  290. },
  291. ],
  292. '<proposal description>',
  293. );
  294. await this.helper.propose();
  295. await this.helper.waitForSnapshot();
  296. await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 });
  297. await this.helper.waitForDeadline();
  298. await expectRevert(this.helper.execute(), 'CallReceiverMock: reverting');
  299. });
  300. it('if proposal was already executed', async function () {
  301. await this.helper.propose();
  302. await this.helper.waitForSnapshot();
  303. await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 });
  304. await this.helper.waitForDeadline();
  305. await this.helper.execute();
  306. await expectRevert(this.helper.execute(), 'Governor: proposal not successful');
  307. });
  308. });
  309. });
  310. describe('state', function () {
  311. it('Unset', async function () {
  312. await expectRevert(this.mock.state(this.proposal.id), 'Governor: unknown proposal id');
  313. });
  314. it('Pending & Active', async function () {
  315. await this.helper.propose();
  316. expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Pending);
  317. await this.helper.waitForSnapshot();
  318. expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Pending);
  319. await this.helper.waitForSnapshot(+1);
  320. expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Active);
  321. });
  322. it('Defeated', async function () {
  323. await this.helper.propose();
  324. await this.helper.waitForDeadline();
  325. expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Active);
  326. await this.helper.waitForDeadline(+1);
  327. expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Defeated);
  328. });
  329. it('Succeeded', async function () {
  330. await this.helper.propose();
  331. await this.helper.waitForSnapshot();
  332. await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 });
  333. await this.helper.waitForDeadline();
  334. expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Active);
  335. await this.helper.waitForDeadline(+1);
  336. expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Succeeded);
  337. });
  338. it('Executed', async function () {
  339. await this.helper.propose();
  340. await this.helper.waitForSnapshot();
  341. await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 });
  342. await this.helper.waitForDeadline();
  343. await this.helper.execute();
  344. expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Executed);
  345. });
  346. });
  347. describe('cancel', function () {
  348. describe('internal', function () {
  349. it('before proposal', async function () {
  350. await expectRevert(this.helper.cancel('internal'), 'Governor: unknown proposal id');
  351. });
  352. it('after proposal', async function () {
  353. await this.helper.propose();
  354. await this.helper.cancel('internal');
  355. expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Canceled);
  356. await this.helper.waitForSnapshot();
  357. await expectRevert(
  358. this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }),
  359. 'Governor: vote not currently active',
  360. );
  361. });
  362. it('after vote', async function () {
  363. await this.helper.propose();
  364. await this.helper.waitForSnapshot();
  365. await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 });
  366. await this.helper.cancel('internal');
  367. expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Canceled);
  368. await this.helper.waitForDeadline();
  369. await expectRevert(this.helper.execute(), 'Governor: proposal not successful');
  370. });
  371. it('after deadline', async function () {
  372. await this.helper.propose();
  373. await this.helper.waitForSnapshot();
  374. await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 });
  375. await this.helper.waitForDeadline();
  376. await this.helper.cancel('internal');
  377. expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Canceled);
  378. await expectRevert(this.helper.execute(), 'Governor: proposal not successful');
  379. });
  380. it('after execution', async function () {
  381. await this.helper.propose();
  382. await this.helper.waitForSnapshot();
  383. await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 });
  384. await this.helper.waitForDeadline();
  385. await this.helper.execute();
  386. await expectRevert(this.helper.cancel('internal'), 'Governor: proposal not active');
  387. });
  388. });
  389. describe('public', function () {
  390. it('before proposal', async function () {
  391. await expectRevert(this.helper.cancel('external'), 'Governor: unknown proposal id');
  392. });
  393. it('after proposal', async function () {
  394. await this.helper.propose();
  395. await this.helper.cancel('external');
  396. });
  397. it('after proposal - restricted to proposer', async function () {
  398. await this.helper.propose();
  399. await expectRevert(this.helper.cancel('external', { from: owner }), 'Governor: only proposer can cancel');
  400. });
  401. it('after vote started', async function () {
  402. await this.helper.propose();
  403. await this.helper.waitForSnapshot(1); // snapshot + 1 block
  404. await expectRevert(this.helper.cancel('external'), 'Governor: too late to cancel');
  405. });
  406. it('after vote', async function () {
  407. await this.helper.propose();
  408. await this.helper.waitForSnapshot();
  409. await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 });
  410. await expectRevert(this.helper.cancel('external'), 'Governor: too late to cancel');
  411. });
  412. it('after deadline', async function () {
  413. await this.helper.propose();
  414. await this.helper.waitForSnapshot();
  415. await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 });
  416. await this.helper.waitForDeadline();
  417. await expectRevert(this.helper.cancel('external'), 'Governor: too late to cancel');
  418. });
  419. it('after execution', async function () {
  420. await this.helper.propose();
  421. await this.helper.waitForSnapshot();
  422. await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 });
  423. await this.helper.waitForDeadline();
  424. await this.helper.execute();
  425. await expectRevert(this.helper.cancel('external'), 'Governor: too late to cancel');
  426. });
  427. });
  428. });
  429. describe('proposal length', function () {
  430. it('empty', async function () {
  431. this.helper.setProposal([], '<proposal description>');
  432. await expectRevert(this.helper.propose(), 'Governor: empty proposal');
  433. });
  434. it('mismatch #1', async function () {
  435. this.helper.setProposal(
  436. {
  437. targets: [],
  438. values: [web3.utils.toWei('0')],
  439. data: [this.receiver.contract.methods.mockFunction().encodeABI()],
  440. },
  441. '<proposal description>',
  442. );
  443. await expectRevert(this.helper.propose(), 'Governor: invalid proposal length');
  444. });
  445. it('mismatch #2', async function () {
  446. this.helper.setProposal(
  447. {
  448. targets: [this.receiver.address],
  449. values: [],
  450. data: [this.receiver.contract.methods.mockFunction().encodeABI()],
  451. },
  452. '<proposal description>',
  453. );
  454. await expectRevert(this.helper.propose(), 'Governor: invalid proposal length');
  455. });
  456. it('mismatch #3', async function () {
  457. this.helper.setProposal(
  458. {
  459. targets: [this.receiver.address],
  460. values: [web3.utils.toWei('0')],
  461. data: [],
  462. },
  463. '<proposal description>',
  464. );
  465. await expectRevert(this.helper.propose(), 'Governor: invalid proposal length');
  466. });
  467. });
  468. describe('onlyGovernance updates', function () {
  469. it('setVotingDelay is protected', async function () {
  470. await expectRevert(this.mock.setVotingDelay('0'), 'Governor: onlyGovernance');
  471. });
  472. it('setVotingPeriod is protected', async function () {
  473. await expectRevert(this.mock.setVotingPeriod('32'), 'Governor: onlyGovernance');
  474. });
  475. it('setProposalThreshold is protected', async function () {
  476. await expectRevert(this.mock.setProposalThreshold('1000000000000000000'), 'Governor: onlyGovernance');
  477. });
  478. it('can setVotingDelay through governance', async function () {
  479. this.helper.setProposal(
  480. [
  481. {
  482. target: this.mock.address,
  483. data: this.mock.contract.methods.setVotingDelay('0').encodeABI(),
  484. },
  485. ],
  486. '<proposal description>',
  487. );
  488. await this.helper.propose();
  489. await this.helper.waitForSnapshot();
  490. await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 });
  491. await this.helper.waitForDeadline();
  492. expectEvent(await this.helper.execute(), 'VotingDelaySet', { oldVotingDelay: '4', newVotingDelay: '0' });
  493. expect(await this.mock.votingDelay()).to.be.bignumber.equal('0');
  494. });
  495. it('can setVotingPeriod through governance', async function () {
  496. this.helper.setProposal(
  497. [
  498. {
  499. target: this.mock.address,
  500. data: this.mock.contract.methods.setVotingPeriod('32').encodeABI(),
  501. },
  502. ],
  503. '<proposal description>',
  504. );
  505. await this.helper.propose();
  506. await this.helper.waitForSnapshot();
  507. await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 });
  508. await this.helper.waitForDeadline();
  509. expectEvent(await this.helper.execute(), 'VotingPeriodSet', { oldVotingPeriod: '16', newVotingPeriod: '32' });
  510. expect(await this.mock.votingPeriod()).to.be.bignumber.equal('32');
  511. });
  512. it('cannot setVotingPeriod to 0 through governance', async function () {
  513. this.helper.setProposal(
  514. [
  515. {
  516. target: this.mock.address,
  517. data: this.mock.contract.methods.setVotingPeriod('0').encodeABI(),
  518. },
  519. ],
  520. '<proposal description>',
  521. );
  522. await this.helper.propose();
  523. await this.helper.waitForSnapshot();
  524. await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 });
  525. await this.helper.waitForDeadline();
  526. await expectRevert(this.helper.execute(), 'GovernorSettings: voting period too low');
  527. });
  528. it('can setProposalThreshold to 0 through governance', async function () {
  529. this.helper.setProposal(
  530. [
  531. {
  532. target: this.mock.address,
  533. data: this.mock.contract.methods.setProposalThreshold('1000000000000000000').encodeABI(),
  534. },
  535. ],
  536. '<proposal description>',
  537. );
  538. await this.helper.propose();
  539. await this.helper.waitForSnapshot();
  540. await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 });
  541. await this.helper.waitForDeadline();
  542. expectEvent(await this.helper.execute(), 'ProposalThresholdSet', {
  543. oldProposalThreshold: '0',
  544. newProposalThreshold: '1000000000000000000',
  545. });
  546. expect(await this.mock.proposalThreshold()).to.be.bignumber.equal('1000000000000000000');
  547. });
  548. });
  549. describe('safe receive', function () {
  550. describe('ERC721', function () {
  551. const name = 'Non Fungible Token';
  552. const symbol = 'NFT';
  553. const tokenId = web3.utils.toBN(1);
  554. beforeEach(async function () {
  555. this.token = await ERC721.new(name, symbol);
  556. await this.token.$_mint(owner, tokenId);
  557. });
  558. it('can receive an ERC721 safeTransfer', async function () {
  559. await this.token.safeTransferFrom(owner, this.mock.address, tokenId, { from: owner });
  560. });
  561. });
  562. describe('ERC1155', function () {
  563. const uri = 'https://token-cdn-domain/{id}.json';
  564. const tokenIds = {
  565. 1: web3.utils.toBN(1000),
  566. 2: web3.utils.toBN(2000),
  567. 3: web3.utils.toBN(3000),
  568. };
  569. beforeEach(async function () {
  570. this.token = await ERC1155.new(uri);
  571. await this.token.$_mintBatch(owner, Object.keys(tokenIds), Object.values(tokenIds), '0x');
  572. });
  573. it('can receive ERC1155 safeTransfer', async function () {
  574. await this.token.safeTransferFrom(
  575. owner,
  576. this.mock.address,
  577. ...Object.entries(tokenIds)[0], // id + amount
  578. '0x',
  579. { from: owner },
  580. );
  581. });
  582. it('can receive ERC1155 safeBatchTransfer', async function () {
  583. await this.token.safeBatchTransferFrom(
  584. owner,
  585. this.mock.address,
  586. Object.keys(tokenIds),
  587. Object.values(tokenIds),
  588. '0x',
  589. { from: owner },
  590. );
  591. });
  592. });
  593. });
  594. });
  595. }
  596. });