ERC1155.behavior.js 28 KB

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