Przeglądaj źródła

Migrate ERC1155 tests to ethers v6 (#4771)

Co-authored-by: ernestognw <ernestognw@gmail.com>
Hadrien Croubois 1 rok temu
rodzic
commit
be0572a8dc

+ 4 - 1
scripts/generate/templates/EnumerableMap.opts.js

@@ -1,4 +1,7 @@
-const mapType = str => (str == 'uint256' ? 'Uint' : `${str.charAt(0).toUpperCase()}${str.slice(1)}`);
+const { capitalize } = require('../../helpers');
+
+const mapType = str => (str == 'uint256' ? 'Uint' : capitalize(str));
+
 const formatType = (keyType, valueType) => ({
   name: `${mapType(keyType)}To${mapType(valueType)}Map`,
   keyType,

+ 3 - 1
scripts/generate/templates/EnumerableSet.opts.js

@@ -1,4 +1,6 @@
-const mapType = str => (str == 'uint256' ? 'Uint' : `${str.charAt(0).toUpperCase()}${str.slice(1)}`);
+const { capitalize } = require('../../helpers');
+
+const mapType = str => (str == 'uint256' ? 'Uint' : capitalize(str));
 
 const formatType = type => ({
   name: `${mapType(type)}Set`,

+ 1 - 1
test/access/Ownable.test.js

@@ -14,7 +14,7 @@ describe('Ownable', function () {
   });
 
   it('emits ownership transfer events during construction', async function () {
-    await expect(await this.ownable.deploymentTransaction())
+    await expect(this.ownable.deploymentTransaction())
       .to.emit(this.ownable, 'OwnershipTransferred')
       .withArgs(ethers.ZeroAddress, this.owner.address);
   });

+ 1 - 1
test/access/manager/AccessManaged.test.js

@@ -32,7 +32,7 @@ describe('AccessManaged', function () {
   });
 
   it('sets authority and emits AuthorityUpdated event during construction', async function () {
-    await expect(await this.managed.deploymentTransaction())
+    await expect(this.managed.deploymentTransaction())
       .to.emit(this.managed, 'AuthorityUpdated')
       .withArgs(this.authority.target);
   });

+ 1 - 0
test/helpers/enums.js

@@ -14,6 +14,7 @@ function createExport(Enum) {
     VoteType: Enum('Against', 'For', 'Abstain'),
     Rounding: Enum('Floor', 'Ceil', 'Trunc', 'Expand'),
     OperationState: Enum('Unset', 'Waiting', 'Ready', 'Done'),
+    RevertType: Enum('None', 'RevertWithoutMessage', 'RevertWithMessage', 'RevertWithCustomError', 'Panic'),
   };
 }
 

+ 510 - 609
test/token/ERC1155/ERC1155.behavior.js

@@ -1,897 +1,798 @@
-const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
+const { ethers } = require('hardhat');
 const { expect } = require('chai');
-const { ZERO_ADDRESS } = constants;
-
+const { anyValue } = require('@nomicfoundation/hardhat-chai-matchers/withArgs');
+const {
+  bigint: { RevertType },
+} = require('../../helpers/enums');
 const { shouldSupportInterfaces } = require('../../utils/introspection/SupportsInterface.behavior');
-const { expectRevertCustomError } = require('../../helpers/customError');
-const { Enum } = require('../../helpers/enums');
-
-const ERC1155ReceiverMock = artifacts.require('ERC1155ReceiverMock');
-const RevertType = Enum('None', 'RevertWithoutMessage', 'RevertWithMessage', 'RevertWithCustomError', 'Panic');
 
-function shouldBehaveLikeERC1155([minter, firstTokenHolder, secondTokenHolder, multiTokenHolder, recipient, proxy]) {
-  const firstTokenId = new BN(1);
-  const secondTokenId = new BN(2);
-  const unknownTokenId = new BN(3);
+function shouldBehaveLikeERC1155() {
+  const firstTokenId = 1n;
+  const secondTokenId = 2n;
+  const unknownTokenId = 3n;
 
-  const firstTokenValue = new BN(1000);
-  const secondTokenValue = new BN(2000);
+  const firstTokenValue = 1000n;
+  const secondTokenValue = 2000n;
 
   const RECEIVER_SINGLE_MAGIC_VALUE = '0xf23a6e61';
   const RECEIVER_BATCH_MAGIC_VALUE = '0xbc197c81';
 
+  beforeEach(async function () {
+    [this.recipient, this.proxy, this.alice, this.bruce] = this.otherAccounts;
+  });
+
   describe('like an ERC1155', function () {
     describe('balanceOf', function () {
       it('should return 0 when queried about the zero address', async function () {
-        expect(await this.token.balanceOf(ZERO_ADDRESS, firstTokenId)).to.be.bignumber.equal('0');
+        expect(await this.token.balanceOf(ethers.ZeroAddress, firstTokenId)).to.equal(0n);
       });
 
-      context("when accounts don't own tokens", function () {
+      describe("when accounts don't own tokens", function () {
         it('returns zero for given addresses', async function () {
-          expect(await this.token.balanceOf(firstTokenHolder, firstTokenId)).to.be.bignumber.equal('0');
-
-          expect(await this.token.balanceOf(secondTokenHolder, secondTokenId)).to.be.bignumber.equal('0');
-
-          expect(await this.token.balanceOf(firstTokenHolder, unknownTokenId)).to.be.bignumber.equal('0');
+          expect(await this.token.balanceOf(this.alice, firstTokenId)).to.equal(0n);
+          expect(await this.token.balanceOf(this.bruce, secondTokenId)).to.equal(0n);
+          expect(await this.token.balanceOf(this.alice, unknownTokenId)).to.equal(0n);
         });
       });
 
-      context('when accounts own some tokens', function () {
+      describe('when accounts own some tokens', function () {
         beforeEach(async function () {
-          await this.token.$_mint(firstTokenHolder, firstTokenId, firstTokenValue, '0x', {
-            from: minter,
-          });
-          await this.token.$_mint(secondTokenHolder, secondTokenId, secondTokenValue, '0x', {
-            from: minter,
-          });
+          await this.token.$_mint(this.alice, firstTokenId, firstTokenValue, '0x');
+          await this.token.$_mint(this.bruce, secondTokenId, secondTokenValue, '0x');
         });
 
         it('returns the amount of tokens owned by the given addresses', async function () {
-          expect(await this.token.balanceOf(firstTokenHolder, firstTokenId)).to.be.bignumber.equal(firstTokenValue);
-
-          expect(await this.token.balanceOf(secondTokenHolder, secondTokenId)).to.be.bignumber.equal(secondTokenValue);
-
-          expect(await this.token.balanceOf(firstTokenHolder, unknownTokenId)).to.be.bignumber.equal('0');
+          expect(await this.token.balanceOf(this.alice, firstTokenId)).to.equal(firstTokenValue);
+          expect(await this.token.balanceOf(this.bruce, secondTokenId)).to.equal(secondTokenValue);
+          expect(await this.token.balanceOf(this.alice, unknownTokenId)).to.equal(0n);
         });
       });
     });
 
     describe('balanceOfBatch', function () {
       it("reverts when input arrays don't match up", async function () {
-        const accounts1 = [firstTokenHolder, secondTokenHolder, firstTokenHolder, secondTokenHolder];
+        const accounts1 = [this.alice, this.bruce, this.alice, this.bruce];
         const ids1 = [firstTokenId, secondTokenId, unknownTokenId];
-        await expectRevertCustomError(this.token.balanceOfBatch(accounts1, ids1), 'ERC1155InvalidArrayLength', [
-          accounts1.length,
-          ids1.length,
-        ]);
 
-        const accounts2 = [firstTokenHolder, secondTokenHolder];
+        await expect(this.token.balanceOfBatch(accounts1, ids1))
+          .to.be.revertedWithCustomError(this.token, 'ERC1155InvalidArrayLength')
+          .withArgs(ids1.length, accounts1.length);
+
+        const accounts2 = [this.alice, this.bruce];
         const ids2 = [firstTokenId, secondTokenId, unknownTokenId];
-        await expectRevertCustomError(this.token.balanceOfBatch(accounts2, ids2), 'ERC1155InvalidArrayLength', [
-          accounts2.length,
-          ids2.length,
-        ]);
+        await expect(this.token.balanceOfBatch(accounts2, ids2))
+          .to.be.revertedWithCustomError(this.token, 'ERC1155InvalidArrayLength')
+          .withArgs(ids2.length, accounts2.length);
       });
 
       it('should return 0 as the balance when one of the addresses is the zero address', async function () {
         const result = await this.token.balanceOfBatch(
-          [firstTokenHolder, secondTokenHolder, ZERO_ADDRESS],
+          [this.alice, this.bruce, ethers.ZeroAddress],
           [firstTokenId, secondTokenId, unknownTokenId],
         );
-        expect(result).to.be.an('array');
-        expect(result[0]).to.be.a.bignumber.equal('0');
-        expect(result[1]).to.be.a.bignumber.equal('0');
-        expect(result[2]).to.be.a.bignumber.equal('0');
+        expect(result).to.deep.equal([0n, 0n, 0n]);
       });
 
-      context("when accounts don't own tokens", function () {
+      describe("when accounts don't own tokens", function () {
         it('returns zeros for each account', async function () {
           const result = await this.token.balanceOfBatch(
-            [firstTokenHolder, secondTokenHolder, firstTokenHolder],
+            [this.alice, this.bruce, this.alice],
             [firstTokenId, secondTokenId, unknownTokenId],
           );
-          expect(result).to.be.an('array');
-          expect(result[0]).to.be.a.bignumber.equal('0');
-          expect(result[1]).to.be.a.bignumber.equal('0');
-          expect(result[2]).to.be.a.bignumber.equal('0');
+          expect(result).to.deep.equal([0n, 0n, 0n]);
         });
       });
 
-      context('when accounts own some tokens', function () {
+      describe('when accounts own some tokens', function () {
         beforeEach(async function () {
-          await this.token.$_mint(firstTokenHolder, firstTokenId, firstTokenValue, '0x', {
-            from: minter,
-          });
-          await this.token.$_mint(secondTokenHolder, secondTokenId, secondTokenValue, '0x', {
-            from: minter,
-          });
+          await this.token.$_mint(this.alice, firstTokenId, firstTokenValue, '0x');
+          await this.token.$_mint(this.bruce, secondTokenId, secondTokenValue, '0x');
         });
 
         it('returns amounts owned by each account in order passed', async function () {
           const result = await this.token.balanceOfBatch(
-            [secondTokenHolder, firstTokenHolder, firstTokenHolder],
+            [this.bruce, this.alice, this.alice],
             [secondTokenId, firstTokenId, unknownTokenId],
           );
-          expect(result).to.be.an('array');
-          expect(result[0]).to.be.a.bignumber.equal(secondTokenValue);
-          expect(result[1]).to.be.a.bignumber.equal(firstTokenValue);
-          expect(result[2]).to.be.a.bignumber.equal('0');
+          expect(result).to.deep.equal([secondTokenValue, firstTokenValue, 0n]);
         });
 
         it('returns multiple times the balance of the same address when asked', async function () {
           const result = await this.token.balanceOfBatch(
-            [firstTokenHolder, secondTokenHolder, firstTokenHolder],
+            [this.alice, this.bruce, this.alice],
             [firstTokenId, secondTokenId, firstTokenId],
           );
-          expect(result).to.be.an('array');
-          expect(result[0]).to.be.a.bignumber.equal(result[2]);
-          expect(result[0]).to.be.a.bignumber.equal(firstTokenValue);
-          expect(result[1]).to.be.a.bignumber.equal(secondTokenValue);
-          expect(result[2]).to.be.a.bignumber.equal(firstTokenValue);
+          expect(result).to.deep.equal([firstTokenValue, secondTokenValue, firstTokenValue]);
         });
       });
     });
 
     describe('setApprovalForAll', function () {
-      let receipt;
       beforeEach(async function () {
-        receipt = await this.token.setApprovalForAll(proxy, true, { from: multiTokenHolder });
+        this.tx = await this.token.connect(this.holder).setApprovalForAll(this.proxy, true);
       });
 
       it('sets approval status which can be queried via isApprovedForAll', async function () {
-        expect(await this.token.isApprovedForAll(multiTokenHolder, proxy)).to.be.equal(true);
+        expect(await this.token.isApprovedForAll(this.holder, this.proxy)).to.be.true;
       });
 
-      it('emits an ApprovalForAll log', function () {
-        expectEvent(receipt, 'ApprovalForAll', { account: multiTokenHolder, operator: proxy, approved: true });
+      it('emits an ApprovalForAll log', async function () {
+        await expect(this.tx)
+          .to.emit(this.token, 'ApprovalForAll')
+          .withArgs(this.holder.address, this.proxy.address, true);
       });
 
       it('can unset approval for an operator', async function () {
-        await this.token.setApprovalForAll(proxy, false, { from: multiTokenHolder });
-        expect(await this.token.isApprovedForAll(multiTokenHolder, proxy)).to.be.equal(false);
+        await this.token.connect(this.holder).setApprovalForAll(this.proxy, false);
+        expect(await this.token.isApprovedForAll(this.holder, this.proxy)).to.be.false;
       });
 
       it('reverts if attempting to approve zero address as an operator', async function () {
-        await expectRevertCustomError(
-          this.token.setApprovalForAll(constants.ZERO_ADDRESS, true, { from: multiTokenHolder }),
-          'ERC1155InvalidOperator',
-          [constants.ZERO_ADDRESS],
-        );
+        await expect(this.token.connect(this.holder).setApprovalForAll(ethers.ZeroAddress, true))
+          .to.be.revertedWithCustomError(this.token, 'ERC1155InvalidOperator')
+          .withArgs(ethers.ZeroAddress);
       });
     });
 
     describe('safeTransferFrom', function () {
       beforeEach(async function () {
-        await this.token.$_mint(multiTokenHolder, firstTokenId, firstTokenValue, '0x', {
-          from: minter,
-        });
-        await this.token.$_mint(multiTokenHolder, secondTokenId, secondTokenValue, '0x', {
-          from: minter,
-        });
+        await this.token.$_mint(this.holder, firstTokenId, firstTokenValue, '0x');
+        await this.token.$_mint(this.holder, secondTokenId, secondTokenValue, '0x');
       });
 
       it('reverts when transferring more than balance', async function () {
-        await expectRevertCustomError(
-          this.token.safeTransferFrom(multiTokenHolder, recipient, firstTokenId, firstTokenValue.addn(1), '0x', {
-            from: multiTokenHolder,
-          }),
-          'ERC1155InsufficientBalance',
-          [multiTokenHolder, firstTokenValue, firstTokenValue.addn(1), firstTokenId],
-        );
+        await expect(
+          this.token
+            .connect(this.holder)
+            .safeTransferFrom(this.holder, this.recipient, firstTokenId, firstTokenValue + 1n, '0x'),
+        )
+          .to.be.revertedWithCustomError(this.token, 'ERC1155InsufficientBalance')
+          .withArgs(this.holder.address, firstTokenValue, firstTokenValue + 1n, firstTokenId);
       });
 
       it('reverts when transferring to zero address', async function () {
-        await expectRevertCustomError(
-          this.token.safeTransferFrom(multiTokenHolder, ZERO_ADDRESS, firstTokenId, firstTokenValue, '0x', {
-            from: multiTokenHolder,
-          }),
-          'ERC1155InvalidReceiver',
-          [ZERO_ADDRESS],
-        );
+        await expect(
+          this.token
+            .connect(this.holder)
+            .safeTransferFrom(this.holder, ethers.ZeroAddress, firstTokenId, firstTokenValue, '0x'),
+        )
+          .to.be.revertedWithCustomError(this.token, 'ERC1155InvalidReceiver')
+          .withArgs(ethers.ZeroAddress);
       });
 
-      function transferWasSuccessful({ operator, from, id, value }) {
+      function transferWasSuccessful() {
         it('debits transferred balance from sender', async function () {
-          const newBalance = await this.token.balanceOf(from, id);
-          expect(newBalance).to.be.a.bignumber.equal('0');
+          expect(await this.token.balanceOf(this.args.from, this.args.id)).to.equal(0n);
         });
 
         it('credits transferred balance to receiver', async function () {
-          const newBalance = await this.token.balanceOf(this.toWhom, id);
-          expect(newBalance).to.be.a.bignumber.equal(value);
-        });
-
-        it('emits a TransferSingle log', function () {
-          expectEvent(this.transferLogs, 'TransferSingle', {
-            operator,
-            from,
-            to: this.toWhom,
-            id,
-            value,
-          });
+          expect(await this.token.balanceOf(this.args.to, this.args.id)).to.equal(this.args.value);
+        });
+
+        it('emits a TransferSingle log', async function () {
+          await expect(this.tx)
+            .to.emit(this.token, 'TransferSingle')
+            .withArgs(
+              this.args.operator.address ?? this.args.operator.target ?? this.args.operator,
+              this.args.from.address ?? this.args.from.target ?? this.args.from,
+              this.args.to.address ?? this.args.to.target ?? this.args.to,
+              this.args.id,
+              this.args.value,
+            );
         });
       }
 
-      context('when called by the multiTokenHolder', async function () {
+      describe('when called by the holder', async function () {
         beforeEach(async function () {
-          this.toWhom = recipient;
-          this.transferLogs = await this.token.safeTransferFrom(
-            multiTokenHolder,
-            recipient,
-            firstTokenId,
-            firstTokenValue,
-            '0x',
-            {
-              from: multiTokenHolder,
-            },
-          );
-        });
-
-        transferWasSuccessful.call(this, {
-          operator: multiTokenHolder,
-          from: multiTokenHolder,
-          id: firstTokenId,
-          value: firstTokenValue,
+          this.args = {
+            operator: this.holder,
+            from: this.holder,
+            to: this.recipient,
+            id: firstTokenId,
+            value: firstTokenValue,
+            data: '0x',
+          };
+          this.tx = await this.token
+            .connect(this.args.operator)
+            .safeTransferFrom(this.args.from, this.args.to, this.args.id, this.args.value, this.args.data);
         });
 
-        it('preserves existing balances which are not transferred by multiTokenHolder', async function () {
-          const balance1 = await this.token.balanceOf(multiTokenHolder, secondTokenId);
-          expect(balance1).to.be.a.bignumber.equal(secondTokenValue);
+        transferWasSuccessful();
 
-          const balance2 = await this.token.balanceOf(recipient, secondTokenId);
-          expect(balance2).to.be.a.bignumber.equal('0');
+        it('preserves existing balances which are not transferred by holder', async function () {
+          expect(await this.token.balanceOf(this.holder, secondTokenId)).to.equal(secondTokenValue);
+          expect(await this.token.balanceOf(this.recipient, secondTokenId)).to.equal(0n);
         });
       });
 
-      context('when called by an operator on behalf of the multiTokenHolder', function () {
-        context('when operator is not approved by multiTokenHolder', function () {
+      describe('when called by an operator on behalf of the holder', function () {
+        describe('when operator is not approved by holder', function () {
           beforeEach(async function () {
-            await this.token.setApprovalForAll(proxy, false, { from: multiTokenHolder });
+            await this.token.connect(this.holder).setApprovalForAll(this.proxy, false);
           });
 
           it('reverts', async function () {
-            await expectRevertCustomError(
-              this.token.safeTransferFrom(multiTokenHolder, recipient, firstTokenId, firstTokenValue, '0x', {
-                from: proxy,
-              }),
-              'ERC1155MissingApprovalForAll',
-              [proxy, multiTokenHolder],
-            );
+            await expect(
+              this.token
+                .connect(this.proxy)
+                .safeTransferFrom(this.holder, this.recipient, firstTokenId, firstTokenValue, '0x'),
+            )
+              .to.be.revertedWithCustomError(this.token, 'ERC1155MissingApprovalForAll')
+              .withArgs(this.proxy.address, this.holder.address);
           });
         });
 
-        context('when operator is approved by multiTokenHolder', function () {
+        describe('when operator is approved by holder', function () {
           beforeEach(async function () {
-            this.toWhom = recipient;
-            await this.token.setApprovalForAll(proxy, true, { from: multiTokenHolder });
-            this.transferLogs = await this.token.safeTransferFrom(
-              multiTokenHolder,
-              recipient,
-              firstTokenId,
-              firstTokenValue,
-              '0x',
-              {
-                from: proxy,
-              },
-            );
-          });
+            await this.token.connect(this.holder).setApprovalForAll(this.proxy, true);
 
-          transferWasSuccessful.call(this, {
-            operator: proxy,
-            from: multiTokenHolder,
-            id: firstTokenId,
-            value: firstTokenValue,
+            this.args = {
+              operator: this.proxy,
+              from: this.holder,
+              to: this.recipient,
+              id: firstTokenId,
+              value: firstTokenValue,
+              data: '0x',
+            };
+            this.tx = await this.token
+              .connect(this.args.operator)
+              .safeTransferFrom(this.args.from, this.args.to, this.args.id, this.args.value, this.args.data);
           });
 
-          it("preserves operator's balances not involved in the transfer", async function () {
-            const balance1 = await this.token.balanceOf(proxy, firstTokenId);
-            expect(balance1).to.be.a.bignumber.equal('0');
+          transferWasSuccessful();
 
-            const balance2 = await this.token.balanceOf(proxy, secondTokenId);
-            expect(balance2).to.be.a.bignumber.equal('0');
+          it("preserves operator's balances not involved in the transfer", async function () {
+            expect(await this.token.balanceOf(this.proxy, firstTokenId)).to.equal(0n);
+            expect(await this.token.balanceOf(this.proxy, secondTokenId)).to.equal(0n);
           });
         });
       });
 
-      context('when sending to a valid receiver', function () {
+      describe('when sending to a valid receiver', function () {
         beforeEach(async function () {
-          this.receiver = await ERC1155ReceiverMock.new(
+          this.receiver = await ethers.deployContract('$ERC1155ReceiverMock', [
             RECEIVER_SINGLE_MAGIC_VALUE,
             RECEIVER_BATCH_MAGIC_VALUE,
             RevertType.None,
-          );
+          ]);
         });
 
-        context('without data', function () {
+        describe('without data', function () {
           beforeEach(async function () {
-            this.toWhom = this.receiver.address;
-            this.transferReceipt = await this.token.safeTransferFrom(
-              multiTokenHolder,
-              this.receiver.address,
-              firstTokenId,
-              firstTokenValue,
-              '0x',
-              { from: multiTokenHolder },
-            );
-            this.transferLogs = this.transferReceipt;
+            this.args = {
+              operator: this.holder,
+              from: this.holder,
+              to: this.receiver,
+              id: firstTokenId,
+              value: firstTokenValue,
+              data: '0x',
+            };
+            this.tx = await this.token
+              .connect(this.args.operator)
+              .safeTransferFrom(this.args.from, this.args.to, this.args.id, this.args.value, this.args.data);
           });
 
-          transferWasSuccessful.call(this, {
-            operator: multiTokenHolder,
-            from: multiTokenHolder,
-            id: firstTokenId,
-            value: firstTokenValue,
-          });
+          transferWasSuccessful();
 
           it('calls onERC1155Received', async function () {
-            await expectEvent.inTransaction(this.transferReceipt.tx, ERC1155ReceiverMock, 'Received', {
-              operator: multiTokenHolder,
-              from: multiTokenHolder,
-              id: firstTokenId,
-              value: firstTokenValue,
-              data: null,
-            });
+            await expect(this.tx)
+              .to.emit(this.receiver, 'Received')
+              .withArgs(
+                this.args.operator.address,
+                this.args.from.address,
+                this.args.id,
+                this.args.value,
+                this.args.data,
+                anyValue,
+              );
           });
         });
 
-        context('with data', function () {
-          const data = '0xf00dd00d';
+        describe('with data', function () {
           beforeEach(async function () {
-            this.toWhom = this.receiver.address;
-            this.transferReceipt = await this.token.safeTransferFrom(
-              multiTokenHolder,
-              this.receiver.address,
-              firstTokenId,
-              firstTokenValue,
-              data,
-              { from: multiTokenHolder },
-            );
-            this.transferLogs = this.transferReceipt;
+            this.args = {
+              operator: this.holder,
+              from: this.holder,
+              to: this.receiver,
+              id: firstTokenId,
+              value: firstTokenValue,
+              data: '0xf00dd00d',
+            };
+            this.tx = await this.token
+              .connect(this.args.operator)
+              .safeTransferFrom(this.args.from, this.args.to, this.args.id, this.args.value, this.args.data);
           });
 
-          transferWasSuccessful.call(this, {
-            operator: multiTokenHolder,
-            from: multiTokenHolder,
-            id: firstTokenId,
-            value: firstTokenValue,
-          });
+          transferWasSuccessful();
 
           it('calls onERC1155Received', async function () {
-            await expectEvent.inTransaction(this.transferReceipt.tx, ERC1155ReceiverMock, 'Received', {
-              operator: multiTokenHolder,
-              from: multiTokenHolder,
-              id: firstTokenId,
-              value: firstTokenValue,
-              data,
-            });
+            await expect(this.tx)
+              .to.emit(this.receiver, 'Received')
+              .withArgs(
+                this.args.operator.address,
+                this.args.from.address,
+                this.args.id,
+                this.args.value,
+                this.args.data,
+                anyValue,
+              );
           });
         });
       });
 
-      context('to a receiver contract returning unexpected value', function () {
-        beforeEach(async function () {
-          this.receiver = await ERC1155ReceiverMock.new('0x00c0ffee', RECEIVER_BATCH_MAGIC_VALUE, RevertType.None);
-        });
-
+      describe('to a receiver contract returning unexpected value', function () {
         it('reverts', async function () {
-          await expectRevertCustomError(
-            this.token.safeTransferFrom(multiTokenHolder, this.receiver.address, firstTokenId, firstTokenValue, '0x', {
-              from: multiTokenHolder,
-            }),
-            'ERC1155InvalidReceiver',
-            [this.receiver.address],
-          );
+          const receiver = await ethers.deployContract('$ERC1155ReceiverMock', [
+            '0x00c0ffee',
+            RECEIVER_BATCH_MAGIC_VALUE,
+            RevertType.None,
+          ]);
+
+          await expect(
+            this.token
+              .connect(this.holder)
+              .safeTransferFrom(this.holder, receiver, firstTokenId, firstTokenValue, '0x'),
+          )
+            .to.be.revertedWithCustomError(this.token, 'ERC1155InvalidReceiver')
+            .withArgs(receiver.target);
         });
       });
 
-      context('to a receiver contract that reverts', function () {
-        context('with a revert string', function () {
-          beforeEach(async function () {
-            this.receiver = await ERC1155ReceiverMock.new(
+      describe('to a receiver contract that reverts', function () {
+        describe('with a revert string', function () {
+          it('reverts', async function () {
+            const receiver = await ethers.deployContract('$ERC1155ReceiverMock', [
               RECEIVER_SINGLE_MAGIC_VALUE,
               RECEIVER_BATCH_MAGIC_VALUE,
               RevertType.RevertWithMessage,
-            );
-          });
+            ]);
 
-          it('reverts', async function () {
-            await expectRevert(
-              this.token.safeTransferFrom(
-                multiTokenHolder,
-                this.receiver.address,
-                firstTokenId,
-                firstTokenValue,
-                '0x',
-                {
-                  from: multiTokenHolder,
-                },
-              ),
-              'ERC1155ReceiverMock: reverting on receive',
-            );
+            await expect(
+              this.token
+                .connect(this.holder)
+                .safeTransferFrom(this.holder, receiver, firstTokenId, firstTokenValue, '0x'),
+            ).to.be.revertedWith('ERC1155ReceiverMock: reverting on receive');
           });
         });
 
-        context('without a revert string', function () {
-          beforeEach(async function () {
-            this.receiver = await ERC1155ReceiverMock.new(
+        describe('without a revert string', function () {
+          it('reverts', async function () {
+            const receiver = await ethers.deployContract('$ERC1155ReceiverMock', [
               RECEIVER_SINGLE_MAGIC_VALUE,
               RECEIVER_BATCH_MAGIC_VALUE,
               RevertType.RevertWithoutMessage,
-            );
-          });
+            ]);
 
-          it('reverts', async function () {
-            await expectRevertCustomError(
-              this.token.safeTransferFrom(
-                multiTokenHolder,
-                this.receiver.address,
-                firstTokenId,
-                firstTokenValue,
-                '0x',
-                {
-                  from: multiTokenHolder,
-                },
-              ),
-              'ERC1155InvalidReceiver',
-              [this.receiver.address],
-            );
+            await expect(
+              this.token
+                .connect(this.holder)
+                .safeTransferFrom(this.holder, receiver, firstTokenId, firstTokenValue, '0x'),
+            )
+              .to.be.revertedWithCustomError(this.token, 'ERC1155InvalidReceiver')
+              .withArgs(receiver.target);
           });
         });
 
-        context('with a custom error', function () {
-          beforeEach(async function () {
-            this.receiver = await ERC1155ReceiverMock.new(
+        describe('with a custom error', function () {
+          it('reverts', async function () {
+            const receiver = await ethers.deployContract('$ERC1155ReceiverMock', [
               RECEIVER_SINGLE_MAGIC_VALUE,
               RECEIVER_BATCH_MAGIC_VALUE,
               RevertType.RevertWithCustomError,
-            );
-          });
+            ]);
 
-          it('reverts', async function () {
-            await expectRevertCustomError(
-              this.token.safeTransferFrom(
-                multiTokenHolder,
-                this.receiver.address,
-                firstTokenId,
-                firstTokenValue,
-                '0x',
-                {
-                  from: multiTokenHolder,
-                },
-              ),
-              'CustomError',
-              [RECEIVER_SINGLE_MAGIC_VALUE],
-            );
+            await expect(
+              this.token
+                .connect(this.holder)
+                .safeTransferFrom(this.holder, receiver, firstTokenId, firstTokenValue, '0x'),
+            )
+              .to.be.revertedWithCustomError(receiver, 'CustomError')
+              .withArgs(RECEIVER_SINGLE_MAGIC_VALUE);
           });
         });
 
-        context('with a panic', function () {
-          beforeEach(async function () {
-            this.receiver = await ERC1155ReceiverMock.new(
+        describe('with a panic', function () {
+          it('reverts', async function () {
+            const receiver = await ethers.deployContract('$ERC1155ReceiverMock', [
               RECEIVER_SINGLE_MAGIC_VALUE,
               RECEIVER_BATCH_MAGIC_VALUE,
               RevertType.Panic,
-            );
-          });
+            ]);
 
-          it('reverts', async function () {
-            await expectRevert.unspecified(
-              this.token.safeTransferFrom(
-                multiTokenHolder,
-                this.receiver.address,
-                firstTokenId,
-                firstTokenValue,
-                '0x',
-                {
-                  from: multiTokenHolder,
-                },
-              ),
-            );
+            await expect(
+              this.token
+                .connect(this.holder)
+                .safeTransferFrom(this.holder, receiver, firstTokenId, firstTokenValue, '0x'),
+            ).to.be.revertedWithPanic();
           });
         });
       });
 
-      context('to a contract that does not implement the required function', function () {
+      describe('to a contract that does not implement the required function', function () {
         it('reverts', async function () {
-          const invalidReceiver = this.token;
-          await expectRevert.unspecified(
-            this.token.safeTransferFrom(
-              multiTokenHolder,
-              invalidReceiver.address,
-              firstTokenId,
-              firstTokenValue,
-              '0x',
-              {
-                from: multiTokenHolder,
-              },
-            ),
-          );
+          const invalidReceiver = this.token.target;
+
+          await expect(
+            this.token
+              .connect(this.holder)
+              .safeTransferFrom(this.holder, invalidReceiver, firstTokenId, firstTokenValue, '0x'),
+          )
+            .to.be.revertedWithCustomError(this.token, 'ERC1155InvalidReceiver')
+            .withArgs(invalidReceiver);
         });
       });
     });
 
     describe('safeBatchTransferFrom', function () {
       beforeEach(async function () {
-        await this.token.$_mint(multiTokenHolder, firstTokenId, firstTokenValue, '0x', {
-          from: minter,
-        });
-        await this.token.$_mint(multiTokenHolder, secondTokenId, secondTokenValue, '0x', {
-          from: minter,
-        });
+        await this.token.$_mint(this.holder, firstTokenId, firstTokenValue, '0x');
+        await this.token.$_mint(this.holder, secondTokenId, secondTokenValue, '0x');
       });
 
       it('reverts when transferring value more than any of balances', async function () {
-        await expectRevertCustomError(
-          this.token.safeBatchTransferFrom(
-            multiTokenHolder,
-            recipient,
-            [firstTokenId, secondTokenId],
-            [firstTokenValue, secondTokenValue.addn(1)],
-            '0x',
-            { from: multiTokenHolder },
-          ),
-          'ERC1155InsufficientBalance',
-          [multiTokenHolder, secondTokenValue, secondTokenValue.addn(1), secondTokenId],
-        );
+        await expect(
+          this.token
+            .connect(this.holder)
+            .safeBatchTransferFrom(
+              this.holder,
+              this.recipient,
+              [firstTokenId, secondTokenId],
+              [firstTokenValue, secondTokenValue + 1n],
+              '0x',
+            ),
+        )
+          .to.be.revertedWithCustomError(this.token, 'ERC1155InsufficientBalance')
+          .withArgs(this.holder.address, secondTokenValue, secondTokenValue + 1n, secondTokenId);
       });
 
       it("reverts when ids array length doesn't match values array length", async function () {
         const ids1 = [firstTokenId];
         const tokenValues1 = [firstTokenValue, secondTokenValue];
 
-        await expectRevertCustomError(
-          this.token.safeBatchTransferFrom(multiTokenHolder, recipient, ids1, tokenValues1, '0x', {
-            from: multiTokenHolder,
-          }),
-          'ERC1155InvalidArrayLength',
-          [ids1.length, tokenValues1.length],
-        );
+        await expect(
+          this.token.connect(this.holder).safeBatchTransferFrom(this.holder, this.recipient, ids1, tokenValues1, '0x'),
+        )
+          .to.be.revertedWithCustomError(this.token, 'ERC1155InvalidArrayLength')
+          .withArgs(ids1.length, tokenValues1.length);
 
         const ids2 = [firstTokenId, secondTokenId];
         const tokenValues2 = [firstTokenValue];
-        await expectRevertCustomError(
-          this.token.safeBatchTransferFrom(multiTokenHolder, recipient, ids2, tokenValues2, '0x', {
-            from: multiTokenHolder,
-          }),
-          'ERC1155InvalidArrayLength',
-          [ids2.length, tokenValues2.length],
-        );
+
+        await expect(
+          this.token.connect(this.holder).safeBatchTransferFrom(this.holder, this.recipient, ids2, tokenValues2, '0x'),
+        )
+          .to.be.revertedWithCustomError(this.token, 'ERC1155InvalidArrayLength')
+          .withArgs(ids2.length, tokenValues2.length);
       });
 
       it('reverts when transferring to zero address', async function () {
-        await expectRevertCustomError(
-          this.token.safeBatchTransferFrom(
-            multiTokenHolder,
-            ZERO_ADDRESS,
-            [firstTokenId, secondTokenId],
-            [firstTokenValue, secondTokenValue],
-            '0x',
-            { from: multiTokenHolder },
-          ),
-          'ERC1155InvalidReceiver',
-          [ZERO_ADDRESS],
-        );
+        await expect(
+          this.token
+            .connect(this.holder)
+            .safeBatchTransferFrom(
+              this.holder,
+              ethers.ZeroAddress,
+              [firstTokenId, secondTokenId],
+              [firstTokenValue, secondTokenValue],
+              '0x',
+            ),
+        )
+          .to.be.revertedWithCustomError(this.token, 'ERC1155InvalidReceiver')
+          .withArgs(ethers.ZeroAddress);
       });
 
       it('reverts when transferring from zero address', async function () {
-        await expectRevertCustomError(
-          this.token.$_safeBatchTransferFrom(ZERO_ADDRESS, multiTokenHolder, [firstTokenId], [firstTokenValue], '0x'),
-          'ERC1155InvalidSender',
-          [ZERO_ADDRESS],
-        );
+        await expect(
+          this.token.$_safeBatchTransferFrom(ethers.ZeroAddress, this.holder, [firstTokenId], [firstTokenValue], '0x'),
+        )
+          .to.be.revertedWithCustomError(this.token, 'ERC1155InvalidSender')
+          .withArgs(ethers.ZeroAddress);
       });
 
-      function batchTransferWasSuccessful({ operator, from, ids, values }) {
+      function batchTransferWasSuccessful() {
         it('debits transferred balances from sender', async function () {
-          const newBalances = await this.token.balanceOfBatch(new Array(ids.length).fill(from), ids);
-          for (const newBalance of newBalances) {
-            expect(newBalance).to.be.a.bignumber.equal('0');
-          }
+          const newBalances = await this.token.balanceOfBatch(
+            this.args.ids.map(() => this.args.from),
+            this.args.ids,
+          );
+          expect(newBalances).to.deep.equal(this.args.ids.map(() => 0n));
         });
 
         it('credits transferred balances to receiver', async function () {
-          const newBalances = await this.token.balanceOfBatch(new Array(ids.length).fill(this.toWhom), ids);
-          for (let i = 0; i < newBalances.length; i++) {
-            expect(newBalances[i]).to.be.a.bignumber.equal(values[i]);
-          }
-        });
-
-        it('emits a TransferBatch log', function () {
-          expectEvent(this.transferLogs, 'TransferBatch', {
-            operator,
-            from,
-            to: this.toWhom,
-            // ids,
-            // values,
-          });
+          const newBalances = await this.token.balanceOfBatch(
+            this.args.ids.map(() => this.args.to),
+            this.args.ids,
+          );
+          expect(newBalances).to.deep.equal(this.args.values);
+        });
+
+        it('emits a TransferBatch log', async function () {
+          await expect(this.tx)
+            .to.emit(this.token, 'TransferBatch')
+            .withArgs(
+              this.args.operator.address ?? this.args.operator.target ?? this.args.operator,
+              this.args.from.address ?? this.args.from.target ?? this.args.from,
+              this.args.to.address ?? this.args.to.target ?? this.args.to,
+              this.args.ids,
+              this.args.values,
+            );
         });
       }
 
-      context('when called by the multiTokenHolder', async function () {
+      describe('when called by the holder', async function () {
         beforeEach(async function () {
-          this.toWhom = recipient;
-          this.transferLogs = await this.token.safeBatchTransferFrom(
-            multiTokenHolder,
-            recipient,
-            [firstTokenId, secondTokenId],
-            [firstTokenValue, secondTokenValue],
-            '0x',
-            { from: multiTokenHolder },
-          );
+          this.args = {
+            operator: this.holder,
+            from: this.holder,
+            to: this.recipient,
+            ids: [firstTokenId, secondTokenId],
+            values: [firstTokenValue, secondTokenValue],
+            data: '0x',
+          };
+          this.tx = await this.token
+            .connect(this.args.operator)
+            .safeBatchTransferFrom(this.args.from, this.args.to, this.args.ids, this.args.values, this.args.data);
         });
 
-        batchTransferWasSuccessful.call(this, {
-          operator: multiTokenHolder,
-          from: multiTokenHolder,
-          ids: [firstTokenId, secondTokenId],
-          values: [firstTokenValue, secondTokenValue],
-        });
+        batchTransferWasSuccessful();
       });
 
-      context('when called by an operator on behalf of the multiTokenHolder', function () {
-        context('when operator is not approved by multiTokenHolder', function () {
+      describe('when called by an operator on behalf of the holder', function () {
+        describe('when operator is not approved by holder', function () {
           beforeEach(async function () {
-            await this.token.setApprovalForAll(proxy, false, { from: multiTokenHolder });
+            await this.token.connect(this.holder).setApprovalForAll(this.proxy, false);
           });
 
           it('reverts', async function () {
-            await expectRevertCustomError(
-              this.token.safeBatchTransferFrom(
-                multiTokenHolder,
-                recipient,
-                [firstTokenId, secondTokenId],
-                [firstTokenValue, secondTokenValue],
-                '0x',
-                { from: proxy },
-              ),
-              'ERC1155MissingApprovalForAll',
-              [proxy, multiTokenHolder],
-            );
-          });
-        });
-
-        context('when operator is approved by multiTokenHolder', function () {
+            await expect(
+              this.token
+                .connect(this.proxy)
+                .safeBatchTransferFrom(
+                  this.holder,
+                  this.recipient,
+                  [firstTokenId, secondTokenId],
+                  [firstTokenValue, secondTokenValue],
+                  '0x',
+                ),
+            )
+              .to.be.revertedWithCustomError(this.token, 'ERC1155MissingApprovalForAll')
+              .withArgs(this.proxy.address, this.holder.address);
+          });
+        });
+
+        describe('when operator is approved by holder', function () {
           beforeEach(async function () {
-            this.toWhom = recipient;
-            await this.token.setApprovalForAll(proxy, true, { from: multiTokenHolder });
-            this.transferLogs = await this.token.safeBatchTransferFrom(
-              multiTokenHolder,
-              recipient,
-              [firstTokenId, secondTokenId],
-              [firstTokenValue, secondTokenValue],
-              '0x',
-              { from: proxy },
-            );
-          });
+            await this.token.connect(this.holder).setApprovalForAll(this.proxy, true);
 
-          batchTransferWasSuccessful.call(this, {
-            operator: proxy,
-            from: multiTokenHolder,
-            ids: [firstTokenId, secondTokenId],
-            values: [firstTokenValue, secondTokenValue],
+            this.args = {
+              operator: this.proxy,
+              from: this.holder,
+              to: this.recipient,
+              ids: [firstTokenId, secondTokenId],
+              values: [firstTokenValue, secondTokenValue],
+              data: '0x',
+            };
+            this.tx = await this.token
+              .connect(this.args.operator)
+              .safeBatchTransferFrom(this.args.from, this.args.to, this.args.ids, this.args.values, this.args.data);
           });
 
+          batchTransferWasSuccessful();
+
           it("preserves operator's balances not involved in the transfer", async function () {
-            const balance1 = await this.token.balanceOf(proxy, firstTokenId);
-            expect(balance1).to.be.a.bignumber.equal('0');
-            const balance2 = await this.token.balanceOf(proxy, secondTokenId);
-            expect(balance2).to.be.a.bignumber.equal('0');
+            expect(await this.token.balanceOf(this.proxy, firstTokenId)).to.equal(0n);
+            expect(await this.token.balanceOf(this.proxy, secondTokenId)).to.equal(0n);
           });
         });
       });
 
-      context('when sending to a valid receiver', function () {
+      describe('when sending to a valid receiver', function () {
         beforeEach(async function () {
-          this.receiver = await ERC1155ReceiverMock.new(
+          this.receiver = await ethers.deployContract('$ERC1155ReceiverMock', [
             RECEIVER_SINGLE_MAGIC_VALUE,
             RECEIVER_BATCH_MAGIC_VALUE,
             RevertType.None,
-          );
+          ]);
         });
 
-        context('without data', function () {
+        describe('without data', function () {
           beforeEach(async function () {
-            this.toWhom = this.receiver.address;
-            this.transferReceipt = await this.token.safeBatchTransferFrom(
-              multiTokenHolder,
-              this.receiver.address,
-              [firstTokenId, secondTokenId],
-              [firstTokenValue, secondTokenValue],
-              '0x',
-              { from: multiTokenHolder },
-            );
-            this.transferLogs = this.transferReceipt;
+            this.args = {
+              operator: this.holder,
+              from: this.holder,
+              to: this.receiver,
+              ids: [firstTokenId, secondTokenId],
+              values: [firstTokenValue, secondTokenValue],
+              data: '0x',
+            };
+            this.tx = await this.token
+              .connect(this.args.operator)
+              .safeBatchTransferFrom(this.args.from, this.args.to, this.args.ids, this.args.values, this.args.data);
           });
 
-          batchTransferWasSuccessful.call(this, {
-            operator: multiTokenHolder,
-            from: multiTokenHolder,
-            ids: [firstTokenId, secondTokenId],
-            values: [firstTokenValue, secondTokenValue],
-          });
+          batchTransferWasSuccessful();
 
           it('calls onERC1155BatchReceived', async function () {
-            await expectEvent.inTransaction(this.transferReceipt.tx, ERC1155ReceiverMock, 'BatchReceived', {
-              operator: multiTokenHolder,
-              from: multiTokenHolder,
-              // ids: [firstTokenId, secondTokenId],
-              // values: [firstTokenValue, secondTokenValue],
-              data: null,
-            });
+            await expect(this.tx)
+              .to.emit(this.receiver, 'BatchReceived')
+              .withArgs(
+                this.holder.address,
+                this.holder.address,
+                this.args.ids,
+                this.args.values,
+                this.args.data,
+                anyValue,
+              );
           });
         });
 
-        context('with data', function () {
-          const data = '0xf00dd00d';
+        describe('with data', function () {
           beforeEach(async function () {
-            this.toWhom = this.receiver.address;
-            this.transferReceipt = await this.token.safeBatchTransferFrom(
-              multiTokenHolder,
-              this.receiver.address,
-              [firstTokenId, secondTokenId],
-              [firstTokenValue, secondTokenValue],
-              data,
-              { from: multiTokenHolder },
-            );
-            this.transferLogs = this.transferReceipt;
+            this.args = {
+              operator: this.holder,
+              from: this.holder,
+              to: this.receiver,
+              ids: [firstTokenId, secondTokenId],
+              values: [firstTokenValue, secondTokenValue],
+              data: '0xf00dd00d',
+            };
+            this.tx = await this.token
+              .connect(this.args.operator)
+              .safeBatchTransferFrom(this.args.from, this.args.to, this.args.ids, this.args.values, this.args.data);
           });
 
-          batchTransferWasSuccessful.call(this, {
-            operator: multiTokenHolder,
-            from: multiTokenHolder,
-            ids: [firstTokenId, secondTokenId],
-            values: [firstTokenValue, secondTokenValue],
-          });
+          batchTransferWasSuccessful();
 
           it('calls onERC1155Received', async function () {
-            await expectEvent.inTransaction(this.transferReceipt.tx, ERC1155ReceiverMock, 'BatchReceived', {
-              operator: multiTokenHolder,
-              from: multiTokenHolder,
-              // ids: [firstTokenId, secondTokenId],
-              // values: [firstTokenValue, secondTokenValue],
-              data,
-            });
+            await expect(this.tx)
+              .to.emit(this.receiver, 'BatchReceived')
+              .withArgs(
+                this.holder.address,
+                this.holder.address,
+                this.args.ids,
+                this.args.values,
+                this.args.data,
+                anyValue,
+              );
           });
         });
       });
 
-      context('to a receiver contract returning unexpected value', function () {
-        beforeEach(async function () {
-          this.receiver = await ERC1155ReceiverMock.new(
+      describe('to a receiver contract returning unexpected value', function () {
+        it('reverts', async function () {
+          const receiver = await ethers.deployContract('$ERC1155ReceiverMock', [
             RECEIVER_SINGLE_MAGIC_VALUE,
             RECEIVER_SINGLE_MAGIC_VALUE,
             RevertType.None,
-          );
-        });
-
-        it('reverts', async function () {
-          await expectRevertCustomError(
-            this.token.safeBatchTransferFrom(
-              multiTokenHolder,
-              this.receiver.address,
-              [firstTokenId, secondTokenId],
-              [firstTokenValue, secondTokenValue],
-              '0x',
-              { from: multiTokenHolder },
-            ),
-            'ERC1155InvalidReceiver',
-            [this.receiver.address],
-          );
+          ]);
+
+          await expect(
+            this.token
+              .connect(this.holder)
+              .safeBatchTransferFrom(
+                this.holder,
+                receiver,
+                [firstTokenId, secondTokenId],
+                [firstTokenValue, secondTokenValue],
+                '0x',
+              ),
+          )
+            .to.be.revertedWithCustomError(this.token, 'ERC1155InvalidReceiver')
+            .withArgs(receiver.target);
         });
       });
 
-      context('to a receiver contract that reverts', function () {
-        context('with a revert string', function () {
-          beforeEach(async function () {
-            this.receiver = await ERC1155ReceiverMock.new(
+      describe('to a receiver contract that reverts', function () {
+        describe('with a revert string', function () {
+          it('reverts', async function () {
+            const receiver = await ethers.deployContract('$ERC1155ReceiverMock', [
               RECEIVER_SINGLE_MAGIC_VALUE,
               RECEIVER_BATCH_MAGIC_VALUE,
               RevertType.RevertWithMessage,
-            );
-          });
+            ]);
 
-          it('reverts', async function () {
-            await expectRevert(
-              this.token.safeBatchTransferFrom(
-                multiTokenHolder,
-                this.receiver.address,
-                [firstTokenId, secondTokenId],
-                [firstTokenValue, secondTokenValue],
-                '0x',
-                { from: multiTokenHolder },
-              ),
-              'ERC1155ReceiverMock: reverting on batch receive',
-            );
+            await expect(
+              this.token
+                .connect(this.holder)
+                .safeBatchTransferFrom(
+                  this.holder,
+                  receiver,
+                  [firstTokenId, secondTokenId],
+                  [firstTokenValue, secondTokenValue],
+                  '0x',
+                ),
+            ).to.be.revertedWith('ERC1155ReceiverMock: reverting on batch receive');
           });
         });
 
-        context('without a revert string', function () {
-          beforeEach(async function () {
-            this.receiver = await ERC1155ReceiverMock.new(
+        describe('without a revert string', function () {
+          it('reverts', async function () {
+            const receiver = await ethers.deployContract('$ERC1155ReceiverMock', [
               RECEIVER_SINGLE_MAGIC_VALUE,
               RECEIVER_BATCH_MAGIC_VALUE,
               RevertType.RevertWithoutMessage,
-            );
-          });
+            ]);
 
-          it('reverts', async function () {
-            await expectRevertCustomError(
-              this.token.safeBatchTransferFrom(
-                multiTokenHolder,
-                this.receiver.address,
-                [firstTokenId, secondTokenId],
-                [firstTokenValue, secondTokenValue],
-                '0x',
-                { from: multiTokenHolder },
-              ),
-              'ERC1155InvalidReceiver',
-              [this.receiver.address],
-            );
+            await expect(
+              this.token
+                .connect(this.holder)
+                .safeBatchTransferFrom(
+                  this.holder,
+                  receiver,
+                  [firstTokenId, secondTokenId],
+                  [firstTokenValue, secondTokenValue],
+                  '0x',
+                ),
+            )
+              .to.be.revertedWithCustomError(this.token, 'ERC1155InvalidReceiver')
+              .withArgs(receiver.target);
           });
         });
 
-        context('with a custom error', function () {
-          beforeEach(async function () {
-            this.receiver = await ERC1155ReceiverMock.new(
+        describe('with a custom error', function () {
+          it('reverts', async function () {
+            const receiver = await ethers.deployContract('$ERC1155ReceiverMock', [
               RECEIVER_SINGLE_MAGIC_VALUE,
               RECEIVER_BATCH_MAGIC_VALUE,
               RevertType.RevertWithCustomError,
-            );
-          });
+            ]);
 
-          it('reverts', async function () {
-            await expectRevertCustomError(
-              this.token.safeBatchTransferFrom(
-                multiTokenHolder,
-                this.receiver.address,
-                [firstTokenId, secondTokenId],
-                [firstTokenValue, secondTokenValue],
-                '0x',
-                { from: multiTokenHolder },
-              ),
-              'CustomError',
-              [RECEIVER_SINGLE_MAGIC_VALUE],
-            );
+            await expect(
+              this.token
+                .connect(this.holder)
+                .safeBatchTransferFrom(
+                  this.holder,
+                  receiver,
+                  [firstTokenId, secondTokenId],
+                  [firstTokenValue, secondTokenValue],
+                  '0x',
+                ),
+            )
+              .to.be.revertedWithCustomError(receiver, 'CustomError')
+              .withArgs(RECEIVER_SINGLE_MAGIC_VALUE);
           });
         });
 
-        context('with a panic', function () {
-          beforeEach(async function () {
-            this.receiver = await ERC1155ReceiverMock.new(
+        describe('with a panic', function () {
+          it('reverts', async function () {
+            const receiver = await ethers.deployContract('$ERC1155ReceiverMock', [
               RECEIVER_SINGLE_MAGIC_VALUE,
               RECEIVER_BATCH_MAGIC_VALUE,
               RevertType.Panic,
-            );
-          });
+            ]);
 
-          it('reverts', async function () {
-            await expectRevert.unspecified(
-              this.token.safeBatchTransferFrom(
-                multiTokenHolder,
-                this.receiver.address,
-                [firstTokenId, secondTokenId],
-                [firstTokenValue, secondTokenValue],
-                '0x',
-                { from: multiTokenHolder },
-              ),
-            );
+            await expect(
+              this.token
+                .connect(this.holder)
+                .safeBatchTransferFrom(
+                  this.holder,
+                  receiver,
+                  [firstTokenId, secondTokenId],
+                  [firstTokenValue, secondTokenValue],
+                  '0x',
+                ),
+            ).to.be.revertedWithPanic();
           });
         });
       });
 
-      context('to a contract that does not implement the required function', function () {
+      describe('to a contract that does not implement the required function', function () {
         it('reverts', async function () {
-          const invalidReceiver = this.token;
-          await expectRevert.unspecified(
-            this.token.safeBatchTransferFrom(
-              multiTokenHolder,
-              invalidReceiver.address,
-              [firstTokenId, secondTokenId],
-              [firstTokenValue, secondTokenValue],
-              '0x',
-              { from: multiTokenHolder },
-            ),
-          );
+          const invalidReceiver = this.token.target;
+
+          await expect(
+            this.token
+              .connect(this.holder)
+              .safeBatchTransferFrom(
+                this.holder,
+                invalidReceiver,
+                [firstTokenId, secondTokenId],
+                [firstTokenValue, secondTokenValue],
+                '0x',
+              ),
+          )
+            .to.be.revertedWithCustomError(this.token, 'ERC1155InvalidReceiver')
+            .withArgs(invalidReceiver);
         });
       });
     });

+ 96 - 135
test/token/ERC1155/ERC1155.test.js

@@ -1,251 +1,212 @@
-const { BN, constants, expectEvent } = require('@openzeppelin/test-helpers');
-const { ZERO_ADDRESS } = constants;
-
+const { ethers } = require('hardhat');
 const { expect } = require('chai');
-
-const { expectRevertCustomError } = require('../../helpers/customError');
+const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
+const { zip } = require('../../helpers/iterate');
 
 const { shouldBehaveLikeERC1155 } = require('./ERC1155.behavior');
-const ERC1155Mock = artifacts.require('$ERC1155');
 
-contract('ERC1155', function (accounts) {
-  const [operator, tokenHolder, tokenBatchHolder, ...otherAccounts] = accounts;
+const initialURI = 'https://token-cdn-domain/{id}.json';
 
-  const initialURI = 'https://token-cdn-domain/{id}.json';
+async function fixture() {
+  const [operator, holder, ...otherAccounts] = await ethers.getSigners();
+  const token = await ethers.deployContract('$ERC1155', [initialURI]);
+  return { token, operator, holder, otherAccounts };
+}
 
+describe('ERC1155', function () {
   beforeEach(async function () {
-    this.token = await ERC1155Mock.new(initialURI);
+    Object.assign(this, await loadFixture(fixture));
   });
 
-  shouldBehaveLikeERC1155(otherAccounts);
+  shouldBehaveLikeERC1155();
 
   describe('internal functions', function () {
-    const tokenId = new BN(1990);
-    const mintValue = new BN(9001);
-    const burnValue = new BN(3000);
+    const tokenId = 1990n;
+    const mintValue = 9001n;
+    const burnValue = 3000n;
 
-    const tokenBatchIds = [new BN(2000), new BN(2010), new BN(2020)];
-    const mintValues = [new BN(5000), new BN(10000), new BN(42195)];
-    const burnValues = [new BN(5000), new BN(9001), new BN(195)];
+    const tokenBatchIds = [2000n, 2010n, 2020n];
+    const mintValues = [5000n, 10000n, 42195n];
+    const burnValues = [5000n, 9001n, 195n];
 
     const data = '0x12345678';
 
     describe('_mint', function () {
       it('reverts with a zero destination address', async function () {
-        await expectRevertCustomError(
-          this.token.$_mint(ZERO_ADDRESS, tokenId, mintValue, data),
-          'ERC1155InvalidReceiver',
-          [ZERO_ADDRESS],
-        );
+        await expect(this.token.$_mint(ethers.ZeroAddress, tokenId, mintValue, data))
+          .to.be.revertedWithCustomError(this.token, 'ERC1155InvalidReceiver')
+          .withArgs(ethers.ZeroAddress);
       });
 
-      context('with minted tokens', function () {
+      describe('with minted tokens', function () {
         beforeEach(async function () {
-          this.receipt = await this.token.$_mint(tokenHolder, tokenId, mintValue, data, { from: operator });
+          this.tx = await this.token.connect(this.operator).$_mint(this.holder, tokenId, mintValue, data);
         });
 
-        it('emits a TransferSingle event', function () {
-          expectEvent(this.receipt, 'TransferSingle', {
-            operator,
-            from: ZERO_ADDRESS,
-            to: tokenHolder,
-            id: tokenId,
-            value: mintValue,
-          });
+        it('emits a TransferSingle event', async function () {
+          await expect(this.tx)
+            .to.emit(this.token, 'TransferSingle')
+            .withArgs(this.operator.address, ethers.ZeroAddress, this.holder.address, tokenId, mintValue);
         });
 
         it('credits the minted token value', async function () {
-          expect(await this.token.balanceOf(tokenHolder, tokenId)).to.be.bignumber.equal(mintValue);
+          expect(await this.token.balanceOf(this.holder, tokenId)).to.equal(mintValue);
         });
       });
     });
 
     describe('_mintBatch', function () {
       it('reverts with a zero destination address', async function () {
-        await expectRevertCustomError(
-          this.token.$_mintBatch(ZERO_ADDRESS, tokenBatchIds, mintValues, data),
-          'ERC1155InvalidReceiver',
-          [ZERO_ADDRESS],
-        );
+        await expect(this.token.$_mintBatch(ethers.ZeroAddress, tokenBatchIds, mintValues, data))
+          .to.be.revertedWithCustomError(this.token, 'ERC1155InvalidReceiver')
+          .withArgs(ethers.ZeroAddress);
       });
 
       it('reverts if length of inputs do not match', async function () {
-        await expectRevertCustomError(
-          this.token.$_mintBatch(tokenBatchHolder, tokenBatchIds, mintValues.slice(1), data),
-          'ERC1155InvalidArrayLength',
-          [tokenBatchIds.length, mintValues.length - 1],
-        );
-
-        await expectRevertCustomError(
-          this.token.$_mintBatch(tokenBatchHolder, tokenBatchIds.slice(1), mintValues, data),
-          'ERC1155InvalidArrayLength',
-          [tokenBatchIds.length - 1, mintValues.length],
-        );
+        await expect(this.token.$_mintBatch(this.holder, tokenBatchIds, mintValues.slice(1), data))
+          .to.be.revertedWithCustomError(this.token, 'ERC1155InvalidArrayLength')
+          .withArgs(tokenBatchIds.length, mintValues.length - 1);
+
+        await expect(this.token.$_mintBatch(this.holder, tokenBatchIds.slice(1), mintValues, data))
+          .to.be.revertedWithCustomError(this.token, 'ERC1155InvalidArrayLength')
+          .withArgs(tokenBatchIds.length - 1, mintValues.length);
       });
 
-      context('with minted batch of tokens', function () {
+      describe('with minted batch of tokens', function () {
         beforeEach(async function () {
-          this.receipt = await this.token.$_mintBatch(tokenBatchHolder, tokenBatchIds, mintValues, data, {
-            from: operator,
-          });
+          this.tx = await this.token.connect(this.operator).$_mintBatch(this.holder, tokenBatchIds, mintValues, data);
         });
 
-        it('emits a TransferBatch event', function () {
-          expectEvent(this.receipt, 'TransferBatch', {
-            operator,
-            from: ZERO_ADDRESS,
-            to: tokenBatchHolder,
-          });
+        it('emits a TransferBatch event', async function () {
+          await expect(this.tx)
+            .to.emit(this.token, 'TransferBatch')
+            .withArgs(this.operator.address, ethers.ZeroAddress, this.holder.address, tokenBatchIds, mintValues);
         });
 
         it('credits the minted batch of tokens', async function () {
           const holderBatchBalances = await this.token.balanceOfBatch(
-            new Array(tokenBatchIds.length).fill(tokenBatchHolder),
+            tokenBatchIds.map(() => this.holder),
             tokenBatchIds,
           );
 
-          for (let i = 0; i < holderBatchBalances.length; i++) {
-            expect(holderBatchBalances[i]).to.be.bignumber.equal(mintValues[i]);
-          }
+          expect(holderBatchBalances).to.deep.equal(mintValues);
         });
       });
     });
 
     describe('_burn', function () {
       it("reverts when burning the zero account's tokens", async function () {
-        await expectRevertCustomError(this.token.$_burn(ZERO_ADDRESS, tokenId, mintValue), 'ERC1155InvalidSender', [
-          ZERO_ADDRESS,
-        ]);
+        await expect(this.token.$_burn(ethers.ZeroAddress, tokenId, mintValue))
+          .to.be.revertedWithCustomError(this.token, 'ERC1155InvalidSender')
+          .withArgs(ethers.ZeroAddress);
       });
 
       it('reverts when burning a non-existent token id', async function () {
-        await expectRevertCustomError(
-          this.token.$_burn(tokenHolder, tokenId, mintValue),
-          'ERC1155InsufficientBalance',
-          [tokenHolder, 0, mintValue, tokenId],
-        );
+        await expect(this.token.$_burn(this.holder, tokenId, mintValue))
+          .to.be.revertedWithCustomError(this.token, 'ERC1155InsufficientBalance')
+          .withArgs(this.holder.address, 0, mintValue, tokenId);
       });
 
       it('reverts when burning more than available tokens', async function () {
-        await this.token.$_mint(tokenHolder, tokenId, mintValue, data, { from: operator });
+        await this.token.connect(this.operator).$_mint(this.holder, tokenId, mintValue, data);
 
-        await expectRevertCustomError(
-          this.token.$_burn(tokenHolder, tokenId, mintValue.addn(1)),
-          'ERC1155InsufficientBalance',
-          [tokenHolder, mintValue, mintValue.addn(1), tokenId],
-        );
+        await expect(this.token.$_burn(this.holder, tokenId, mintValue + 1n))
+          .to.be.revertedWithCustomError(this.token, 'ERC1155InsufficientBalance')
+          .withArgs(this.holder.address, mintValue, mintValue + 1n, tokenId);
       });
 
-      context('with minted-then-burnt tokens', function () {
+      describe('with minted-then-burnt tokens', function () {
         beforeEach(async function () {
-          await this.token.$_mint(tokenHolder, tokenId, mintValue, data);
-          this.receipt = await this.token.$_burn(tokenHolder, tokenId, burnValue, { from: operator });
+          await this.token.$_mint(this.holder, tokenId, mintValue, data);
+          this.tx = await this.token.connect(this.operator).$_burn(this.holder, tokenId, burnValue);
         });
 
-        it('emits a TransferSingle event', function () {
-          expectEvent(this.receipt, 'TransferSingle', {
-            operator,
-            from: tokenHolder,
-            to: ZERO_ADDRESS,
-            id: tokenId,
-            value: burnValue,
-          });
+        it('emits a TransferSingle event', async function () {
+          await expect(this.tx)
+            .to.emit(this.token, 'TransferSingle')
+            .withArgs(this.operator.address, this.holder.address, ethers.ZeroAddress, tokenId, burnValue);
         });
 
         it('accounts for both minting and burning', async function () {
-          expect(await this.token.balanceOf(tokenHolder, tokenId)).to.be.bignumber.equal(mintValue.sub(burnValue));
+          expect(await this.token.balanceOf(this.holder, tokenId)).to.equal(mintValue - burnValue);
         });
       });
     });
 
     describe('_burnBatch', function () {
       it("reverts when burning the zero account's tokens", async function () {
-        await expectRevertCustomError(
-          this.token.$_burnBatch(ZERO_ADDRESS, tokenBatchIds, burnValues),
-          'ERC1155InvalidSender',
-          [ZERO_ADDRESS],
-        );
+        await expect(this.token.$_burnBatch(ethers.ZeroAddress, tokenBatchIds, burnValues))
+          .to.be.revertedWithCustomError(this.token, 'ERC1155InvalidSender')
+          .withArgs(ethers.ZeroAddress);
       });
 
       it('reverts if length of inputs do not match', async function () {
-        await expectRevertCustomError(
-          this.token.$_burnBatch(tokenBatchHolder, tokenBatchIds, burnValues.slice(1)),
-          'ERC1155InvalidArrayLength',
-          [tokenBatchIds.length, burnValues.length - 1],
-        );
-
-        await expectRevertCustomError(
-          this.token.$_burnBatch(tokenBatchHolder, tokenBatchIds.slice(1), burnValues),
-          'ERC1155InvalidArrayLength',
-          [tokenBatchIds.length - 1, burnValues.length],
-        );
+        await expect(this.token.$_burnBatch(this.holder, tokenBatchIds, burnValues.slice(1)))
+          .to.be.revertedWithCustomError(this.token, 'ERC1155InvalidArrayLength')
+          .withArgs(tokenBatchIds.length, burnValues.length - 1);
+
+        await expect(this.token.$_burnBatch(this.holder, tokenBatchIds.slice(1), burnValues))
+          .to.be.revertedWithCustomError(this.token, 'ERC1155InvalidArrayLength')
+          .withArgs(tokenBatchIds.length - 1, burnValues.length);
       });
 
       it('reverts when burning a non-existent token id', async function () {
-        await expectRevertCustomError(
-          this.token.$_burnBatch(tokenBatchHolder, tokenBatchIds, burnValues),
-          'ERC1155InsufficientBalance',
-          [tokenBatchHolder, 0, tokenBatchIds[0], burnValues[0]],
-        );
+        await expect(this.token.$_burnBatch(this.holder, tokenBatchIds, burnValues))
+          .to.be.revertedWithCustomError(this.token, 'ERC1155InsufficientBalance')
+          .withArgs(this.holder.address, 0, burnValues[0], tokenBatchIds[0]);
       });
 
-      context('with minted-then-burnt tokens', function () {
+      describe('with minted-then-burnt tokens', function () {
         beforeEach(async function () {
-          await this.token.$_mintBatch(tokenBatchHolder, tokenBatchIds, mintValues, data);
-          this.receipt = await this.token.$_burnBatch(tokenBatchHolder, tokenBatchIds, burnValues, { from: operator });
+          await this.token.$_mintBatch(this.holder, tokenBatchIds, mintValues, data);
+          this.tx = await this.token.connect(this.operator).$_burnBatch(this.holder, tokenBatchIds, burnValues);
         });
 
-        it('emits a TransferBatch event', function () {
-          expectEvent(this.receipt, 'TransferBatch', {
-            operator,
-            from: tokenBatchHolder,
-            to: ZERO_ADDRESS,
-            // ids: tokenBatchIds,
-            // values: burnValues,
-          });
+        it('emits a TransferBatch event', async function () {
+          await expect(this.tx)
+            .to.emit(this.token, 'TransferBatch')
+            .withArgs(this.operator.address, this.holder.address, ethers.ZeroAddress, tokenBatchIds, burnValues);
         });
 
         it('accounts for both minting and burning', async function () {
           const holderBatchBalances = await this.token.balanceOfBatch(
-            new Array(tokenBatchIds.length).fill(tokenBatchHolder),
+            tokenBatchIds.map(() => this.holder),
             tokenBatchIds,
           );
 
-          for (let i = 0; i < holderBatchBalances.length; i++) {
-            expect(holderBatchBalances[i]).to.be.bignumber.equal(mintValues[i].sub(burnValues[i]));
-          }
+          expect(holderBatchBalances).to.deep.equal(
+            zip(mintValues, burnValues).map(([mintValue, burnValue]) => mintValue - burnValue),
+          );
         });
       });
     });
   });
 
   describe('ERC1155MetadataURI', function () {
-    const firstTokenID = new BN('42');
-    const secondTokenID = new BN('1337');
+    const firstTokenID = 42n;
+    const secondTokenID = 1337n;
 
     it('emits no URI event in constructor', async function () {
-      await expectEvent.notEmitted.inConstruction(this.token, 'URI');
+      await expect(this.token.deploymentTransaction()).to.not.emit(this.token, 'URI');
     });
 
     it('sets the initial URI for all token types', async function () {
-      expect(await this.token.uri(firstTokenID)).to.be.equal(initialURI);
-      expect(await this.token.uri(secondTokenID)).to.be.equal(initialURI);
+      expect(await this.token.uri(firstTokenID)).to.equal(initialURI);
+      expect(await this.token.uri(secondTokenID)).to.equal(initialURI);
     });
 
     describe('_setURI', function () {
       const newURI = 'https://token-cdn-domain/{locale}/{id}.json';
 
       it('emits no URI event', async function () {
-        const receipt = await this.token.$_setURI(newURI);
-
-        expectEvent.notEmitted(receipt, 'URI');
+        await expect(this.token.$_setURI(newURI)).to.not.emit(this.token, 'URI');
       });
 
       it('sets the new URI for all token types', async function () {
         await this.token.$_setURI(newURI);
 
-        expect(await this.token.uri(firstTokenID)).to.be.equal(newURI);
-        expect(await this.token.uri(secondTokenID)).to.be.equal(newURI);
+        expect(await this.token.uri(firstTokenID)).to.equal(newURI);
+        expect(await this.token.uri(secondTokenID)).to.equal(newURI);
       });
     });
   });

+ 31 - 36
test/token/ERC1155/extensions/ERC1155Burnable.test.js

@@ -1,71 +1,66 @@
-const { BN } = require('@openzeppelin/test-helpers');
-
+const { ethers } = require('hardhat');
 const { expect } = require('chai');
+const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
 
-const { expectRevertCustomError } = require('../../../helpers/customError');
-
-const ERC1155Burnable = artifacts.require('$ERC1155Burnable');
+const ids = [42n, 1137n];
+const values = [3000n, 9902n];
 
-contract('ERC1155Burnable', function (accounts) {
-  const [holder, operator, other] = accounts;
+async function fixture() {
+  const [holder, operator, other] = await ethers.getSigners();
 
-  const uri = 'https://token.com';
+  const token = await ethers.deployContract('$ERC1155Burnable', ['https://token-cdn-domain/{id}.json']);
+  await token.$_mint(holder, ids[0], values[0], '0x');
+  await token.$_mint(holder, ids[1], values[1], '0x');
 
-  const tokenIds = [new BN('42'), new BN('1137')];
-  const values = [new BN('3000'), new BN('9902')];
+  return { token, holder, operator, other };
+}
 
+describe('ERC1155Burnable', function () {
   beforeEach(async function () {
-    this.token = await ERC1155Burnable.new(uri);
-
-    await this.token.$_mint(holder, tokenIds[0], values[0], '0x');
-    await this.token.$_mint(holder, tokenIds[1], values[1], '0x');
+    Object.assign(this, await loadFixture(fixture));
   });
 
   describe('burn', function () {
     it('holder can burn their tokens', async function () {
-      await this.token.burn(holder, tokenIds[0], values[0].subn(1), { from: holder });
+      await this.token.connect(this.holder).burn(this.holder, ids[0], values[0] - 1n);
 
-      expect(await this.token.balanceOf(holder, tokenIds[0])).to.be.bignumber.equal('1');
+      expect(await this.token.balanceOf(this.holder, ids[0])).to.equal(1n);
     });
 
     it("approved operators can burn the holder's tokens", async function () {
-      await this.token.setApprovalForAll(operator, true, { from: holder });
-      await this.token.burn(holder, tokenIds[0], values[0].subn(1), { from: operator });
+      await this.token.connect(this.holder).setApprovalForAll(this.operator, true);
+      await this.token.connect(this.operator).burn(this.holder, ids[0], values[0] - 1n);
 
-      expect(await this.token.balanceOf(holder, tokenIds[0])).to.be.bignumber.equal('1');
+      expect(await this.token.balanceOf(this.holder, ids[0])).to.equal(1n);
     });
 
     it("unapproved accounts cannot burn the holder's tokens", async function () {
-      await expectRevertCustomError(
-        this.token.burn(holder, tokenIds[0], values[0].subn(1), { from: other }),
-        'ERC1155MissingApprovalForAll',
-        [other, holder],
-      );
+      await expect(this.token.connect(this.other).burn(this.holder, ids[0], values[0] - 1n))
+        .to.be.revertedWithCustomError(this.token, 'ERC1155MissingApprovalForAll')
+        .withArgs(this.other.address, this.holder.address);
     });
   });
 
   describe('burnBatch', function () {
     it('holder can burn their tokens', async function () {
-      await this.token.burnBatch(holder, tokenIds, [values[0].subn(1), values[1].subn(2)], { from: holder });
+      await this.token.connect(this.holder).burnBatch(this.holder, ids, [values[0] - 1n, values[1] - 2n]);
 
-      expect(await this.token.balanceOf(holder, tokenIds[0])).to.be.bignumber.equal('1');
-      expect(await this.token.balanceOf(holder, tokenIds[1])).to.be.bignumber.equal('2');
+      expect(await this.token.balanceOf(this.holder, ids[0])).to.equal(1n);
+      expect(await this.token.balanceOf(this.holder, ids[1])).to.equal(2n);
     });
 
     it("approved operators can burn the holder's tokens", async function () {
-      await this.token.setApprovalForAll(operator, true, { from: holder });
-      await this.token.burnBatch(holder, tokenIds, [values[0].subn(1), values[1].subn(2)], { from: operator });
+      await this.token.connect(this.holder).setApprovalForAll(this.operator, true);
+      await this.token.connect(this.operator).burnBatch(this.holder, ids, [values[0] - 1n, values[1] - 2n]);
 
-      expect(await this.token.balanceOf(holder, tokenIds[0])).to.be.bignumber.equal('1');
-      expect(await this.token.balanceOf(holder, tokenIds[1])).to.be.bignumber.equal('2');
+      expect(await this.token.balanceOf(this.holder, ids[0])).to.equal(1n);
+      expect(await this.token.balanceOf(this.holder, ids[1])).to.equal(2n);
     });
 
     it("unapproved accounts cannot burn the holder's tokens", async function () {
-      await expectRevertCustomError(
-        this.token.burnBatch(holder, tokenIds, [values[0].subn(1), values[1].subn(2)], { from: other }),
-        'ERC1155MissingApprovalForAll',
-        [other, holder],
-      );
+      await expect(this.token.connect(this.other).burnBatch(this.holder, ids, [values[0] - 1n, values[1] - 2n]))
+        .to.be.revertedWithCustomError(this.token, 'ERC1155MissingApprovalForAll')
+        .withArgs(this.other.address, this.holder.address);
     });
   });
 });

+ 51 - 59
test/token/ERC1155/extensions/ERC1155Pausable.test.js

@@ -1,112 +1,104 @@
-const { BN } = require('@openzeppelin/test-helpers');
-
+const { ethers } = require('hardhat');
 const { expect } = require('chai');
-const { expectRevertCustomError } = require('../../../helpers/customError');
-
-const ERC1155Pausable = artifacts.require('$ERC1155Pausable');
+const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
 
-contract('ERC1155Pausable', function (accounts) {
-  const [holder, operator, receiver, other] = accounts;
+async function fixture() {
+  const [holder, operator, receiver, other] = await ethers.getSigners();
+  const token = await ethers.deployContract('$ERC1155Pausable', ['https://token-cdn-domain/{id}.json']);
+  return { token, holder, operator, receiver, other };
+}
 
-  const uri = 'https://token.com';
+contract('ERC1155Pausable', function () {
+  const firstTokenId = 37n;
+  const firstTokenValue = 42n;
+  const secondTokenId = 19842n;
+  const secondTokenValue = 23n;
 
   beforeEach(async function () {
-    this.token = await ERC1155Pausable.new(uri);
+    Object.assign(this, await loadFixture(fixture));
   });
 
   context('when token is paused', function () {
-    const firstTokenId = new BN('37');
-    const firstTokenValue = new BN('42');
-
-    const secondTokenId = new BN('19842');
-    const secondTokenValue = new BN('23');
-
     beforeEach(async function () {
-      await this.token.setApprovalForAll(operator, true, { from: holder });
-      await this.token.$_mint(holder, firstTokenId, firstTokenValue, '0x');
-
+      await this.token.connect(this.holder).setApprovalForAll(this.operator, true);
+      await this.token.$_mint(this.holder, firstTokenId, firstTokenValue, '0x');
       await this.token.$_pause();
     });
 
     it('reverts when trying to safeTransferFrom from holder', async function () {
-      await expectRevertCustomError(
-        this.token.safeTransferFrom(holder, receiver, firstTokenId, firstTokenValue, '0x', { from: holder }),
-        'EnforcedPause',
-        [],
-      );
+      await expect(
+        this.token
+          .connect(this.holder)
+          .safeTransferFrom(this.holder, this.receiver, firstTokenId, firstTokenValue, '0x'),
+      ).to.be.revertedWithCustomError(this.token, 'EnforcedPause');
     });
 
     it('reverts when trying to safeTransferFrom from operator', async function () {
-      await expectRevertCustomError(
-        this.token.safeTransferFrom(holder, receiver, firstTokenId, firstTokenValue, '0x', { from: operator }),
-        'EnforcedPause',
-        [],
-      );
+      await expect(
+        this.token
+          .connect(this.operator)
+          .safeTransferFrom(this.holder, this.receiver, firstTokenId, firstTokenValue, '0x'),
+      ).to.be.revertedWithCustomError(this.token, 'EnforcedPause');
     });
 
     it('reverts when trying to safeBatchTransferFrom from holder', async function () {
-      await expectRevertCustomError(
-        this.token.safeBatchTransferFrom(holder, receiver, [firstTokenId], [firstTokenValue], '0x', { from: holder }),
-        'EnforcedPause',
-        [],
-      );
+      await expect(
+        this.token
+          .connect(this.holder)
+          .safeBatchTransferFrom(this.holder, this.receiver, [firstTokenId], [firstTokenValue], '0x'),
+      ).to.be.revertedWithCustomError(this.token, 'EnforcedPause');
     });
 
     it('reverts when trying to safeBatchTransferFrom from operator', async function () {
-      await expectRevertCustomError(
-        this.token.safeBatchTransferFrom(holder, receiver, [firstTokenId], [firstTokenValue], '0x', {
-          from: operator,
-        }),
-        'EnforcedPause',
-        [],
-      );
+      await expect(
+        this.token
+          .connect(this.operator)
+          .safeBatchTransferFrom(this.holder, this.receiver, [firstTokenId], [firstTokenValue], '0x'),
+      ).to.be.revertedWithCustomError(this.token, 'EnforcedPause');
     });
 
     it('reverts when trying to mint', async function () {
-      await expectRevertCustomError(
-        this.token.$_mint(holder, secondTokenId, secondTokenValue, '0x'),
+      await expect(this.token.$_mint(this.holder, secondTokenId, secondTokenValue, '0x')).to.be.revertedWithCustomError(
+        this.token,
         'EnforcedPause',
-        [],
       );
     });
 
     it('reverts when trying to mintBatch', async function () {
-      await expectRevertCustomError(
-        this.token.$_mintBatch(holder, [secondTokenId], [secondTokenValue], '0x'),
-        'EnforcedPause',
-        [],
-      );
+      await expect(
+        this.token.$_mintBatch(this.holder, [secondTokenId], [secondTokenValue], '0x'),
+      ).to.be.revertedWithCustomError(this.token, 'EnforcedPause');
     });
 
     it('reverts when trying to burn', async function () {
-      await expectRevertCustomError(this.token.$_burn(holder, firstTokenId, firstTokenValue), 'EnforcedPause', []);
+      await expect(this.token.$_burn(this.holder, firstTokenId, firstTokenValue)).to.be.revertedWithCustomError(
+        this.token,
+        'EnforcedPause',
+      );
     });
 
     it('reverts when trying to burnBatch', async function () {
-      await expectRevertCustomError(
-        this.token.$_burnBatch(holder, [firstTokenId], [firstTokenValue]),
-        'EnforcedPause',
-        [],
-      );
+      await expect(
+        this.token.$_burnBatch(this.holder, [firstTokenId], [firstTokenValue]),
+      ).to.be.revertedWithCustomError(this.token, 'EnforcedPause');
     });
 
     describe('setApprovalForAll', function () {
       it('approves an operator', async function () {
-        await this.token.setApprovalForAll(other, true, { from: holder });
-        expect(await this.token.isApprovedForAll(holder, other)).to.equal(true);
+        await this.token.connect(this.holder).setApprovalForAll(this.other, true);
+        expect(await this.token.isApprovedForAll(this.holder, this.other)).to.be.true;
       });
     });
 
     describe('balanceOf', function () {
       it('returns the token value owned by the given address', async function () {
-        const balance = await this.token.balanceOf(holder, firstTokenId);
-        expect(balance).to.be.bignumber.equal(firstTokenValue);
+        expect(await this.token.balanceOf(this.holder, firstTokenId)).to.equal(firstTokenValue);
       });
     });
 
     describe('isApprovedForAll', function () {
       it('returns the approval of the operator', async function () {
-        expect(await this.token.isApprovedForAll(holder, operator)).to.equal(true);
+        expect(await this.token.isApprovedForAll(this.holder, this.operator)).to.be.true;
       });
     });
   });

+ 59 - 56
test/token/ERC1155/extensions/ERC1155Supply.test.js

@@ -1,116 +1,119 @@
-const { BN, constants } = require('@openzeppelin/test-helpers');
-
+const { ethers } = require('hardhat');
 const { expect } = require('chai');
+const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
 
-const { ZERO_ADDRESS } = constants;
-
-const ERC1155Supply = artifacts.require('$ERC1155Supply');
-
-contract('ERC1155Supply', function (accounts) {
-  const [holder] = accounts;
-
-  const uri = 'https://token.com';
-
-  const firstTokenId = new BN('37');
-  const firstTokenValue = new BN('42');
+async function fixture() {
+  const [holder] = await ethers.getSigners();
+  const token = await ethers.deployContract('$ERC1155Supply', ['https://token-cdn-domain/{id}.json']);
+  return { token, holder };
+}
 
-  const secondTokenId = new BN('19842');
-  const secondTokenValue = new BN('23');
+describe('ERC1155Supply', function () {
+  const firstTokenId = 37n;
+  const firstTokenValue = 42n;
+  const secondTokenId = 19842n;
+  const secondTokenValue = 23n;
 
   beforeEach(async function () {
-    this.token = await ERC1155Supply.new(uri);
+    Object.assign(this, await loadFixture(fixture));
   });
 
-  context('before mint', function () {
+  describe('before mint', function () {
     it('exist', async function () {
-      expect(await this.token.exists(firstTokenId)).to.be.equal(false);
+      expect(await this.token.exists(firstTokenId)).to.be.false;
     });
 
     it('totalSupply', async function () {
-      expect(await this.token.methods['totalSupply(uint256)'](firstTokenId)).to.be.bignumber.equal('0');
-      expect(await this.token.methods['totalSupply()']()).to.be.bignumber.equal('0');
+      expect(await this.token.totalSupply(ethers.Typed.uint256(firstTokenId))).to.equal(0n);
+      expect(await this.token.totalSupply()).to.equal(0n);
     });
   });
 
-  context('after mint', function () {
-    context('single', function () {
+  describe('after mint', function () {
+    describe('single', function () {
       beforeEach(async function () {
-        await this.token.$_mint(holder, firstTokenId, firstTokenValue, '0x');
+        await this.token.$_mint(this.holder, firstTokenId, firstTokenValue, '0x');
       });
 
       it('exist', async function () {
-        expect(await this.token.exists(firstTokenId)).to.be.equal(true);
+        expect(await this.token.exists(firstTokenId)).to.be.true;
       });
 
       it('totalSupply', async function () {
-        expect(await this.token.methods['totalSupply(uint256)'](firstTokenId)).to.be.bignumber.equal(firstTokenValue);
-        expect(await this.token.methods['totalSupply()']()).to.be.bignumber.equal(firstTokenValue);
+        expect(await this.token.totalSupply(ethers.Typed.uint256(firstTokenId))).to.equal(firstTokenValue);
+        expect(await this.token.totalSupply()).to.equal(firstTokenValue);
       });
     });
 
-    context('batch', function () {
+    describe('batch', function () {
       beforeEach(async function () {
-        await this.token.$_mintBatch(holder, [firstTokenId, secondTokenId], [firstTokenValue, secondTokenValue], '0x');
+        await this.token.$_mintBatch(
+          this.holder,
+          [firstTokenId, secondTokenId],
+          [firstTokenValue, secondTokenValue],
+          '0x',
+        );
       });
 
       it('exist', async function () {
-        expect(await this.token.exists(firstTokenId)).to.be.equal(true);
-        expect(await this.token.exists(secondTokenId)).to.be.equal(true);
+        expect(await this.token.exists(firstTokenId)).to.be.true;
+        expect(await this.token.exists(secondTokenId)).to.be.true;
       });
 
       it('totalSupply', async function () {
-        expect(await this.token.methods['totalSupply(uint256)'](firstTokenId)).to.be.bignumber.equal(firstTokenValue);
-        expect(await this.token.methods['totalSupply(uint256)'](secondTokenId)).to.be.bignumber.equal(secondTokenValue);
-        expect(await this.token.methods['totalSupply()']()).to.be.bignumber.equal(
-          firstTokenValue.add(secondTokenValue),
-        );
+        expect(await this.token.totalSupply(ethers.Typed.uint256(firstTokenId))).to.equal(firstTokenValue);
+        expect(await this.token.totalSupply(ethers.Typed.uint256(secondTokenId))).to.equal(secondTokenValue);
+        expect(await this.token.totalSupply()).to.equal(firstTokenValue + secondTokenValue);
       });
     });
   });
 
-  context('after burn', function () {
-    context('single', function () {
+  describe('after burn', function () {
+    describe('single', function () {
       beforeEach(async function () {
-        await this.token.$_mint(holder, firstTokenId, firstTokenValue, '0x');
-        await this.token.$_burn(holder, firstTokenId, firstTokenValue);
+        await this.token.$_mint(this.holder, firstTokenId, firstTokenValue, '0x');
+        await this.token.$_burn(this.holder, firstTokenId, firstTokenValue);
       });
 
       it('exist', async function () {
-        expect(await this.token.exists(firstTokenId)).to.be.equal(false);
+        expect(await this.token.exists(firstTokenId)).to.be.false;
       });
 
       it('totalSupply', async function () {
-        expect(await this.token.methods['totalSupply(uint256)'](firstTokenId)).to.be.bignumber.equal('0');
-        expect(await this.token.methods['totalSupply()']()).to.be.bignumber.equal('0');
+        expect(await this.token.totalSupply(ethers.Typed.uint256(firstTokenId))).to.equal(0n);
+        expect(await this.token.totalSupply()).to.equal(0n);
       });
     });
 
-    context('batch', function () {
+    describe('batch', function () {
       beforeEach(async function () {
-        await this.token.$_mintBatch(holder, [firstTokenId, secondTokenId], [firstTokenValue, secondTokenValue], '0x');
-        await this.token.$_burnBatch(holder, [firstTokenId, secondTokenId], [firstTokenValue, secondTokenValue]);
+        await this.token.$_mintBatch(
+          this.holder,
+          [firstTokenId, secondTokenId],
+          [firstTokenValue, secondTokenValue],
+          '0x',
+        );
+        await this.token.$_burnBatch(this.holder, [firstTokenId, secondTokenId], [firstTokenValue, secondTokenValue]);
       });
 
       it('exist', async function () {
-        expect(await this.token.exists(firstTokenId)).to.be.equal(false);
-        expect(await this.token.exists(secondTokenId)).to.be.equal(false);
+        expect(await this.token.exists(firstTokenId)).to.be.false;
+        expect(await this.token.exists(secondTokenId)).to.be.false;
       });
 
       it('totalSupply', async function () {
-        expect(await this.token.methods['totalSupply(uint256)'](firstTokenId)).to.be.bignumber.equal('0');
-        expect(await this.token.methods['totalSupply(uint256)'](secondTokenId)).to.be.bignumber.equal('0');
-        expect(await this.token.methods['totalSupply()']()).to.be.bignumber.equal('0');
+        expect(await this.token.totalSupply(ethers.Typed.uint256(firstTokenId))).to.equal(0n);
+        expect(await this.token.totalSupply(ethers.Typed.uint256(secondTokenId))).to.equal(0n);
+        expect(await this.token.totalSupply()).to.equal(0n);
       });
     });
   });
 
-  context('other', function () {
+  describe('other', function () {
     it('supply unaffected by no-op', async function () {
-      this.token.safeTransferFrom(ZERO_ADDRESS, ZERO_ADDRESS, firstTokenId, firstTokenValue, '0x', {
-        from: ZERO_ADDRESS,
-      });
-      expect(await this.token.methods['totalSupply(uint256)'](firstTokenId)).to.be.bignumber.equal('0');
-      expect(await this.token.methods['totalSupply()']()).to.be.bignumber.equal('0');
+      this.token.safeTransferFrom(ethers.ZeroAddress, ethers.ZeroAddress, firstTokenId, firstTokenValue, '0x');
+      expect(await this.token.totalSupply(ethers.Typed.uint256(firstTokenId))).to.equal(0n);
+      expect(await this.token.totalSupply()).to.equal(0n);
     });
   });
 });

+ 37 - 33
test/token/ERC1155/extensions/ERC1155URIStorage.test.js

@@ -1,66 +1,70 @@
-const { BN, expectEvent } = require('@openzeppelin/test-helpers');
-
+const { ethers } = require('hardhat');
 const { expect } = require('chai');
-const { artifacts } = require('hardhat');
+const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
 
-const ERC1155URIStorage = artifacts.require('$ERC1155URIStorage');
+const erc1155Uri = 'https://token.com/nfts/';
+const baseUri = 'https://token.com/';
+const tokenId = 1n;
+const value = 3000n;
 
-contract(['ERC1155URIStorage'], function (accounts) {
-  const [holder] = accounts;
+describe('ERC1155URIStorage', function () {
+  describe('with base uri set', function () {
+    async function fixture() {
+      const [holder] = await ethers.getSigners();
 
-  const erc1155Uri = 'https://token.com/nfts/';
-  const baseUri = 'https://token.com/';
+      const token = await ethers.deployContract('$ERC1155URIStorage', [erc1155Uri]);
+      await token.$_setBaseURI(baseUri);
+      await token.$_mint(holder, tokenId, value, '0x');
 
-  const tokenId = new BN('1');
-  const value = new BN('3000');
+      return { token, holder };
+    }
 
-  describe('with base uri set', function () {
     beforeEach(async function () {
-      this.token = await ERC1155URIStorage.new(erc1155Uri);
-      await this.token.$_setBaseURI(baseUri);
-
-      await this.token.$_mint(holder, tokenId, value, '0x');
+      Object.assign(this, await loadFixture(fixture));
     });
 
     it('can request the token uri, returning the erc1155 uri if no token uri was set', async function () {
-      const receivedTokenUri = await this.token.uri(tokenId);
-
-      expect(receivedTokenUri).to.be.equal(erc1155Uri);
+      expect(await this.token.uri(tokenId)).to.equal(erc1155Uri);
     });
 
     it('can request the token uri, returning the concatenated uri if a token uri was set', async function () {
       const tokenUri = '1234/';
-      const receipt = await this.token.$_setURI(tokenId, tokenUri);
+      const expectedUri = `${baseUri}${tokenUri}`;
 
-      const receivedTokenUri = await this.token.uri(tokenId);
+      await expect(this.token.$_setURI(ethers.Typed.uint256(tokenId), tokenUri))
+        .to.emit(this.token, 'URI')
+        .withArgs(expectedUri, tokenId);
 
-      const expectedUri = `${baseUri}${tokenUri}`;
-      expect(receivedTokenUri).to.be.equal(expectedUri);
-      expectEvent(receipt, 'URI', { value: expectedUri, id: tokenId });
+      expect(await this.token.uri(tokenId)).to.equal(expectedUri);
     });
   });
 
   describe('with base uri set to the empty string', function () {
-    beforeEach(async function () {
-      this.token = await ERC1155URIStorage.new('');
+    async function fixture() {
+      const [holder] = await ethers.getSigners();
+
+      const token = await ethers.deployContract('$ERC1155URIStorage', ['']);
+      await token.$_mint(holder, tokenId, value, '0x');
 
-      await this.token.$_mint(holder, tokenId, value, '0x');
+      return { token, holder };
+    }
+
+    beforeEach(async function () {
+      Object.assign(this, await loadFixture(fixture));
     });
 
     it('can request the token uri, returning an empty string if no token uri was set', async function () {
-      const receivedTokenUri = await this.token.uri(tokenId);
-
-      expect(receivedTokenUri).to.be.equal('');
+      expect(await this.token.uri(tokenId)).to.equal('');
     });
 
     it('can request the token uri, returning the token uri if a token uri was set', async function () {
       const tokenUri = 'ipfs://1234/';
-      const receipt = await this.token.$_setURI(tokenId, tokenUri);
 
-      const receivedTokenUri = await this.token.uri(tokenId);
+      await expect(this.token.$_setURI(ethers.Typed.uint256(tokenId), tokenUri))
+        .to.emit(this.token, 'URI')
+        .withArgs(tokenUri, tokenId);
 
-      expect(receivedTokenUri).to.be.equal(tokenUri);
-      expectEvent(receipt, 'URI', { value: tokenUri, id: tokenId });
+      expect(await this.token.uri(tokenId)).to.equal(tokenUri);
     });
   });
 });

+ 37 - 45
test/token/ERC1155/utils/ERC1155Holder.test.js

@@ -1,64 +1,56 @@
-const { BN } = require('@openzeppelin/test-helpers');
-
-const ERC1155Holder = artifacts.require('$ERC1155Holder');
-const ERC1155 = artifacts.require('$ERC1155');
-
+const { ethers } = require('hardhat');
 const { expect } = require('chai');
+const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
 
 const { shouldSupportInterfaces } = require('../../../utils/introspection/SupportsInterface.behavior');
 
-contract('ERC1155Holder', function (accounts) {
-  const [creator] = accounts;
-  const uri = 'https://token-cdn-domain/{id}.json';
-  const multiTokenIds = [new BN(1), new BN(2), new BN(3)];
-  const multiTokenValues = [new BN(1000), new BN(2000), new BN(3000)];
-  const transferData = '0x12345678';
+const ids = [1n, 2n, 3n];
+const values = [1000n, 2000n, 3000n];
+const data = '0x12345678';
+
+async function fixture() {
+  const [owner] = await ethers.getSigners();
+
+  const token = await ethers.deployContract('$ERC1155', ['https://token-cdn-domain/{id}.json']);
+  const mock = await ethers.deployContract('$ERC1155Holder');
+
+  await token.$_mintBatch(owner, ids, values, '0x');
 
+  return { owner, token, mock };
+}
+
+describe('ERC1155Holder', function () {
   beforeEach(async function () {
-    this.multiToken = await ERC1155.new(uri);
-    this.holder = await ERC1155Holder.new();
-    await this.multiToken.$_mintBatch(creator, multiTokenIds, multiTokenValues, '0x');
+    Object.assign(this, await loadFixture(fixture));
   });
 
   shouldSupportInterfaces(['ERC165', 'ERC1155Receiver']);
 
   it('receives ERC1155 tokens from a single ID', async function () {
-    await this.multiToken.safeTransferFrom(
-      creator,
-      this.holder.address,
-      multiTokenIds[0],
-      multiTokenValues[0],
-      transferData,
-      { from: creator },
-    );
+    await this.token.connect(this.owner).safeTransferFrom(this.owner, this.mock, ids[0], values[0], data);
 
-    expect(await this.multiToken.balanceOf(this.holder.address, multiTokenIds[0])).to.be.bignumber.equal(
-      multiTokenValues[0],
-    );
+    expect(await this.token.balanceOf(this.mock, ids[0])).to.equal(values[0]);
 
-    for (let i = 1; i < multiTokenIds.length; i++) {
-      expect(await this.multiToken.balanceOf(this.holder.address, multiTokenIds[i])).to.be.bignumber.equal(new BN(0));
+    for (let i = 1; i < ids.length; i++) {
+      expect(await this.token.balanceOf(this.mock, ids[i])).to.equal(0n);
     }
   });
 
   it('receives ERC1155 tokens from a multiple IDs', async function () {
-    for (let i = 0; i < multiTokenIds.length; i++) {
-      expect(await this.multiToken.balanceOf(this.holder.address, multiTokenIds[i])).to.be.bignumber.equal(new BN(0));
-    }
-
-    await this.multiToken.safeBatchTransferFrom(
-      creator,
-      this.holder.address,
-      multiTokenIds,
-      multiTokenValues,
-      transferData,
-      { from: creator },
-    );
-
-    for (let i = 0; i < multiTokenIds.length; i++) {
-      expect(await this.multiToken.balanceOf(this.holder.address, multiTokenIds[i])).to.be.bignumber.equal(
-        multiTokenValues[i],
-      );
-    }
+    expect(
+      await this.token.balanceOfBatch(
+        ids.map(() => this.mock),
+        ids,
+      ),
+    ).to.deep.equal(ids.map(() => 0n));
+
+    await this.token.connect(this.owner).safeBatchTransferFrom(this.owner, this.mock, ids, values, data);
+
+    expect(
+      await this.token.balanceOfBatch(
+        ids.map(() => this.mock),
+        ids,
+      ),
+    ).to.deep.equal(values);
   });
 });

+ 1 - 3
test/token/ERC721/ERC721.behavior.js

@@ -5,11 +5,9 @@ const { anyValue } = require('@nomicfoundation/hardhat-chai-matchers/withArgs');
 
 const { shouldSupportInterfaces } = require('../../utils/introspection/SupportsInterface.behavior');
 const {
-  bigint: { Enum },
+  bigint: { RevertType },
 } = require('../../helpers/enums');
 
-const RevertType = Enum('None', 'RevertWithoutMessage', 'RevertWithMessage', 'RevertWithCustomError', 'Panic');
-
 const firstTokenId = 5042n;
 const secondTokenId = 79217n;
 const nonExistentTokenId = 13n;