Governor.test.js 23 KB

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