ERC1155.test.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. const { ethers } = require('hardhat');
  2. const { expect } = require('chai');
  3. const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
  4. const { zip } = require('../../helpers/iterate');
  5. const { shouldBehaveLikeERC1155 } = require('./ERC1155.behavior');
  6. const initialURI = 'https://token-cdn-domain/{id}.json';
  7. async function fixture() {
  8. const [operator, holder, ...otherAccounts] = await ethers.getSigners();
  9. const token = await ethers.deployContract('$ERC1155', [initialURI]);
  10. return { token, operator, holder, otherAccounts };
  11. }
  12. describe('ERC1155', function () {
  13. beforeEach(async function () {
  14. Object.assign(this, await loadFixture(fixture));
  15. });
  16. shouldBehaveLikeERC1155();
  17. describe('internal functions', function () {
  18. const tokenId = 1990n;
  19. const mintValue = 9001n;
  20. const burnValue = 3000n;
  21. const tokenBatchIds = [2000n, 2010n, 2020n];
  22. const mintValues = [5000n, 10000n, 42195n];
  23. const burnValues = [5000n, 9001n, 195n];
  24. const data = '0x12345678';
  25. describe('_mint', function () {
  26. it('reverts with a zero destination address', async function () {
  27. await expect(this.token.$_mint(ethers.ZeroAddress, tokenId, mintValue, data))
  28. .to.be.revertedWithCustomError(this.token, 'ERC1155InvalidReceiver')
  29. .withArgs(ethers.ZeroAddress);
  30. });
  31. describe('with minted tokens', function () {
  32. beforeEach(async function () {
  33. this.tx = await this.token.connect(this.operator).$_mint(this.holder, tokenId, mintValue, data);
  34. });
  35. it('emits a TransferSingle event', async function () {
  36. await expect(this.tx)
  37. .to.emit(this.token, 'TransferSingle')
  38. .withArgs(this.operator, ethers.ZeroAddress, this.holder, tokenId, mintValue);
  39. });
  40. it('credits the minted token value', async function () {
  41. expect(await this.token.balanceOf(this.holder, tokenId)).to.equal(mintValue);
  42. });
  43. });
  44. });
  45. describe('_mintBatch', function () {
  46. it('reverts with a zero destination address', async function () {
  47. await expect(this.token.$_mintBatch(ethers.ZeroAddress, tokenBatchIds, mintValues, data))
  48. .to.be.revertedWithCustomError(this.token, 'ERC1155InvalidReceiver')
  49. .withArgs(ethers.ZeroAddress);
  50. });
  51. it('reverts if length of inputs do not match', async function () {
  52. await expect(this.token.$_mintBatch(this.holder, tokenBatchIds, mintValues.slice(1), data))
  53. .to.be.revertedWithCustomError(this.token, 'ERC1155InvalidArrayLength')
  54. .withArgs(tokenBatchIds.length, mintValues.length - 1);
  55. await expect(this.token.$_mintBatch(this.holder, tokenBatchIds.slice(1), mintValues, data))
  56. .to.be.revertedWithCustomError(this.token, 'ERC1155InvalidArrayLength')
  57. .withArgs(tokenBatchIds.length - 1, mintValues.length);
  58. });
  59. describe('with minted batch of tokens', function () {
  60. beforeEach(async function () {
  61. this.tx = await this.token.connect(this.operator).$_mintBatch(this.holder, tokenBatchIds, mintValues, data);
  62. });
  63. it('emits a TransferBatch event', async function () {
  64. await expect(this.tx)
  65. .to.emit(this.token, 'TransferBatch')
  66. .withArgs(this.operator, ethers.ZeroAddress, this.holder, tokenBatchIds, mintValues);
  67. });
  68. it('credits the minted batch of tokens', async function () {
  69. const holderBatchBalances = await this.token.balanceOfBatch(
  70. tokenBatchIds.map(() => this.holder),
  71. tokenBatchIds,
  72. );
  73. expect(holderBatchBalances).to.deep.equal(mintValues);
  74. });
  75. });
  76. });
  77. describe('_burn', function () {
  78. it("reverts when burning the zero account's tokens", async function () {
  79. await expect(this.token.$_burn(ethers.ZeroAddress, tokenId, mintValue))
  80. .to.be.revertedWithCustomError(this.token, 'ERC1155InvalidSender')
  81. .withArgs(ethers.ZeroAddress);
  82. });
  83. it('reverts when burning a non-existent token id', async function () {
  84. await expect(this.token.$_burn(this.holder, tokenId, mintValue))
  85. .to.be.revertedWithCustomError(this.token, 'ERC1155InsufficientBalance')
  86. .withArgs(this.holder, 0, mintValue, tokenId);
  87. });
  88. it('reverts when burning more than available tokens', async function () {
  89. await this.token.connect(this.operator).$_mint(this.holder, tokenId, mintValue, data);
  90. await expect(this.token.$_burn(this.holder, tokenId, mintValue + 1n))
  91. .to.be.revertedWithCustomError(this.token, 'ERC1155InsufficientBalance')
  92. .withArgs(this.holder, mintValue, mintValue + 1n, tokenId);
  93. });
  94. describe('with minted-then-burnt tokens', function () {
  95. beforeEach(async function () {
  96. await this.token.$_mint(this.holder, tokenId, mintValue, data);
  97. this.tx = await this.token.connect(this.operator).$_burn(this.holder, tokenId, burnValue);
  98. });
  99. it('emits a TransferSingle event', async function () {
  100. await expect(this.tx)
  101. .to.emit(this.token, 'TransferSingle')
  102. .withArgs(this.operator, this.holder, ethers.ZeroAddress, tokenId, burnValue);
  103. });
  104. it('accounts for both minting and burning', async function () {
  105. expect(await this.token.balanceOf(this.holder, tokenId)).to.equal(mintValue - burnValue);
  106. });
  107. });
  108. });
  109. describe('_burnBatch', function () {
  110. it("reverts when burning the zero account's tokens", async function () {
  111. await expect(this.token.$_burnBatch(ethers.ZeroAddress, tokenBatchIds, burnValues))
  112. .to.be.revertedWithCustomError(this.token, 'ERC1155InvalidSender')
  113. .withArgs(ethers.ZeroAddress);
  114. });
  115. it('reverts if length of inputs do not match', async function () {
  116. await expect(this.token.$_burnBatch(this.holder, tokenBatchIds, burnValues.slice(1)))
  117. .to.be.revertedWithCustomError(this.token, 'ERC1155InvalidArrayLength')
  118. .withArgs(tokenBatchIds.length, burnValues.length - 1);
  119. await expect(this.token.$_burnBatch(this.holder, tokenBatchIds.slice(1), burnValues))
  120. .to.be.revertedWithCustomError(this.token, 'ERC1155InvalidArrayLength')
  121. .withArgs(tokenBatchIds.length - 1, burnValues.length);
  122. });
  123. it('reverts when burning a non-existent token id', async function () {
  124. await expect(this.token.$_burnBatch(this.holder, tokenBatchIds, burnValues))
  125. .to.be.revertedWithCustomError(this.token, 'ERC1155InsufficientBalance')
  126. .withArgs(this.holder, 0, burnValues[0], tokenBatchIds[0]);
  127. });
  128. describe('with minted-then-burnt tokens', function () {
  129. beforeEach(async function () {
  130. await this.token.$_mintBatch(this.holder, tokenBatchIds, mintValues, data);
  131. this.tx = await this.token.connect(this.operator).$_burnBatch(this.holder, tokenBatchIds, burnValues);
  132. });
  133. it('emits a TransferBatch event', async function () {
  134. await expect(this.tx)
  135. .to.emit(this.token, 'TransferBatch')
  136. .withArgs(this.operator, this.holder, ethers.ZeroAddress, tokenBatchIds, burnValues);
  137. });
  138. it('accounts for both minting and burning', async function () {
  139. const holderBatchBalances = await this.token.balanceOfBatch(
  140. tokenBatchIds.map(() => this.holder),
  141. tokenBatchIds,
  142. );
  143. expect(holderBatchBalances).to.deep.equal(
  144. zip(mintValues, burnValues).map(([mintValue, burnValue]) => mintValue - burnValue),
  145. );
  146. });
  147. });
  148. });
  149. });
  150. describe('ERC1155MetadataURI', function () {
  151. const firstTokenID = 42n;
  152. const secondTokenID = 1337n;
  153. it('emits no URI event in constructor', async function () {
  154. await expect(this.token.deploymentTransaction()).to.not.emit(this.token, 'URI');
  155. });
  156. it('sets the initial URI for all token types', async function () {
  157. expect(await this.token.uri(firstTokenID)).to.equal(initialURI);
  158. expect(await this.token.uri(secondTokenID)).to.equal(initialURI);
  159. });
  160. describe('_setURI', function () {
  161. const newURI = 'https://token-cdn-domain/{locale}/{id}.json';
  162. it('emits no URI event', async function () {
  163. await expect(this.token.$_setURI(newURI)).to.not.emit(this.token, 'URI');
  164. });
  165. it('sets the new URI for all token types', async function () {
  166. await this.token.$_setURI(newURI);
  167. expect(await this.token.uri(firstTokenID)).to.equal(newURI);
  168. expect(await this.token.uri(secondTokenID)).to.equal(newURI);
  169. });
  170. });
  171. });
  172. });