ERC1155.behavior.js 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905
  1. const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
  2. const { expect } = require('chai');
  3. const { ZERO_ADDRESS } = constants;
  4. const { shouldSupportInterfaces } = require('../../utils/introspection/SupportsInterface.behavior');
  5. const { expectRevertCustomError } = require('../../helpers/customError');
  6. const { Enum } = require('../../helpers/enums');
  7. const ERC1155ReceiverMock = artifacts.require('ERC1155ReceiverMock');
  8. const RevertType = Enum('None', 'RevertWithoutMessage', 'RevertWithMessage', 'RevertWithCustomError', 'Panic');
  9. function shouldBehaveLikeERC1155([minter, firstTokenHolder, secondTokenHolder, multiTokenHolder, recipient, proxy]) {
  10. const firstTokenId = new BN(1);
  11. const secondTokenId = new BN(2);
  12. const unknownTokenId = new BN(3);
  13. const firstTokenValue = new BN(1000);
  14. const secondTokenValue = new BN(2000);
  15. const RECEIVER_SINGLE_MAGIC_VALUE = '0xf23a6e61';
  16. const RECEIVER_BATCH_MAGIC_VALUE = '0xbc197c81';
  17. describe('like an ERC1155', function () {
  18. describe('balanceOf', function () {
  19. it('should return 0 when queried about the zero address', async function () {
  20. expect(await this.token.balanceOf(ZERO_ADDRESS, firstTokenId)).to.be.bignumber.equal('0');
  21. });
  22. context("when accounts don't own tokens", function () {
  23. it('returns zero for given addresses', async function () {
  24. expect(await this.token.balanceOf(firstTokenHolder, firstTokenId)).to.be.bignumber.equal('0');
  25. expect(await this.token.balanceOf(secondTokenHolder, secondTokenId)).to.be.bignumber.equal('0');
  26. expect(await this.token.balanceOf(firstTokenHolder, unknownTokenId)).to.be.bignumber.equal('0');
  27. });
  28. });
  29. context('when accounts own some tokens', function () {
  30. beforeEach(async function () {
  31. await this.token.$_mint(firstTokenHolder, firstTokenId, firstTokenValue, '0x', {
  32. from: minter,
  33. });
  34. await this.token.$_mint(secondTokenHolder, secondTokenId, secondTokenValue, '0x', {
  35. from: minter,
  36. });
  37. });
  38. it('returns the amount of tokens owned by the given addresses', async function () {
  39. expect(await this.token.balanceOf(firstTokenHolder, firstTokenId)).to.be.bignumber.equal(firstTokenValue);
  40. expect(await this.token.balanceOf(secondTokenHolder, secondTokenId)).to.be.bignumber.equal(secondTokenValue);
  41. expect(await this.token.balanceOf(firstTokenHolder, unknownTokenId)).to.be.bignumber.equal('0');
  42. });
  43. });
  44. });
  45. describe('balanceOfBatch', function () {
  46. it("reverts when input arrays don't match up", async function () {
  47. const accounts1 = [firstTokenHolder, secondTokenHolder, firstTokenHolder, secondTokenHolder];
  48. const ids1 = [firstTokenId, secondTokenId, unknownTokenId];
  49. await expectRevertCustomError(this.token.balanceOfBatch(accounts1, ids1), 'ERC1155InvalidArrayLength', [
  50. accounts1.length,
  51. ids1.length,
  52. ]);
  53. const accounts2 = [firstTokenHolder, secondTokenHolder];
  54. const ids2 = [firstTokenId, secondTokenId, unknownTokenId];
  55. await expectRevertCustomError(this.token.balanceOfBatch(accounts2, ids2), 'ERC1155InvalidArrayLength', [
  56. accounts2.length,
  57. ids2.length,
  58. ]);
  59. });
  60. it('should return 0 as the balance when one of the addresses is the zero address', async function () {
  61. const result = await this.token.balanceOfBatch(
  62. [firstTokenHolder, secondTokenHolder, ZERO_ADDRESS],
  63. [firstTokenId, secondTokenId, unknownTokenId],
  64. );
  65. expect(result).to.be.an('array');
  66. expect(result[0]).to.be.a.bignumber.equal('0');
  67. expect(result[1]).to.be.a.bignumber.equal('0');
  68. expect(result[2]).to.be.a.bignumber.equal('0');
  69. });
  70. context("when accounts don't own tokens", function () {
  71. it('returns zeros for each account', async function () {
  72. const result = await this.token.balanceOfBatch(
  73. [firstTokenHolder, secondTokenHolder, firstTokenHolder],
  74. [firstTokenId, secondTokenId, unknownTokenId],
  75. );
  76. expect(result).to.be.an('array');
  77. expect(result[0]).to.be.a.bignumber.equal('0');
  78. expect(result[1]).to.be.a.bignumber.equal('0');
  79. expect(result[2]).to.be.a.bignumber.equal('0');
  80. });
  81. });
  82. context('when accounts own some tokens', function () {
  83. beforeEach(async function () {
  84. await this.token.$_mint(firstTokenHolder, firstTokenId, firstTokenValue, '0x', {
  85. from: minter,
  86. });
  87. await this.token.$_mint(secondTokenHolder, secondTokenId, secondTokenValue, '0x', {
  88. from: minter,
  89. });
  90. });
  91. it('returns amounts owned by each account in order passed', async function () {
  92. const result = await this.token.balanceOfBatch(
  93. [secondTokenHolder, firstTokenHolder, firstTokenHolder],
  94. [secondTokenId, firstTokenId, unknownTokenId],
  95. );
  96. expect(result).to.be.an('array');
  97. expect(result[0]).to.be.a.bignumber.equal(secondTokenValue);
  98. expect(result[1]).to.be.a.bignumber.equal(firstTokenValue);
  99. expect(result[2]).to.be.a.bignumber.equal('0');
  100. });
  101. it('returns multiple times the balance of the same address when asked', async function () {
  102. const result = await this.token.balanceOfBatch(
  103. [firstTokenHolder, secondTokenHolder, firstTokenHolder],
  104. [firstTokenId, secondTokenId, firstTokenId],
  105. );
  106. expect(result).to.be.an('array');
  107. expect(result[0]).to.be.a.bignumber.equal(result[2]);
  108. expect(result[0]).to.be.a.bignumber.equal(firstTokenValue);
  109. expect(result[1]).to.be.a.bignumber.equal(secondTokenValue);
  110. expect(result[2]).to.be.a.bignumber.equal(firstTokenValue);
  111. });
  112. });
  113. });
  114. describe('setApprovalForAll', function () {
  115. let receipt;
  116. beforeEach(async function () {
  117. receipt = await this.token.setApprovalForAll(proxy, true, { from: multiTokenHolder });
  118. });
  119. it('sets approval status which can be queried via isApprovedForAll', async function () {
  120. expect(await this.token.isApprovedForAll(multiTokenHolder, proxy)).to.be.equal(true);
  121. });
  122. it('emits an ApprovalForAll log', function () {
  123. expectEvent(receipt, 'ApprovalForAll', { account: multiTokenHolder, operator: proxy, approved: true });
  124. });
  125. it('can unset approval for an operator', async function () {
  126. await this.token.setApprovalForAll(proxy, false, { from: multiTokenHolder });
  127. expect(await this.token.isApprovedForAll(multiTokenHolder, proxy)).to.be.equal(false);
  128. });
  129. it('reverts if attempting to approve zero address as an operator', async function () {
  130. await expectRevertCustomError(
  131. this.token.setApprovalForAll(constants.ZERO_ADDRESS, true, { from: multiTokenHolder }),
  132. 'ERC1155InvalidOperator',
  133. [constants.ZERO_ADDRESS],
  134. );
  135. });
  136. });
  137. describe('safeTransferFrom', function () {
  138. beforeEach(async function () {
  139. await this.token.$_mint(multiTokenHolder, firstTokenId, firstTokenValue, '0x', {
  140. from: minter,
  141. });
  142. await this.token.$_mint(multiTokenHolder, secondTokenId, secondTokenValue, '0x', {
  143. from: minter,
  144. });
  145. });
  146. it('reverts when transferring more than balance', async function () {
  147. await expectRevertCustomError(
  148. this.token.safeTransferFrom(multiTokenHolder, recipient, firstTokenId, firstTokenValue.addn(1), '0x', {
  149. from: multiTokenHolder,
  150. }),
  151. 'ERC1155InsufficientBalance',
  152. [multiTokenHolder, firstTokenValue, firstTokenValue.addn(1), firstTokenId],
  153. );
  154. });
  155. it('reverts when transferring to zero address', async function () {
  156. await expectRevertCustomError(
  157. this.token.safeTransferFrom(multiTokenHolder, ZERO_ADDRESS, firstTokenId, firstTokenValue, '0x', {
  158. from: multiTokenHolder,
  159. }),
  160. 'ERC1155InvalidReceiver',
  161. [ZERO_ADDRESS],
  162. );
  163. });
  164. function transferWasSuccessful({ operator, from, id, value }) {
  165. it('debits transferred balance from sender', async function () {
  166. const newBalance = await this.token.balanceOf(from, id);
  167. expect(newBalance).to.be.a.bignumber.equal('0');
  168. });
  169. it('credits transferred balance to receiver', async function () {
  170. const newBalance = await this.token.balanceOf(this.toWhom, id);
  171. expect(newBalance).to.be.a.bignumber.equal(value);
  172. });
  173. it('emits a TransferSingle log', function () {
  174. expectEvent(this.transferLogs, 'TransferSingle', {
  175. operator,
  176. from,
  177. to: this.toWhom,
  178. id,
  179. value,
  180. });
  181. });
  182. }
  183. context('when called by the multiTokenHolder', async function () {
  184. beforeEach(async function () {
  185. this.toWhom = recipient;
  186. this.transferLogs = await this.token.safeTransferFrom(
  187. multiTokenHolder,
  188. recipient,
  189. firstTokenId,
  190. firstTokenValue,
  191. '0x',
  192. {
  193. from: multiTokenHolder,
  194. },
  195. );
  196. });
  197. transferWasSuccessful.call(this, {
  198. operator: multiTokenHolder,
  199. from: multiTokenHolder,
  200. id: firstTokenId,
  201. value: firstTokenValue,
  202. });
  203. it('preserves existing balances which are not transferred by multiTokenHolder', async function () {
  204. const balance1 = await this.token.balanceOf(multiTokenHolder, secondTokenId);
  205. expect(balance1).to.be.a.bignumber.equal(secondTokenValue);
  206. const balance2 = await this.token.balanceOf(recipient, secondTokenId);
  207. expect(balance2).to.be.a.bignumber.equal('0');
  208. });
  209. });
  210. context('when called by an operator on behalf of the multiTokenHolder', function () {
  211. context('when operator is not approved by multiTokenHolder', function () {
  212. beforeEach(async function () {
  213. await this.token.setApprovalForAll(proxy, false, { from: multiTokenHolder });
  214. });
  215. it('reverts', async function () {
  216. await expectRevertCustomError(
  217. this.token.safeTransferFrom(multiTokenHolder, recipient, firstTokenId, firstTokenValue, '0x', {
  218. from: proxy,
  219. }),
  220. 'ERC1155MissingApprovalForAll',
  221. [proxy, multiTokenHolder],
  222. );
  223. });
  224. });
  225. context('when operator is approved by multiTokenHolder', function () {
  226. beforeEach(async function () {
  227. this.toWhom = recipient;
  228. await this.token.setApprovalForAll(proxy, true, { from: multiTokenHolder });
  229. this.transferLogs = await this.token.safeTransferFrom(
  230. multiTokenHolder,
  231. recipient,
  232. firstTokenId,
  233. firstTokenValue,
  234. '0x',
  235. {
  236. from: proxy,
  237. },
  238. );
  239. });
  240. transferWasSuccessful.call(this, {
  241. operator: proxy,
  242. from: multiTokenHolder,
  243. id: firstTokenId,
  244. value: firstTokenValue,
  245. });
  246. it("preserves operator's balances not involved in the transfer", async function () {
  247. const balance1 = await this.token.balanceOf(proxy, firstTokenId);
  248. expect(balance1).to.be.a.bignumber.equal('0');
  249. const balance2 = await this.token.balanceOf(proxy, secondTokenId);
  250. expect(balance2).to.be.a.bignumber.equal('0');
  251. });
  252. });
  253. });
  254. context('when sending to a valid receiver', function () {
  255. beforeEach(async function () {
  256. this.receiver = await ERC1155ReceiverMock.new(
  257. RECEIVER_SINGLE_MAGIC_VALUE,
  258. RECEIVER_BATCH_MAGIC_VALUE,
  259. RevertType.None,
  260. );
  261. });
  262. context('without data', function () {
  263. beforeEach(async function () {
  264. this.toWhom = this.receiver.address;
  265. this.transferReceipt = await this.token.safeTransferFrom(
  266. multiTokenHolder,
  267. this.receiver.address,
  268. firstTokenId,
  269. firstTokenValue,
  270. '0x',
  271. { from: multiTokenHolder },
  272. );
  273. this.transferLogs = this.transferReceipt;
  274. });
  275. transferWasSuccessful.call(this, {
  276. operator: multiTokenHolder,
  277. from: multiTokenHolder,
  278. id: firstTokenId,
  279. value: firstTokenValue,
  280. });
  281. it('calls onERC1155Received', async function () {
  282. await expectEvent.inTransaction(this.transferReceipt.tx, ERC1155ReceiverMock, 'Received', {
  283. operator: multiTokenHolder,
  284. from: multiTokenHolder,
  285. id: firstTokenId,
  286. value: firstTokenValue,
  287. data: null,
  288. });
  289. });
  290. });
  291. context('with data', function () {
  292. const data = '0xf00dd00d';
  293. beforeEach(async function () {
  294. this.toWhom = this.receiver.address;
  295. this.transferReceipt = await this.token.safeTransferFrom(
  296. multiTokenHolder,
  297. this.receiver.address,
  298. firstTokenId,
  299. firstTokenValue,
  300. data,
  301. { from: multiTokenHolder },
  302. );
  303. this.transferLogs = this.transferReceipt;
  304. });
  305. transferWasSuccessful.call(this, {
  306. operator: multiTokenHolder,
  307. from: multiTokenHolder,
  308. id: firstTokenId,
  309. value: firstTokenValue,
  310. });
  311. it('calls onERC1155Received', async function () {
  312. await expectEvent.inTransaction(this.transferReceipt.tx, ERC1155ReceiverMock, 'Received', {
  313. operator: multiTokenHolder,
  314. from: multiTokenHolder,
  315. id: firstTokenId,
  316. value: firstTokenValue,
  317. data,
  318. });
  319. });
  320. });
  321. });
  322. context('to a receiver contract returning unexpected value', function () {
  323. beforeEach(async function () {
  324. this.receiver = await ERC1155ReceiverMock.new('0x00c0ffee', RECEIVER_BATCH_MAGIC_VALUE, RevertType.None);
  325. });
  326. it('reverts', async function () {
  327. await expectRevertCustomError(
  328. this.token.safeTransferFrom(multiTokenHolder, this.receiver.address, firstTokenId, firstTokenValue, '0x', {
  329. from: multiTokenHolder,
  330. }),
  331. 'ERC1155InvalidReceiver',
  332. [this.receiver.address],
  333. );
  334. });
  335. });
  336. context('to a receiver contract that reverts', function () {
  337. context('with a revert string', function () {
  338. beforeEach(async function () {
  339. this.receiver = await ERC1155ReceiverMock.new(
  340. RECEIVER_SINGLE_MAGIC_VALUE,
  341. RECEIVER_BATCH_MAGIC_VALUE,
  342. RevertType.RevertWithMessage,
  343. );
  344. });
  345. it('reverts', async function () {
  346. await expectRevert(
  347. this.token.safeTransferFrom(
  348. multiTokenHolder,
  349. this.receiver.address,
  350. firstTokenId,
  351. firstTokenValue,
  352. '0x',
  353. {
  354. from: multiTokenHolder,
  355. },
  356. ),
  357. 'ERC1155ReceiverMock: reverting on receive',
  358. );
  359. });
  360. });
  361. context('without a revert string', function () {
  362. beforeEach(async function () {
  363. this.receiver = await ERC1155ReceiverMock.new(
  364. RECEIVER_SINGLE_MAGIC_VALUE,
  365. RECEIVER_BATCH_MAGIC_VALUE,
  366. RevertType.RevertWithoutMessage,
  367. );
  368. });
  369. it('reverts', async function () {
  370. await expectRevertCustomError(
  371. this.token.safeTransferFrom(
  372. multiTokenHolder,
  373. this.receiver.address,
  374. firstTokenId,
  375. firstTokenValue,
  376. '0x',
  377. {
  378. from: multiTokenHolder,
  379. },
  380. ),
  381. 'ERC1155InvalidReceiver',
  382. [this.receiver.address],
  383. );
  384. });
  385. });
  386. context('with a custom error', function () {
  387. beforeEach(async function () {
  388. this.receiver = await ERC1155ReceiverMock.new(
  389. RECEIVER_SINGLE_MAGIC_VALUE,
  390. RECEIVER_BATCH_MAGIC_VALUE,
  391. RevertType.RevertWithCustomError,
  392. );
  393. });
  394. it('reverts', async function () {
  395. await expectRevertCustomError(
  396. this.token.safeTransferFrom(
  397. multiTokenHolder,
  398. this.receiver.address,
  399. firstTokenId,
  400. firstTokenValue,
  401. '0x',
  402. {
  403. from: multiTokenHolder,
  404. },
  405. ),
  406. 'CustomError',
  407. [RECEIVER_SINGLE_MAGIC_VALUE],
  408. );
  409. });
  410. });
  411. context('with a panic', function () {
  412. beforeEach(async function () {
  413. this.receiver = await ERC1155ReceiverMock.new(
  414. RECEIVER_SINGLE_MAGIC_VALUE,
  415. RECEIVER_BATCH_MAGIC_VALUE,
  416. RevertType.Panic,
  417. );
  418. });
  419. it('reverts', async function () {
  420. await expectRevert.unspecified(
  421. this.token.safeTransferFrom(
  422. multiTokenHolder,
  423. this.receiver.address,
  424. firstTokenId,
  425. firstTokenValue,
  426. '0x',
  427. {
  428. from: multiTokenHolder,
  429. },
  430. ),
  431. );
  432. });
  433. });
  434. });
  435. context('to a contract that does not implement the required function', function () {
  436. it('reverts', async function () {
  437. const invalidReceiver = this.token;
  438. await expectRevert.unspecified(
  439. this.token.safeTransferFrom(
  440. multiTokenHolder,
  441. invalidReceiver.address,
  442. firstTokenId,
  443. firstTokenValue,
  444. '0x',
  445. {
  446. from: multiTokenHolder,
  447. },
  448. ),
  449. );
  450. });
  451. });
  452. });
  453. describe('safeBatchTransferFrom', function () {
  454. beforeEach(async function () {
  455. await this.token.$_mint(multiTokenHolder, firstTokenId, firstTokenValue, '0x', {
  456. from: minter,
  457. });
  458. await this.token.$_mint(multiTokenHolder, secondTokenId, secondTokenValue, '0x', {
  459. from: minter,
  460. });
  461. });
  462. it('reverts when transferring value more than any of balances', async function () {
  463. await expectRevertCustomError(
  464. this.token.safeBatchTransferFrom(
  465. multiTokenHolder,
  466. recipient,
  467. [firstTokenId, secondTokenId],
  468. [firstTokenValue, secondTokenValue.addn(1)],
  469. '0x',
  470. { from: multiTokenHolder },
  471. ),
  472. 'ERC1155InsufficientBalance',
  473. [multiTokenHolder, secondTokenValue, secondTokenValue.addn(1), secondTokenId],
  474. );
  475. });
  476. it("reverts when ids array length doesn't match values array length", async function () {
  477. const ids1 = [firstTokenId];
  478. const tokenValues1 = [firstTokenValue, secondTokenValue];
  479. await expectRevertCustomError(
  480. this.token.safeBatchTransferFrom(multiTokenHolder, recipient, ids1, tokenValues1, '0x', {
  481. from: multiTokenHolder,
  482. }),
  483. 'ERC1155InvalidArrayLength',
  484. [ids1.length, tokenValues1.length],
  485. );
  486. const ids2 = [firstTokenId, secondTokenId];
  487. const tokenValues2 = [firstTokenValue];
  488. await expectRevertCustomError(
  489. this.token.safeBatchTransferFrom(multiTokenHolder, recipient, ids2, tokenValues2, '0x', {
  490. from: multiTokenHolder,
  491. }),
  492. 'ERC1155InvalidArrayLength',
  493. [ids2.length, tokenValues2.length],
  494. );
  495. });
  496. it('reverts when transferring to zero address', async function () {
  497. await expectRevertCustomError(
  498. this.token.safeBatchTransferFrom(
  499. multiTokenHolder,
  500. ZERO_ADDRESS,
  501. [firstTokenId, secondTokenId],
  502. [firstTokenValue, secondTokenValue],
  503. '0x',
  504. { from: multiTokenHolder },
  505. ),
  506. 'ERC1155InvalidReceiver',
  507. [ZERO_ADDRESS],
  508. );
  509. });
  510. it('reverts when transferring from zero address', async function () {
  511. await expectRevertCustomError(
  512. this.token.$_safeBatchTransferFrom(ZERO_ADDRESS, multiTokenHolder, [firstTokenId], [firstTokenValue], '0x'),
  513. 'ERC1155InvalidSender',
  514. [ZERO_ADDRESS],
  515. );
  516. });
  517. function batchTransferWasSuccessful({ operator, from, ids, values }) {
  518. it('debits transferred balances from sender', async function () {
  519. const newBalances = await this.token.balanceOfBatch(new Array(ids.length).fill(from), ids);
  520. for (const newBalance of newBalances) {
  521. expect(newBalance).to.be.a.bignumber.equal('0');
  522. }
  523. });
  524. it('credits transferred balances to receiver', async function () {
  525. const newBalances = await this.token.balanceOfBatch(new Array(ids.length).fill(this.toWhom), ids);
  526. for (let i = 0; i < newBalances.length; i++) {
  527. expect(newBalances[i]).to.be.a.bignumber.equal(values[i]);
  528. }
  529. });
  530. it('emits a TransferBatch log', function () {
  531. expectEvent(this.transferLogs, 'TransferBatch', {
  532. operator,
  533. from,
  534. to: this.toWhom,
  535. // ids,
  536. // values,
  537. });
  538. });
  539. }
  540. context('when called by the multiTokenHolder', async function () {
  541. beforeEach(async function () {
  542. this.toWhom = recipient;
  543. this.transferLogs = await this.token.safeBatchTransferFrom(
  544. multiTokenHolder,
  545. recipient,
  546. [firstTokenId, secondTokenId],
  547. [firstTokenValue, secondTokenValue],
  548. '0x',
  549. { from: multiTokenHolder },
  550. );
  551. });
  552. batchTransferWasSuccessful.call(this, {
  553. operator: multiTokenHolder,
  554. from: multiTokenHolder,
  555. ids: [firstTokenId, secondTokenId],
  556. values: [firstTokenValue, secondTokenValue],
  557. });
  558. });
  559. context('when called by an operator on behalf of the multiTokenHolder', function () {
  560. context('when operator is not approved by multiTokenHolder', function () {
  561. beforeEach(async function () {
  562. await this.token.setApprovalForAll(proxy, false, { from: multiTokenHolder });
  563. });
  564. it('reverts', async function () {
  565. await expectRevertCustomError(
  566. this.token.safeBatchTransferFrom(
  567. multiTokenHolder,
  568. recipient,
  569. [firstTokenId, secondTokenId],
  570. [firstTokenValue, secondTokenValue],
  571. '0x',
  572. { from: proxy },
  573. ),
  574. 'ERC1155MissingApprovalForAll',
  575. [proxy, multiTokenHolder],
  576. );
  577. });
  578. });
  579. context('when operator is approved by multiTokenHolder', function () {
  580. beforeEach(async function () {
  581. this.toWhom = recipient;
  582. await this.token.setApprovalForAll(proxy, true, { from: multiTokenHolder });
  583. this.transferLogs = await this.token.safeBatchTransferFrom(
  584. multiTokenHolder,
  585. recipient,
  586. [firstTokenId, secondTokenId],
  587. [firstTokenValue, secondTokenValue],
  588. '0x',
  589. { from: proxy },
  590. );
  591. });
  592. batchTransferWasSuccessful.call(this, {
  593. operator: proxy,
  594. from: multiTokenHolder,
  595. ids: [firstTokenId, secondTokenId],
  596. values: [firstTokenValue, secondTokenValue],
  597. });
  598. it("preserves operator's balances not involved in the transfer", async function () {
  599. const balance1 = await this.token.balanceOf(proxy, firstTokenId);
  600. expect(balance1).to.be.a.bignumber.equal('0');
  601. const balance2 = await this.token.balanceOf(proxy, secondTokenId);
  602. expect(balance2).to.be.a.bignumber.equal('0');
  603. });
  604. });
  605. });
  606. context('when sending to a valid receiver', function () {
  607. beforeEach(async function () {
  608. this.receiver = await ERC1155ReceiverMock.new(
  609. RECEIVER_SINGLE_MAGIC_VALUE,
  610. RECEIVER_BATCH_MAGIC_VALUE,
  611. RevertType.None,
  612. );
  613. });
  614. context('without data', function () {
  615. beforeEach(async function () {
  616. this.toWhom = this.receiver.address;
  617. this.transferReceipt = await this.token.safeBatchTransferFrom(
  618. multiTokenHolder,
  619. this.receiver.address,
  620. [firstTokenId, secondTokenId],
  621. [firstTokenValue, secondTokenValue],
  622. '0x',
  623. { from: multiTokenHolder },
  624. );
  625. this.transferLogs = this.transferReceipt;
  626. });
  627. batchTransferWasSuccessful.call(this, {
  628. operator: multiTokenHolder,
  629. from: multiTokenHolder,
  630. ids: [firstTokenId, secondTokenId],
  631. values: [firstTokenValue, secondTokenValue],
  632. });
  633. it('calls onERC1155BatchReceived', async function () {
  634. await expectEvent.inTransaction(this.transferReceipt.tx, ERC1155ReceiverMock, 'BatchReceived', {
  635. operator: multiTokenHolder,
  636. from: multiTokenHolder,
  637. // ids: [firstTokenId, secondTokenId],
  638. // values: [firstTokenValue, secondTokenValue],
  639. data: null,
  640. });
  641. });
  642. });
  643. context('with data', function () {
  644. const data = '0xf00dd00d';
  645. beforeEach(async function () {
  646. this.toWhom = this.receiver.address;
  647. this.transferReceipt = await this.token.safeBatchTransferFrom(
  648. multiTokenHolder,
  649. this.receiver.address,
  650. [firstTokenId, secondTokenId],
  651. [firstTokenValue, secondTokenValue],
  652. data,
  653. { from: multiTokenHolder },
  654. );
  655. this.transferLogs = this.transferReceipt;
  656. });
  657. batchTransferWasSuccessful.call(this, {
  658. operator: multiTokenHolder,
  659. from: multiTokenHolder,
  660. ids: [firstTokenId, secondTokenId],
  661. values: [firstTokenValue, secondTokenValue],
  662. });
  663. it('calls onERC1155Received', async function () {
  664. await expectEvent.inTransaction(this.transferReceipt.tx, ERC1155ReceiverMock, 'BatchReceived', {
  665. operator: multiTokenHolder,
  666. from: multiTokenHolder,
  667. // ids: [firstTokenId, secondTokenId],
  668. // values: [firstTokenValue, secondTokenValue],
  669. data,
  670. });
  671. });
  672. });
  673. });
  674. context('to a receiver contract returning unexpected value', function () {
  675. beforeEach(async function () {
  676. this.receiver = await ERC1155ReceiverMock.new(
  677. RECEIVER_SINGLE_MAGIC_VALUE,
  678. RECEIVER_SINGLE_MAGIC_VALUE,
  679. RevertType.None,
  680. );
  681. });
  682. it('reverts', async function () {
  683. await expectRevertCustomError(
  684. this.token.safeBatchTransferFrom(
  685. multiTokenHolder,
  686. this.receiver.address,
  687. [firstTokenId, secondTokenId],
  688. [firstTokenValue, secondTokenValue],
  689. '0x',
  690. { from: multiTokenHolder },
  691. ),
  692. 'ERC1155InvalidReceiver',
  693. [this.receiver.address],
  694. );
  695. });
  696. });
  697. context('to a receiver contract that reverts', function () {
  698. context('with a revert string', function () {
  699. beforeEach(async function () {
  700. this.receiver = await ERC1155ReceiverMock.new(
  701. RECEIVER_SINGLE_MAGIC_VALUE,
  702. RECEIVER_BATCH_MAGIC_VALUE,
  703. RevertType.RevertWithMessage,
  704. );
  705. });
  706. it('reverts', async function () {
  707. await expectRevert(
  708. this.token.safeBatchTransferFrom(
  709. multiTokenHolder,
  710. this.receiver.address,
  711. [firstTokenId, secondTokenId],
  712. [firstTokenValue, secondTokenValue],
  713. '0x',
  714. { from: multiTokenHolder },
  715. ),
  716. 'ERC1155ReceiverMock: reverting on batch receive',
  717. );
  718. });
  719. });
  720. context('without a revert string', function () {
  721. beforeEach(async function () {
  722. this.receiver = await ERC1155ReceiverMock.new(
  723. RECEIVER_SINGLE_MAGIC_VALUE,
  724. RECEIVER_BATCH_MAGIC_VALUE,
  725. RevertType.RevertWithoutMessage,
  726. );
  727. });
  728. it('reverts', async function () {
  729. await expectRevertCustomError(
  730. this.token.safeBatchTransferFrom(
  731. multiTokenHolder,
  732. this.receiver.address,
  733. [firstTokenId, secondTokenId],
  734. [firstTokenValue, secondTokenValue],
  735. '0x',
  736. { from: multiTokenHolder },
  737. ),
  738. 'ERC1155InvalidReceiver',
  739. [this.receiver.address],
  740. );
  741. });
  742. });
  743. context('with a custom error', function () {
  744. beforeEach(async function () {
  745. this.receiver = await ERC1155ReceiverMock.new(
  746. RECEIVER_SINGLE_MAGIC_VALUE,
  747. RECEIVER_BATCH_MAGIC_VALUE,
  748. RevertType.RevertWithCustomError,
  749. );
  750. });
  751. it('reverts', async function () {
  752. await expectRevertCustomError(
  753. this.token.safeBatchTransferFrom(
  754. multiTokenHolder,
  755. this.receiver.address,
  756. [firstTokenId, secondTokenId],
  757. [firstTokenValue, secondTokenValue],
  758. '0x',
  759. { from: multiTokenHolder },
  760. ),
  761. 'CustomError',
  762. [RECEIVER_SINGLE_MAGIC_VALUE],
  763. );
  764. });
  765. });
  766. context('with a panic', function () {
  767. beforeEach(async function () {
  768. this.receiver = await ERC1155ReceiverMock.new(
  769. RECEIVER_SINGLE_MAGIC_VALUE,
  770. RECEIVER_BATCH_MAGIC_VALUE,
  771. RevertType.Panic,
  772. );
  773. });
  774. it('reverts', async function () {
  775. await expectRevert.unspecified(
  776. this.token.safeBatchTransferFrom(
  777. multiTokenHolder,
  778. this.receiver.address,
  779. [firstTokenId, secondTokenId],
  780. [firstTokenValue, secondTokenValue],
  781. '0x',
  782. { from: multiTokenHolder },
  783. ),
  784. );
  785. });
  786. });
  787. });
  788. context('to a contract that does not implement the required function', function () {
  789. it('reverts', async function () {
  790. const invalidReceiver = this.token;
  791. await expectRevert.unspecified(
  792. this.token.safeBatchTransferFrom(
  793. multiTokenHolder,
  794. invalidReceiver.address,
  795. [firstTokenId, secondTokenId],
  796. [firstTokenValue, secondTokenValue],
  797. '0x',
  798. { from: multiTokenHolder },
  799. ),
  800. );
  801. });
  802. });
  803. });
  804. shouldSupportInterfaces(['ERC165', 'ERC1155']);
  805. });
  806. }
  807. module.exports = {
  808. shouldBehaveLikeERC1155,
  809. };