Governor.test.js 23 KB

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