Governor.test.js 28 KB

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