ERC1155.behavior.js 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870
  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 firstAmount = new BN(1000);
  14. const secondAmount = 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, firstAmount, '0x', {
  32. from: minter,
  33. });
  34. await this.token.$_mint(secondTokenHolder, secondTokenId, secondAmount, '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(firstAmount);
  40. expect(await this.token.balanceOf(secondTokenHolder, secondTokenId)).to.be.bignumber.equal(secondAmount);
  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, firstAmount, '0x', {
  85. from: minter,
  86. });
  87. await this.token.$_mint(secondTokenHolder, secondTokenId, secondAmount, '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(secondAmount);
  98. expect(result[1]).to.be.a.bignumber.equal(firstAmount);
  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(firstAmount);
  109. expect(result[1]).to.be.a.bignumber.equal(secondAmount);
  110. expect(result[2]).to.be.a.bignumber.equal(firstAmount);
  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 self as an operator', async function () {
  130. await expectRevertCustomError(
  131. this.token.setApprovalForAll(multiTokenHolder, true, { from: multiTokenHolder }),
  132. 'ERC1155InvalidOperator',
  133. [multiTokenHolder],
  134. );
  135. });
  136. });
  137. describe('safeTransferFrom', function () {
  138. beforeEach(async function () {
  139. await this.token.$_mint(multiTokenHolder, firstTokenId, firstAmount, '0x', {
  140. from: minter,
  141. });
  142. await this.token.$_mint(multiTokenHolder, secondTokenId, secondAmount, '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, firstAmount.addn(1), '0x', {
  149. from: multiTokenHolder,
  150. }),
  151. 'ERC1155InsufficientBalance',
  152. [multiTokenHolder, firstAmount, firstAmount.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, firstAmount, '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. firstAmount,
  191. '0x',
  192. {
  193. from: multiTokenHolder,
  194. },
  195. );
  196. });
  197. transferWasSuccessful.call(this, {
  198. operator: multiTokenHolder,
  199. from: multiTokenHolder,
  200. id: firstTokenId,
  201. value: firstAmount,
  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(secondAmount);
  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, firstAmount, '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. firstAmount,
  234. '0x',
  235. {
  236. from: proxy,
  237. },
  238. );
  239. });
  240. transferWasSuccessful.call(this, {
  241. operator: proxy,
  242. from: multiTokenHolder,
  243. id: firstTokenId,
  244. value: firstAmount,
  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. firstAmount,
  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: firstAmount,
  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: firstAmount,
  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. firstAmount,
  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: firstAmount,
  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: firstAmount,
  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, firstAmount, '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(multiTokenHolder, this.receiver.address, firstTokenId, firstAmount, '0x', {
  348. from: multiTokenHolder,
  349. }),
  350. 'ERC1155ReceiverMock: reverting on receive',
  351. );
  352. });
  353. });
  354. context('without a revert string', function () {
  355. beforeEach(async function () {
  356. this.receiver = await ERC1155ReceiverMock.new(
  357. RECEIVER_SINGLE_MAGIC_VALUE,
  358. RECEIVER_BATCH_MAGIC_VALUE,
  359. RevertType.RevertWithoutMessage,
  360. );
  361. });
  362. it('reverts', async function () {
  363. await expectRevertCustomError(
  364. this.token.safeTransferFrom(multiTokenHolder, this.receiver.address, firstTokenId, firstAmount, '0x', {
  365. from: multiTokenHolder,
  366. }),
  367. 'ERC1155InvalidReceiver',
  368. [this.receiver.address],
  369. );
  370. });
  371. });
  372. context('with a custom error', function () {
  373. beforeEach(async function () {
  374. this.receiver = await ERC1155ReceiverMock.new(
  375. RECEIVER_SINGLE_MAGIC_VALUE,
  376. RECEIVER_BATCH_MAGIC_VALUE,
  377. RevertType.RevertWithCustomError,
  378. );
  379. });
  380. it('reverts', async function () {
  381. await expectRevertCustomError(
  382. this.token.safeTransferFrom(multiTokenHolder, this.receiver.address, firstTokenId, firstAmount, '0x', {
  383. from: multiTokenHolder,
  384. }),
  385. 'CustomError',
  386. [RECEIVER_SINGLE_MAGIC_VALUE],
  387. );
  388. });
  389. });
  390. context('with a panic', function () {
  391. beforeEach(async function () {
  392. this.receiver = await ERC1155ReceiverMock.new(
  393. RECEIVER_SINGLE_MAGIC_VALUE,
  394. RECEIVER_BATCH_MAGIC_VALUE,
  395. RevertType.Panic,
  396. );
  397. });
  398. it('reverts', async function () {
  399. await expectRevert.unspecified(
  400. this.token.safeTransferFrom(multiTokenHolder, this.receiver.address, firstTokenId, firstAmount, '0x', {
  401. from: multiTokenHolder,
  402. }),
  403. );
  404. });
  405. });
  406. });
  407. context('to a contract that does not implement the required function', function () {
  408. it('reverts', async function () {
  409. const invalidReceiver = this.token;
  410. await expectRevert.unspecified(
  411. this.token.safeTransferFrom(multiTokenHolder, invalidReceiver.address, firstTokenId, firstAmount, '0x', {
  412. from: multiTokenHolder,
  413. }),
  414. );
  415. });
  416. });
  417. });
  418. describe('safeBatchTransferFrom', function () {
  419. beforeEach(async function () {
  420. await this.token.$_mint(multiTokenHolder, firstTokenId, firstAmount, '0x', {
  421. from: minter,
  422. });
  423. await this.token.$_mint(multiTokenHolder, secondTokenId, secondAmount, '0x', {
  424. from: minter,
  425. });
  426. });
  427. it('reverts when transferring amount more than any of balances', async function () {
  428. await expectRevertCustomError(
  429. this.token.safeBatchTransferFrom(
  430. multiTokenHolder,
  431. recipient,
  432. [firstTokenId, secondTokenId],
  433. [firstAmount, secondAmount.addn(1)],
  434. '0x',
  435. { from: multiTokenHolder },
  436. ),
  437. 'ERC1155InsufficientBalance',
  438. [multiTokenHolder, secondAmount, secondAmount.addn(1), secondTokenId],
  439. );
  440. });
  441. it("reverts when ids array length doesn't match amounts array length", async function () {
  442. const ids1 = [firstTokenId];
  443. const amounts1 = [firstAmount, secondAmount];
  444. await expectRevertCustomError(
  445. this.token.safeBatchTransferFrom(multiTokenHolder, recipient, ids1, amounts1, '0x', {
  446. from: multiTokenHolder,
  447. }),
  448. 'ERC1155InvalidArrayLength',
  449. [ids1.length, amounts1.length],
  450. );
  451. const ids2 = [firstTokenId, secondTokenId];
  452. const amounts2 = [firstAmount];
  453. await expectRevertCustomError(
  454. this.token.safeBatchTransferFrom(multiTokenHolder, recipient, ids2, amounts2, '0x', {
  455. from: multiTokenHolder,
  456. }),
  457. 'ERC1155InvalidArrayLength',
  458. [ids2.length, amounts2.length],
  459. );
  460. });
  461. it('reverts when transferring to zero address', async function () {
  462. await expectRevertCustomError(
  463. this.token.safeBatchTransferFrom(
  464. multiTokenHolder,
  465. ZERO_ADDRESS,
  466. [firstTokenId, secondTokenId],
  467. [firstAmount, secondAmount],
  468. '0x',
  469. { from: multiTokenHolder },
  470. ),
  471. 'ERC1155InvalidReceiver',
  472. [ZERO_ADDRESS],
  473. );
  474. });
  475. it('reverts when transferring from zero address', async function () {
  476. await expectRevertCustomError(
  477. this.token.$_safeBatchTransferFrom(ZERO_ADDRESS, multiTokenHolder, [firstTokenId], [firstAmount], '0x'),
  478. 'ERC1155InvalidSender',
  479. [ZERO_ADDRESS],
  480. );
  481. });
  482. function batchTransferWasSuccessful({ operator, from, ids, values }) {
  483. it('debits transferred balances from sender', async function () {
  484. const newBalances = await this.token.balanceOfBatch(new Array(ids.length).fill(from), ids);
  485. for (const newBalance of newBalances) {
  486. expect(newBalance).to.be.a.bignumber.equal('0');
  487. }
  488. });
  489. it('credits transferred balances to receiver', async function () {
  490. const newBalances = await this.token.balanceOfBatch(new Array(ids.length).fill(this.toWhom), ids);
  491. for (let i = 0; i < newBalances.length; i++) {
  492. expect(newBalances[i]).to.be.a.bignumber.equal(values[i]);
  493. }
  494. });
  495. it('emits a TransferBatch log', function () {
  496. expectEvent(this.transferLogs, 'TransferBatch', {
  497. operator,
  498. from,
  499. to: this.toWhom,
  500. // ids,
  501. // values,
  502. });
  503. });
  504. }
  505. context('when called by the multiTokenHolder', async function () {
  506. beforeEach(async function () {
  507. this.toWhom = recipient;
  508. this.transferLogs = await this.token.safeBatchTransferFrom(
  509. multiTokenHolder,
  510. recipient,
  511. [firstTokenId, secondTokenId],
  512. [firstAmount, secondAmount],
  513. '0x',
  514. { from: multiTokenHolder },
  515. );
  516. });
  517. batchTransferWasSuccessful.call(this, {
  518. operator: multiTokenHolder,
  519. from: multiTokenHolder,
  520. ids: [firstTokenId, secondTokenId],
  521. values: [firstAmount, secondAmount],
  522. });
  523. });
  524. context('when called by an operator on behalf of the multiTokenHolder', function () {
  525. context('when operator is not approved by multiTokenHolder', function () {
  526. beforeEach(async function () {
  527. await this.token.setApprovalForAll(proxy, false, { from: multiTokenHolder });
  528. });
  529. it('reverts', async function () {
  530. await expectRevertCustomError(
  531. this.token.safeBatchTransferFrom(
  532. multiTokenHolder,
  533. recipient,
  534. [firstTokenId, secondTokenId],
  535. [firstAmount, secondAmount],
  536. '0x',
  537. { from: proxy },
  538. ),
  539. 'ERC1155MissingApprovalForAll',
  540. [proxy, multiTokenHolder],
  541. );
  542. });
  543. });
  544. context('when operator is approved by multiTokenHolder', function () {
  545. beforeEach(async function () {
  546. this.toWhom = recipient;
  547. await this.token.setApprovalForAll(proxy, true, { from: multiTokenHolder });
  548. this.transferLogs = await this.token.safeBatchTransferFrom(
  549. multiTokenHolder,
  550. recipient,
  551. [firstTokenId, secondTokenId],
  552. [firstAmount, secondAmount],
  553. '0x',
  554. { from: proxy },
  555. );
  556. });
  557. batchTransferWasSuccessful.call(this, {
  558. operator: proxy,
  559. from: multiTokenHolder,
  560. ids: [firstTokenId, secondTokenId],
  561. values: [firstAmount, secondAmount],
  562. });
  563. it("preserves operator's balances not involved in the transfer", async function () {
  564. const balance1 = await this.token.balanceOf(proxy, firstTokenId);
  565. expect(balance1).to.be.a.bignumber.equal('0');
  566. const balance2 = await this.token.balanceOf(proxy, secondTokenId);
  567. expect(balance2).to.be.a.bignumber.equal('0');
  568. });
  569. });
  570. });
  571. context('when sending to a valid receiver', function () {
  572. beforeEach(async function () {
  573. this.receiver = await ERC1155ReceiverMock.new(
  574. RECEIVER_SINGLE_MAGIC_VALUE,
  575. RECEIVER_BATCH_MAGIC_VALUE,
  576. RevertType.None,
  577. );
  578. });
  579. context('without data', function () {
  580. beforeEach(async function () {
  581. this.toWhom = this.receiver.address;
  582. this.transferReceipt = await this.token.safeBatchTransferFrom(
  583. multiTokenHolder,
  584. this.receiver.address,
  585. [firstTokenId, secondTokenId],
  586. [firstAmount, secondAmount],
  587. '0x',
  588. { from: multiTokenHolder },
  589. );
  590. this.transferLogs = this.transferReceipt;
  591. });
  592. batchTransferWasSuccessful.call(this, {
  593. operator: multiTokenHolder,
  594. from: multiTokenHolder,
  595. ids: [firstTokenId, secondTokenId],
  596. values: [firstAmount, secondAmount],
  597. });
  598. it('calls onERC1155BatchReceived', async function () {
  599. await expectEvent.inTransaction(this.transferReceipt.tx, ERC1155ReceiverMock, 'BatchReceived', {
  600. operator: multiTokenHolder,
  601. from: multiTokenHolder,
  602. // ids: [firstTokenId, secondTokenId],
  603. // values: [firstAmount, secondAmount],
  604. data: null,
  605. });
  606. });
  607. });
  608. context('with data', function () {
  609. const data = '0xf00dd00d';
  610. beforeEach(async function () {
  611. this.toWhom = this.receiver.address;
  612. this.transferReceipt = await this.token.safeBatchTransferFrom(
  613. multiTokenHolder,
  614. this.receiver.address,
  615. [firstTokenId, secondTokenId],
  616. [firstAmount, secondAmount],
  617. data,
  618. { from: multiTokenHolder },
  619. );
  620. this.transferLogs = this.transferReceipt;
  621. });
  622. batchTransferWasSuccessful.call(this, {
  623. operator: multiTokenHolder,
  624. from: multiTokenHolder,
  625. ids: [firstTokenId, secondTokenId],
  626. values: [firstAmount, secondAmount],
  627. });
  628. it('calls onERC1155Received', async function () {
  629. await expectEvent.inTransaction(this.transferReceipt.tx, ERC1155ReceiverMock, 'BatchReceived', {
  630. operator: multiTokenHolder,
  631. from: multiTokenHolder,
  632. // ids: [firstTokenId, secondTokenId],
  633. // values: [firstAmount, secondAmount],
  634. data,
  635. });
  636. });
  637. });
  638. });
  639. context('to a receiver contract returning unexpected value', function () {
  640. beforeEach(async function () {
  641. this.receiver = await ERC1155ReceiverMock.new(
  642. RECEIVER_SINGLE_MAGIC_VALUE,
  643. RECEIVER_SINGLE_MAGIC_VALUE,
  644. RevertType.None,
  645. );
  646. });
  647. it('reverts', async function () {
  648. await expectRevertCustomError(
  649. this.token.safeBatchTransferFrom(
  650. multiTokenHolder,
  651. this.receiver.address,
  652. [firstTokenId, secondTokenId],
  653. [firstAmount, secondAmount],
  654. '0x',
  655. { from: multiTokenHolder },
  656. ),
  657. 'ERC1155InvalidReceiver',
  658. [this.receiver.address],
  659. );
  660. });
  661. });
  662. context('to a receiver contract that reverts', function () {
  663. context('with a revert string', function () {
  664. beforeEach(async function () {
  665. this.receiver = await ERC1155ReceiverMock.new(
  666. RECEIVER_SINGLE_MAGIC_VALUE,
  667. RECEIVER_BATCH_MAGIC_VALUE,
  668. RevertType.RevertWithMessage,
  669. );
  670. });
  671. it('reverts', async function () {
  672. await expectRevert(
  673. this.token.safeBatchTransferFrom(
  674. multiTokenHolder,
  675. this.receiver.address,
  676. [firstTokenId, secondTokenId],
  677. [firstAmount, secondAmount],
  678. '0x',
  679. { from: multiTokenHolder },
  680. ),
  681. 'ERC1155ReceiverMock: reverting on batch receive',
  682. );
  683. });
  684. });
  685. context('without a revert string', function () {
  686. beforeEach(async function () {
  687. this.receiver = await ERC1155ReceiverMock.new(
  688. RECEIVER_SINGLE_MAGIC_VALUE,
  689. RECEIVER_BATCH_MAGIC_VALUE,
  690. RevertType.RevertWithoutMessage,
  691. );
  692. });
  693. it('reverts', async function () {
  694. await expectRevertCustomError(
  695. this.token.safeBatchTransferFrom(
  696. multiTokenHolder,
  697. this.receiver.address,
  698. [firstTokenId, secondTokenId],
  699. [firstAmount, secondAmount],
  700. '0x',
  701. { from: multiTokenHolder },
  702. ),
  703. 'ERC1155InvalidReceiver',
  704. [this.receiver.address],
  705. );
  706. });
  707. });
  708. context('with a custom error', function () {
  709. beforeEach(async function () {
  710. this.receiver = await ERC1155ReceiverMock.new(
  711. RECEIVER_SINGLE_MAGIC_VALUE,
  712. RECEIVER_BATCH_MAGIC_VALUE,
  713. RevertType.RevertWithCustomError,
  714. );
  715. });
  716. it('reverts', async function () {
  717. await expectRevertCustomError(
  718. this.token.safeBatchTransferFrom(
  719. multiTokenHolder,
  720. this.receiver.address,
  721. [firstTokenId, secondTokenId],
  722. [firstAmount, secondAmount],
  723. '0x',
  724. { from: multiTokenHolder },
  725. ),
  726. 'CustomError',
  727. [RECEIVER_SINGLE_MAGIC_VALUE],
  728. );
  729. });
  730. });
  731. context('with a panic', function () {
  732. beforeEach(async function () {
  733. this.receiver = await ERC1155ReceiverMock.new(
  734. RECEIVER_SINGLE_MAGIC_VALUE,
  735. RECEIVER_BATCH_MAGIC_VALUE,
  736. RevertType.Panic,
  737. );
  738. });
  739. it('reverts', async function () {
  740. await expectRevert.unspecified(
  741. this.token.safeBatchTransferFrom(
  742. multiTokenHolder,
  743. this.receiver.address,
  744. [firstTokenId, secondTokenId],
  745. [firstAmount, secondAmount],
  746. '0x',
  747. { from: multiTokenHolder },
  748. ),
  749. );
  750. });
  751. });
  752. });
  753. context('to a contract that does not implement the required function', function () {
  754. it('reverts', async function () {
  755. const invalidReceiver = this.token;
  756. await expectRevert.unspecified(
  757. this.token.safeBatchTransferFrom(
  758. multiTokenHolder,
  759. invalidReceiver.address,
  760. [firstTokenId, secondTokenId],
  761. [firstAmount, secondAmount],
  762. '0x',
  763. { from: multiTokenHolder },
  764. ),
  765. );
  766. });
  767. });
  768. });
  769. shouldSupportInterfaces(['ERC165', 'ERC1155']);
  770. });
  771. }
  772. module.exports = {
  773. shouldBehaveLikeERC1155,
  774. };