ERC1155.behavior.js 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859
  1. const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
  2. const { ZERO_ADDRESS } = constants;
  3. const { expect } = require('chai');
  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', 'Empty', 'String', 'Custom');
  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. 'ERC1155InsufficientApprovalForAll',
  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. RevertType.None,
  259. RECEIVER_BATCH_MAGIC_VALUE,
  260. RevertType.None,
  261. );
  262. });
  263. context('without data', function () {
  264. beforeEach(async function () {
  265. this.toWhom = this.receiver.address;
  266. this.transferReceipt = await this.token.safeTransferFrom(
  267. multiTokenHolder,
  268. this.receiver.address,
  269. firstTokenId,
  270. firstAmount,
  271. '0x',
  272. { from: multiTokenHolder },
  273. );
  274. this.transferLogs = this.transferReceipt;
  275. });
  276. transferWasSuccessful.call(this, {
  277. operator: multiTokenHolder,
  278. from: multiTokenHolder,
  279. id: firstTokenId,
  280. value: firstAmount,
  281. });
  282. it('calls onERC1155Received', async function () {
  283. await expectEvent.inTransaction(this.transferReceipt.tx, ERC1155ReceiverMock, 'Received', {
  284. operator: multiTokenHolder,
  285. from: multiTokenHolder,
  286. id: firstTokenId,
  287. value: firstAmount,
  288. data: null,
  289. });
  290. });
  291. });
  292. context('with data', function () {
  293. const data = '0xf00dd00d';
  294. beforeEach(async function () {
  295. this.toWhom = this.receiver.address;
  296. this.transferReceipt = await this.token.safeTransferFrom(
  297. multiTokenHolder,
  298. this.receiver.address,
  299. firstTokenId,
  300. firstAmount,
  301. data,
  302. { from: multiTokenHolder },
  303. );
  304. this.transferLogs = this.transferReceipt;
  305. });
  306. transferWasSuccessful.call(this, {
  307. operator: multiTokenHolder,
  308. from: multiTokenHolder,
  309. id: firstTokenId,
  310. value: firstAmount,
  311. });
  312. it('calls onERC1155Received', async function () {
  313. await expectEvent.inTransaction(this.transferReceipt.tx, ERC1155ReceiverMock, 'Received', {
  314. operator: multiTokenHolder,
  315. from: multiTokenHolder,
  316. id: firstTokenId,
  317. value: firstAmount,
  318. data,
  319. });
  320. });
  321. });
  322. });
  323. context('to a receiver contract returning unexpected value', function () {
  324. beforeEach(async function () {
  325. this.receiver = await ERC1155ReceiverMock.new(
  326. '0x00c0ffee',
  327. RevertType.None,
  328. RECEIVER_BATCH_MAGIC_VALUE,
  329. RevertType.None,
  330. );
  331. });
  332. it('reverts', async function () {
  333. await expectRevertCustomError(
  334. this.token.safeTransferFrom(multiTokenHolder, this.receiver.address, firstTokenId, firstAmount, '0x', {
  335. from: multiTokenHolder,
  336. }),
  337. 'ERC1155InvalidReceiver',
  338. [this.receiver.address],
  339. );
  340. });
  341. });
  342. context('to a receiver contract that reverts', function () {
  343. it('with empty reason', async function () {
  344. const receiver = await ERC1155ReceiverMock.new(
  345. RECEIVER_SINGLE_MAGIC_VALUE,
  346. RevertType.Empty,
  347. RECEIVER_BATCH_MAGIC_VALUE,
  348. RevertType.None,
  349. );
  350. await expectRevertCustomError(
  351. this.token.safeTransferFrom(multiTokenHolder, receiver.address, firstTokenId, firstAmount, '0x', {
  352. from: multiTokenHolder,
  353. }),
  354. 'ERC1155InvalidReceiver',
  355. [receiver.address],
  356. );
  357. });
  358. it('with reason string', async function () {
  359. const receiver = await ERC1155ReceiverMock.new(
  360. RECEIVER_SINGLE_MAGIC_VALUE,
  361. RevertType.String,
  362. RECEIVER_BATCH_MAGIC_VALUE,
  363. RevertType.None,
  364. );
  365. await expectRevert(
  366. this.token.safeTransferFrom(multiTokenHolder, receiver.address, firstTokenId, firstAmount, '0x', {
  367. from: multiTokenHolder,
  368. }),
  369. 'ERC1155ReceiverMock: reverting on receive',
  370. );
  371. });
  372. it('with custom error', async function () {
  373. const receiver = await ERC1155ReceiverMock.new(
  374. RECEIVER_SINGLE_MAGIC_VALUE,
  375. RevertType.Custom,
  376. RECEIVER_BATCH_MAGIC_VALUE,
  377. RevertType.None,
  378. );
  379. await expectRevertCustomError(
  380. this.token.safeTransferFrom(multiTokenHolder, receiver.address, firstTokenId, firstAmount, '0x', {
  381. from: multiTokenHolder,
  382. }),
  383. 'ERC1155ReceiverMockError',
  384. [],
  385. );
  386. });
  387. });
  388. context('to a contract that does not implement the required function', function () {
  389. it('reverts', async function () {
  390. const invalidReceiver = this.token;
  391. await expectRevert.unspecified(
  392. this.token.safeTransferFrom(multiTokenHolder, invalidReceiver.address, firstTokenId, firstAmount, '0x', {
  393. from: multiTokenHolder,
  394. }),
  395. );
  396. });
  397. });
  398. });
  399. describe('safeBatchTransferFrom', function () {
  400. beforeEach(async function () {
  401. await this.token.$_mint(multiTokenHolder, firstTokenId, firstAmount, '0x', {
  402. from: minter,
  403. });
  404. await this.token.$_mint(multiTokenHolder, secondTokenId, secondAmount, '0x', {
  405. from: minter,
  406. });
  407. });
  408. it('reverts when transferring amount more than any of balances', async function () {
  409. await expectRevertCustomError(
  410. this.token.safeBatchTransferFrom(
  411. multiTokenHolder,
  412. recipient,
  413. [firstTokenId, secondTokenId],
  414. [firstAmount, secondAmount.addn(1)],
  415. '0x',
  416. { from: multiTokenHolder },
  417. ),
  418. 'ERC1155InsufficientBalance',
  419. [multiTokenHolder, secondAmount, secondAmount.addn(1), secondTokenId],
  420. );
  421. });
  422. it("reverts when ids array length doesn't match amounts array length", async function () {
  423. const ids1 = [firstTokenId];
  424. const amounts1 = [firstAmount, secondAmount];
  425. await expectRevertCustomError(
  426. this.token.safeBatchTransferFrom(multiTokenHolder, recipient, ids1, amounts1, '0x', {
  427. from: multiTokenHolder,
  428. }),
  429. 'ERC1155InvalidArrayLength',
  430. [ids1.length, amounts1.length],
  431. );
  432. const ids2 = [firstTokenId, secondTokenId];
  433. const amounts2 = [firstAmount];
  434. await expectRevertCustomError(
  435. this.token.safeBatchTransferFrom(multiTokenHolder, recipient, ids2, amounts2, '0x', {
  436. from: multiTokenHolder,
  437. }),
  438. 'ERC1155InvalidArrayLength',
  439. [ids2.length, amounts2.length],
  440. );
  441. });
  442. it('reverts when transferring to zero address', async function () {
  443. await expectRevertCustomError(
  444. this.token.safeBatchTransferFrom(
  445. multiTokenHolder,
  446. ZERO_ADDRESS,
  447. [firstTokenId, secondTokenId],
  448. [firstAmount, secondAmount],
  449. '0x',
  450. { from: multiTokenHolder },
  451. ),
  452. 'ERC1155InvalidReceiver',
  453. [ZERO_ADDRESS],
  454. );
  455. });
  456. it('reverts when transferring from zero address', async function () {
  457. await expectRevertCustomError(
  458. this.token.$_safeBatchTransferFrom(ZERO_ADDRESS, multiTokenHolder, [firstTokenId], [firstAmount], '0x'),
  459. 'ERC1155InvalidSender',
  460. [ZERO_ADDRESS],
  461. );
  462. });
  463. function batchTransferWasSuccessful({ operator, from, ids, values }) {
  464. it('debits transferred balances from sender', async function () {
  465. const newBalances = await this.token.balanceOfBatch(new Array(ids.length).fill(from), ids);
  466. for (const newBalance of newBalances) {
  467. expect(newBalance).to.be.a.bignumber.equal('0');
  468. }
  469. });
  470. it('credits transferred balances to receiver', async function () {
  471. const newBalances = await this.token.balanceOfBatch(new Array(ids.length).fill(this.toWhom), ids);
  472. for (let i = 0; i < newBalances.length; i++) {
  473. expect(newBalances[i]).to.be.a.bignumber.equal(values[i]);
  474. }
  475. });
  476. it('emits a TransferBatch log', function () {
  477. expectEvent(this.transferLogs, 'TransferBatch', {
  478. operator,
  479. from,
  480. to: this.toWhom,
  481. // ids,
  482. // values,
  483. });
  484. });
  485. }
  486. context('when called by the multiTokenHolder', async function () {
  487. beforeEach(async function () {
  488. this.toWhom = recipient;
  489. this.transferLogs = await this.token.safeBatchTransferFrom(
  490. multiTokenHolder,
  491. recipient,
  492. [firstTokenId, secondTokenId],
  493. [firstAmount, secondAmount],
  494. '0x',
  495. { from: multiTokenHolder },
  496. );
  497. });
  498. batchTransferWasSuccessful.call(this, {
  499. operator: multiTokenHolder,
  500. from: multiTokenHolder,
  501. ids: [firstTokenId, secondTokenId],
  502. values: [firstAmount, secondAmount],
  503. });
  504. });
  505. context('when called by an operator on behalf of the multiTokenHolder', function () {
  506. context('when operator is not approved by multiTokenHolder', function () {
  507. beforeEach(async function () {
  508. await this.token.setApprovalForAll(proxy, false, { from: multiTokenHolder });
  509. });
  510. it('reverts', async function () {
  511. await expectRevertCustomError(
  512. this.token.safeBatchTransferFrom(
  513. multiTokenHolder,
  514. recipient,
  515. [firstTokenId, secondTokenId],
  516. [firstAmount, secondAmount],
  517. '0x',
  518. { from: proxy },
  519. ),
  520. 'ERC1155InsufficientApprovalForAll',
  521. [proxy, multiTokenHolder],
  522. );
  523. });
  524. });
  525. context('when operator is approved by multiTokenHolder', function () {
  526. beforeEach(async function () {
  527. this.toWhom = recipient;
  528. await this.token.setApprovalForAll(proxy, true, { from: multiTokenHolder });
  529. this.transferLogs = await this.token.safeBatchTransferFrom(
  530. multiTokenHolder,
  531. recipient,
  532. [firstTokenId, secondTokenId],
  533. [firstAmount, secondAmount],
  534. '0x',
  535. { from: proxy },
  536. );
  537. });
  538. batchTransferWasSuccessful.call(this, {
  539. operator: proxy,
  540. from: multiTokenHolder,
  541. ids: [firstTokenId, secondTokenId],
  542. values: [firstAmount, secondAmount],
  543. });
  544. it("preserves operator's balances not involved in the transfer", async function () {
  545. const balance1 = await this.token.balanceOf(proxy, firstTokenId);
  546. expect(balance1).to.be.a.bignumber.equal('0');
  547. const balance2 = await this.token.balanceOf(proxy, secondTokenId);
  548. expect(balance2).to.be.a.bignumber.equal('0');
  549. });
  550. });
  551. });
  552. context('when sending to a valid receiver', function () {
  553. beforeEach(async function () {
  554. this.receiver = await ERC1155ReceiverMock.new(
  555. RECEIVER_SINGLE_MAGIC_VALUE,
  556. RevertType.None,
  557. RECEIVER_BATCH_MAGIC_VALUE,
  558. RevertType.None,
  559. );
  560. });
  561. context('without data', function () {
  562. beforeEach(async function () {
  563. this.toWhom = this.receiver.address;
  564. this.transferReceipt = await this.token.safeBatchTransferFrom(
  565. multiTokenHolder,
  566. this.receiver.address,
  567. [firstTokenId, secondTokenId],
  568. [firstAmount, secondAmount],
  569. '0x',
  570. { from: multiTokenHolder },
  571. );
  572. this.transferLogs = this.transferReceipt;
  573. });
  574. batchTransferWasSuccessful.call(this, {
  575. operator: multiTokenHolder,
  576. from: multiTokenHolder,
  577. ids: [firstTokenId, secondTokenId],
  578. values: [firstAmount, secondAmount],
  579. });
  580. it('calls onERC1155BatchReceived', async function () {
  581. await expectEvent.inTransaction(this.transferReceipt.tx, ERC1155ReceiverMock, 'BatchReceived', {
  582. operator: multiTokenHolder,
  583. from: multiTokenHolder,
  584. // ids: [firstTokenId, secondTokenId],
  585. // values: [firstAmount, secondAmount],
  586. data: null,
  587. });
  588. });
  589. });
  590. context('with data', function () {
  591. const data = '0xf00dd00d';
  592. beforeEach(async function () {
  593. this.toWhom = this.receiver.address;
  594. this.transferReceipt = await this.token.safeBatchTransferFrom(
  595. multiTokenHolder,
  596. this.receiver.address,
  597. [firstTokenId, secondTokenId],
  598. [firstAmount, secondAmount],
  599. data,
  600. { from: multiTokenHolder },
  601. );
  602. this.transferLogs = this.transferReceipt;
  603. });
  604. batchTransferWasSuccessful.call(this, {
  605. operator: multiTokenHolder,
  606. from: multiTokenHolder,
  607. ids: [firstTokenId, secondTokenId],
  608. values: [firstAmount, secondAmount],
  609. });
  610. it('calls onERC1155Received', async function () {
  611. await expectEvent.inTransaction(this.transferReceipt.tx, ERC1155ReceiverMock, 'BatchReceived', {
  612. operator: multiTokenHolder,
  613. from: multiTokenHolder,
  614. // ids: [firstTokenId, secondTokenId],
  615. // values: [firstAmount, secondAmount],
  616. data,
  617. });
  618. });
  619. });
  620. });
  621. context('to a receiver contract returning unexpected value', function () {
  622. beforeEach(async function () {
  623. this.receiver = await ERC1155ReceiverMock.new(
  624. RECEIVER_SINGLE_MAGIC_VALUE,
  625. RevertType.None,
  626. RECEIVER_SINGLE_MAGIC_VALUE,
  627. RevertType.None,
  628. );
  629. });
  630. it('reverts', async function () {
  631. await expectRevertCustomError(
  632. this.token.safeBatchTransferFrom(
  633. multiTokenHolder,
  634. this.receiver.address,
  635. [firstTokenId, secondTokenId],
  636. [firstAmount, secondAmount],
  637. '0x',
  638. { from: multiTokenHolder },
  639. ),
  640. 'ERC1155InvalidReceiver',
  641. [this.receiver.address],
  642. );
  643. });
  644. });
  645. context('to a receiver contract that reverts', function () {
  646. it('with empty reason', async function () {
  647. const receiver = await ERC1155ReceiverMock.new(
  648. RECEIVER_SINGLE_MAGIC_VALUE,
  649. RevertType.None,
  650. RECEIVER_BATCH_MAGIC_VALUE,
  651. RevertType.Empty,
  652. );
  653. await expectRevertCustomError(
  654. this.token.safeBatchTransferFrom(
  655. multiTokenHolder,
  656. receiver.address,
  657. [firstTokenId, secondTokenId],
  658. [firstAmount, secondAmount],
  659. '0x',
  660. { from: multiTokenHolder },
  661. ),
  662. 'ERC1155InvalidReceiver',
  663. [receiver.address],
  664. );
  665. });
  666. it('with reason string', async function () {
  667. const receiver = await ERC1155ReceiverMock.new(
  668. RECEIVER_SINGLE_MAGIC_VALUE,
  669. RevertType.None,
  670. RECEIVER_BATCH_MAGIC_VALUE,
  671. RevertType.String,
  672. );
  673. await expectRevert(
  674. this.token.safeBatchTransferFrom(
  675. multiTokenHolder,
  676. receiver.address,
  677. [firstTokenId, secondTokenId],
  678. [firstAmount, secondAmount],
  679. '0x',
  680. { from: multiTokenHolder },
  681. ),
  682. 'ERC1155ReceiverMock: reverting on batch receive',
  683. );
  684. });
  685. it('with custom error', async function () {
  686. const receiver = await ERC1155ReceiverMock.new(
  687. RECEIVER_SINGLE_MAGIC_VALUE,
  688. RevertType.None,
  689. RECEIVER_BATCH_MAGIC_VALUE,
  690. RevertType.Custom,
  691. );
  692. await expectRevertCustomError(
  693. this.token.safeBatchTransferFrom(
  694. multiTokenHolder,
  695. receiver.address,
  696. [firstTokenId, secondTokenId],
  697. [firstAmount, secondAmount],
  698. '0x',
  699. { from: multiTokenHolder },
  700. ),
  701. 'ERC1155ReceiverMockError',
  702. [],
  703. );
  704. });
  705. });
  706. context('to a receiver contract that reverts only on single transfers', function () {
  707. beforeEach(async function () {
  708. this.receiver = await ERC1155ReceiverMock.new(
  709. RECEIVER_SINGLE_MAGIC_VALUE,
  710. RevertType.String,
  711. RECEIVER_BATCH_MAGIC_VALUE,
  712. RevertType.None,
  713. );
  714. this.toWhom = this.receiver.address;
  715. this.transferReceipt = await this.token.safeBatchTransferFrom(
  716. multiTokenHolder,
  717. this.receiver.address,
  718. [firstTokenId, secondTokenId],
  719. [firstAmount, secondAmount],
  720. '0x',
  721. { from: multiTokenHolder },
  722. );
  723. this.transferLogs = this.transferReceipt;
  724. });
  725. batchTransferWasSuccessful.call(this, {
  726. operator: multiTokenHolder,
  727. from: multiTokenHolder,
  728. ids: [firstTokenId, secondTokenId],
  729. values: [firstAmount, secondAmount],
  730. });
  731. it('calls onERC1155BatchReceived', async function () {
  732. await expectEvent.inTransaction(this.transferReceipt.tx, ERC1155ReceiverMock, 'BatchReceived', {
  733. operator: multiTokenHolder,
  734. from: multiTokenHolder,
  735. // ids: [firstTokenId, secondTokenId],
  736. // values: [firstAmount, secondAmount],
  737. data: null,
  738. });
  739. });
  740. });
  741. context('to a contract that does not implement the required function', function () {
  742. it('reverts', async function () {
  743. const invalidReceiver = this.token;
  744. await expectRevert.unspecified(
  745. this.token.safeBatchTransferFrom(
  746. multiTokenHolder,
  747. invalidReceiver.address,
  748. [firstTokenId, secondTokenId],
  749. [firstAmount, secondAmount],
  750. '0x',
  751. { from: multiTokenHolder },
  752. ),
  753. );
  754. });
  755. });
  756. });
  757. shouldSupportInterfaces(['ERC165', 'ERC1155']);
  758. });
  759. }
  760. module.exports = {
  761. shouldBehaveLikeERC1155,
  762. };