Ver código fonte

Migrate utils-cryptography to ethers (#4749)

Co-authored-by: Hadrien Croubois <hadrien.croubois@gmail.com>
Co-authored-by: ernestognw <ernestognw@gmail.com>
Renan Souza 1 ano atrás
pai
commit
9702b67ce1

+ 3 - 2
test/helpers/eip712.js

@@ -46,8 +46,9 @@ function domainType(domain) {
 }
 
 function hashTypedData(domain, structHash) {
-  return ethers.keccak256(
-    Buffer.concat(['0x1901', ethers.TypedDataEncoder.hashDomain(domain), structHash].map(ethers.toBeArray)),
+  return ethers.solidityPackedKeccak256(
+    ['bytes', 'bytes32', 'bytes32'],
+    ['0x1901', ethers.TypedDataEncoder.hashDomain(domain), structHash],
   );
 }
 

+ 0 - 63
test/helpers/sign.js

@@ -1,63 +0,0 @@
-function toEthSignedMessageHash(messageHex) {
-  const messageBuffer = Buffer.from(messageHex.substring(2), 'hex');
-  const prefix = Buffer.from(`\u0019Ethereum Signed Message:\n${messageBuffer.length}`);
-  return web3.utils.sha3(Buffer.concat([prefix, messageBuffer]));
-}
-
-/**
- * Create a signed data with intended validator according to the version 0 of EIP-191
- * @param validatorAddress The address of the validator
- * @param dataHex The data to be concatenated with the prefix and signed
- */
-function toDataWithIntendedValidatorHash(validatorAddress, dataHex) {
-  const validatorBuffer = Buffer.from(web3.utils.hexToBytes(validatorAddress));
-  const dataBuffer = Buffer.from(web3.utils.hexToBytes(dataHex));
-  const preambleBuffer = Buffer.from('\x19');
-  const versionBuffer = Buffer.from('\x00');
-  const ethMessage = Buffer.concat([preambleBuffer, versionBuffer, validatorBuffer, dataBuffer]);
-
-  return web3.utils.sha3(ethMessage);
-}
-
-/**
- * Create a signer between a contract and a signer for a voucher of method, args, and redeemer
- * Note that `method` is the web3 method, not the truffle-contract method
- * @param contract TruffleContract
- * @param signer address
- * @param redeemer address
- * @param methodName string
- * @param methodArgs any[]
- */
-const getSignFor =
-  (contract, signer) =>
-  (redeemer, methodName, methodArgs = []) => {
-    const parts = [contract.address, redeemer];
-
-    const REAL_SIGNATURE_SIZE = 2 * 65; // 65 bytes in hexadecimal string length
-    const PADDED_SIGNATURE_SIZE = 2 * 96; // 96 bytes in hexadecimal string length
-    const DUMMY_SIGNATURE = `0x${web3.utils.padLeft('', REAL_SIGNATURE_SIZE)}`;
-
-    // if we have a method, add it to the parts that we're signing
-    if (methodName) {
-      if (methodArgs.length > 0) {
-        parts.push(
-          contract.contract.methods[methodName](...methodArgs.concat([DUMMY_SIGNATURE]))
-            .encodeABI()
-            .slice(0, -1 * PADDED_SIGNATURE_SIZE),
-        );
-      } else {
-        const abi = contract.abi.find(abi => abi.name === methodName);
-        parts.push(abi.signature);
-      }
-    }
-
-    // return the signature of the "Ethereum Signed Message" hash of the hash of `parts`
-    const messageHex = web3.utils.soliditySha3(...parts);
-    return web3.eth.sign(messageHex, signer);
-  };
-
-module.exports = {
-  toEthSignedMessageHash,
-  toDataWithIntendedValidatorHash,
-  getSignFor,
-};

+ 109 - 140
test/utils/cryptography/ECDSA.test.js

@@ -1,104 +1,81 @@
-require('@openzeppelin/test-helpers');
-const { expectRevertCustomError } = require('../../helpers/customError');
-const { toEthSignedMessageHash } = require('../../helpers/sign');
-
+const { ethers } = require('hardhat');
 const { expect } = require('chai');
+const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
 
-const ECDSA = artifacts.require('$ECDSA');
-
-const TEST_MESSAGE = web3.utils.sha3('OpenZeppelin');
-const WRONG_MESSAGE = web3.utils.sha3('Nope');
-const NON_HASH_MESSAGE = '0x' + Buffer.from('abcd').toString('hex');
-
-function to2098Format(signature) {
-  const long = web3.utils.hexToBytes(signature);
-  if (long.length !== 65) {
-    throw new Error('invalid signature length (expected long format)');
-  }
-  if (long[32] >> 7 === 1) {
-    throw new Error("invalid signature 's' value");
-  }
-  const short = long.slice(0, 64);
-  short[32] |= long[64] % 27 << 7; // set the first bit of the 32nd byte to the v parity bit
-  return web3.utils.bytesToHex(short);
-}
+const TEST_MESSAGE = ethers.id('OpenZeppelin');
+const WRONG_MESSAGE = ethers.id('Nope');
+const NON_HASH_MESSAGE = '0xabcd';
 
-function split(signature) {
-  const raw = web3.utils.hexToBytes(signature);
-  switch (raw.length) {
-    case 64:
-      return [
-        web3.utils.bytesToHex(raw.slice(0, 32)), // r
-        web3.utils.bytesToHex(raw.slice(32, 64)), // vs
-      ];
-    case 65:
-      return [
-        raw[64], // v
-        web3.utils.bytesToHex(raw.slice(0, 32)), // r
-        web3.utils.bytesToHex(raw.slice(32, 64)), // s
-      ];
-    default:
-      expect.fail('Invalid signature length, cannot split');
-  }
+function toSignature(signature) {
+  return ethers.Signature.from(signature);
 }
 
-contract('ECDSA', function (accounts) {
-  const [other] = accounts;
+async function fixture() {
+  const [signer] = await ethers.getSigners();
+  const mock = await ethers.deployContract('$ECDSA');
+  return { signer, mock };
+}
 
+describe('ECDSA', function () {
   beforeEach(async function () {
-    this.ecdsa = await ECDSA.new();
+    Object.assign(this, await loadFixture(fixture));
   });
 
-  context('recover with invalid signature', function () {
+  describe('recover with invalid signature', function () {
     it('with short signature', async function () {
-      await expectRevertCustomError(this.ecdsa.$recover(TEST_MESSAGE, '0x1234'), 'ECDSAInvalidSignatureLength', [2]);
+      await expect(this.mock.$recover(TEST_MESSAGE, '0x1234'))
+        .to.be.revertedWithCustomError(this.mock, 'ECDSAInvalidSignatureLength')
+        .withArgs(2);
     });
 
     it('with long signature', async function () {
-      await expectRevertCustomError(
+      await expect(
         // eslint-disable-next-line max-len
-        this.ecdsa.$recover(
+        this.mock.$recover(
           TEST_MESSAGE,
           '0x01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789',
         ),
-        'ECDSAInvalidSignatureLength',
-        [85],
-      );
+      )
+        .to.be.revertedWithCustomError(this.mock, 'ECDSAInvalidSignatureLength')
+        .withArgs(85);
     });
   });
 
-  context('recover with valid signature', function () {
-    context('using web3.eth.sign', function () {
+  describe('recover with valid signature', function () {
+    describe('using <signer>.sign', function () {
       it('returns signer address with correct signature', async function () {
         // Create the signature
-        const signature = await web3.eth.sign(TEST_MESSAGE, other);
+        const signature = await this.signer.signMessage(TEST_MESSAGE);
 
         // Recover the signer address from the generated message and signature.
-        expect(await this.ecdsa.$recover(toEthSignedMessageHash(TEST_MESSAGE), signature)).to.equal(other);
+        expect(await this.mock.$recover(ethers.hashMessage(TEST_MESSAGE), signature)).to.equal(this.signer.address);
       });
 
       it('returns signer address with correct signature for arbitrary length message', async function () {
         // Create the signature
-        const signature = await web3.eth.sign(NON_HASH_MESSAGE, other);
+        const signature = await this.signer.signMessage(NON_HASH_MESSAGE);
 
         // Recover the signer address from the generated message and signature.
-        expect(await this.ecdsa.$recover(toEthSignedMessageHash(NON_HASH_MESSAGE), signature)).to.equal(other);
+        expect(await this.mock.$recover(ethers.hashMessage(NON_HASH_MESSAGE), signature)).to.equal(this.signer.address);
       });
 
       it('returns a different address', async function () {
-        const signature = await web3.eth.sign(TEST_MESSAGE, other);
-        expect(await this.ecdsa.$recover(WRONG_MESSAGE, signature)).to.not.equal(other);
+        const signature = await this.signer.signMessage(TEST_MESSAGE);
+        expect(await this.mock.$recover(WRONG_MESSAGE, signature)).to.not.be.equal(this.signer.address);
       });
 
       it('reverts with invalid signature', async function () {
         // eslint-disable-next-line max-len
         const signature =
           '0x332ce75a821c982f9127538858900d87d3ec1f9f737338ad67cad133fa48feff48e6fa0c18abc62e42820f05943e47af3e9fbe306ce74d64094bdf1691ee53e01c';
-        await expectRevertCustomError(this.ecdsa.$recover(TEST_MESSAGE, signature), 'ECDSAInvalidSignature', []);
+        await expect(this.mock.$recover(TEST_MESSAGE, signature)).to.be.revertedWithCustomError(
+          this.mock,
+          'ECDSAInvalidSignature',
+        );
       });
     });
 
-    context('with v=27 signature', function () {
+    describe('with v=27 signature', function () {
       // Signature generated outside ganache with method web3.eth.sign(signer, message)
       const signer = '0x2cc1166f6212628A0deEf2B33BEFB2187D35b86c';
       // eslint-disable-next-line max-len
@@ -106,124 +83,112 @@ contract('ECDSA', function (accounts) {
         '0x5d99b6f7f6d1f73d1a26497f2b1c89b24c0993913f86e9a2d02cd69887d9c94f3c880358579d811b21dd1b7fd9bb01c1d81d10e69f0384e675c32b39643be892';
 
       it('works with correct v value', async function () {
-        const v = '1b'; // 27 = 1b.
-        const signature = signatureWithoutV + v;
-        expect(await this.ecdsa.$recover(TEST_MESSAGE, signature)).to.equal(signer);
+        const v = '0x1b'; // 27 = 1b.
+        const signature = ethers.concat([signatureWithoutV, v]);
+        expect(await this.mock.$recover(TEST_MESSAGE, signature)).to.equal(signer);
 
-        expect(
-          await this.ecdsa.methods['$recover(bytes32,uint8,bytes32,bytes32)'](TEST_MESSAGE, ...split(signature)),
-        ).to.equal(signer);
+        const { r, s, yParityAndS: vs } = toSignature(signature);
+        expect(await this.mock.getFunction('$recover(bytes32,uint8,bytes32,bytes32)')(TEST_MESSAGE, v, r, s)).to.equal(
+          signer,
+        );
 
-        expect(
-          await this.ecdsa.methods['$recover(bytes32,bytes32,bytes32)'](
-            TEST_MESSAGE,
-            ...split(to2098Format(signature)),
-          ),
-        ).to.equal(signer);
+        expect(await this.mock.getFunction('$recover(bytes32,bytes32,bytes32)')(TEST_MESSAGE, r, vs)).to.equal(signer);
       });
 
       it('rejects incorrect v value', async function () {
-        const v = '1c'; // 28 = 1c.
-        const signature = signatureWithoutV + v;
-        expect(await this.ecdsa.$recover(TEST_MESSAGE, signature)).to.not.equal(signer);
+        const v = '0x1c'; // 28 = 1c.
+        const signature = ethers.concat([signatureWithoutV, v]);
+        expect(await this.mock.$recover(TEST_MESSAGE, signature)).to.not.equal(signer);
 
+        const { r, s, yParityAndS: vs } = toSignature(signature);
         expect(
-          await this.ecdsa.methods['$recover(bytes32,uint8,bytes32,bytes32)'](TEST_MESSAGE, ...split(signature)),
+          await this.mock.getFunction('$recover(bytes32,uint8,bytes32,bytes32)')(TEST_MESSAGE, v, r, s),
         ).to.not.equal(signer);
 
-        expect(
-          await this.ecdsa.methods['$recover(bytes32,bytes32,bytes32)'](
-            TEST_MESSAGE,
-            ...split(to2098Format(signature)),
-          ),
-        ).to.not.equal(signer);
+        expect(await this.mock.getFunction('$recover(bytes32,bytes32,bytes32)')(TEST_MESSAGE, r, vs)).to.not.equal(
+          signer,
+        );
       });
 
       it('reverts wrong v values', async function () {
-        for (const v of ['00', '01']) {
-          const signature = signatureWithoutV + v;
-          await expectRevertCustomError(this.ecdsa.$recover(TEST_MESSAGE, signature), 'ECDSAInvalidSignature', []);
-
-          await expectRevertCustomError(
-            this.ecdsa.methods['$recover(bytes32,uint8,bytes32,bytes32)'](TEST_MESSAGE, ...split(signature)),
+        for (const v of ['0x00', '0x01']) {
+          const signature = ethers.concat([signatureWithoutV, v]);
+          await expect(this.mock.$recover(TEST_MESSAGE, signature)).to.be.revertedWithCustomError(
+            this.mock,
             'ECDSAInvalidSignature',
-            [],
           );
+
+          const { r, s } = toSignature(signature);
+          await expect(
+            this.mock.getFunction('$recover(bytes32,uint8,bytes32,bytes32)')(TEST_MESSAGE, v, r, s),
+          ).to.be.revertedWithCustomError(this.mock, 'ECDSAInvalidSignature');
         }
       });
 
       it('rejects short EIP2098 format', async function () {
-        const v = '1b'; // 27 = 1b.
-        const signature = signatureWithoutV + v;
-        await expectRevertCustomError(
-          this.ecdsa.$recover(TEST_MESSAGE, to2098Format(signature)),
-          'ECDSAInvalidSignatureLength',
-          [64],
-        );
+        const v = '0x1b'; // 27 = 1b.
+        const signature = ethers.concat([signatureWithoutV, v]);
+        await expect(this.mock.$recover(TEST_MESSAGE, toSignature(signature).compactSerialized))
+          .to.be.revertedWithCustomError(this.mock, 'ECDSAInvalidSignatureLength')
+          .withArgs(64);
       });
     });
 
-    context('with v=28 signature', function () {
+    describe('with v=28 signature', function () {
       const signer = '0x1E318623aB09Fe6de3C9b8672098464Aeda9100E';
       // eslint-disable-next-line max-len
       const signatureWithoutV =
         '0x331fe75a821c982f9127538858900d87d3ec1f9f737338ad67cad133fa48feff48e6fa0c18abc62e42820f05943e47af3e9fbe306ce74d64094bdf1691ee53e0';
 
       it('works with correct v value', async function () {
-        const v = '1c'; // 28 = 1c.
-        const signature = signatureWithoutV + v;
-        expect(await this.ecdsa.$recover(TEST_MESSAGE, signature)).to.equal(signer);
+        const v = '0x1c'; // 28 = 1c.
+        const signature = ethers.concat([signatureWithoutV, v]);
+        expect(await this.mock.$recover(TEST_MESSAGE, signature)).to.equal(signer);
 
-        expect(
-          await this.ecdsa.methods['$recover(bytes32,uint8,bytes32,bytes32)'](TEST_MESSAGE, ...split(signature)),
-        ).to.equal(signer);
+        const { r, s, yParityAndS: vs } = toSignature(signature);
+        expect(await this.mock.getFunction('$recover(bytes32,uint8,bytes32,bytes32)')(TEST_MESSAGE, v, r, s)).to.equal(
+          signer,
+        );
 
-        expect(
-          await this.ecdsa.methods['$recover(bytes32,bytes32,bytes32)'](
-            TEST_MESSAGE,
-            ...split(to2098Format(signature)),
-          ),
-        ).to.equal(signer);
+        expect(await this.mock.getFunction('$recover(bytes32,bytes32,bytes32)')(TEST_MESSAGE, r, vs)).to.equal(signer);
       });
 
       it('rejects incorrect v value', async function () {
-        const v = '1b'; // 27 = 1b.
-        const signature = signatureWithoutV + v;
-        expect(await this.ecdsa.$recover(TEST_MESSAGE, signature)).to.not.equal(signer);
+        const v = '0x1b'; // 27 = 1b.
+        const signature = ethers.concat([signatureWithoutV, v]);
+        expect(await this.mock.$recover(TEST_MESSAGE, signature)).to.not.equal(signer);
 
+        const { r, s, yParityAndS: vs } = toSignature(signature);
         expect(
-          await this.ecdsa.methods['$recover(bytes32,uint8,bytes32,bytes32)'](TEST_MESSAGE, ...split(signature)),
+          await this.mock.getFunction('$recover(bytes32,uint8,bytes32,bytes32)')(TEST_MESSAGE, v, r, s),
         ).to.not.equal(signer);
 
-        expect(
-          await this.ecdsa.methods['$recover(bytes32,bytes32,bytes32)'](
-            TEST_MESSAGE,
-            ...split(to2098Format(signature)),
-          ),
-        ).to.not.equal(signer);
+        expect(await this.mock.getFunction('$recover(bytes32,bytes32,bytes32)')(TEST_MESSAGE, r, vs)).to.not.equal(
+          signer,
+        );
       });
 
       it('reverts invalid v values', async function () {
-        for (const v of ['00', '01']) {
-          const signature = signatureWithoutV + v;
-          await expectRevertCustomError(this.ecdsa.$recover(TEST_MESSAGE, signature), 'ECDSAInvalidSignature', []);
-
-          await expectRevertCustomError(
-            this.ecdsa.methods['$recover(bytes32,uint8,bytes32,bytes32)'](TEST_MESSAGE, ...split(signature)),
+        for (const v of ['0x00', '0x01']) {
+          const signature = ethers.concat([signatureWithoutV, v]);
+          await expect(this.mock.$recover(TEST_MESSAGE, signature)).to.be.revertedWithCustomError(
+            this.mock,
             'ECDSAInvalidSignature',
-            [],
           );
+
+          const { r, s } = toSignature(signature);
+          await expect(
+            this.mock.getFunction('$recover(bytes32,uint8,bytes32,bytes32)')(TEST_MESSAGE, v, r, s),
+          ).to.be.revertedWithCustomError(this.mock, 'ECDSAInvalidSignature');
         }
       });
 
       it('rejects short EIP2098 format', async function () {
-        const v = '1c'; // 27 = 1b.
-        const signature = signatureWithoutV + v;
-        await expectRevertCustomError(
-          this.ecdsa.$recover(TEST_MESSAGE, to2098Format(signature)),
-          'ECDSAInvalidSignatureLength',
-          [64],
-        );
+        const v = '0x1c'; // 27 = 1b.
+        const signature = ethers.concat([signatureWithoutV, v]);
+        await expect(this.mock.$recover(TEST_MESSAGE, toSignature(signature).compactSerialized))
+          .to.be.revertedWithCustomError(this.mock, 'ECDSAInvalidSignatureLength')
+          .withArgs(64);
       });
     });
 
@@ -232,14 +197,18 @@ contract('ECDSA', function (accounts) {
       // eslint-disable-next-line max-len
       const highSSignature =
         '0xe742ff452d41413616a5bf43fe15dd88294e983d3d36206c2712f39083d638bde0a0fc89be718fbc1033e1d30d78be1c68081562ed2e97af876f286f3453231d1b';
-      const [r, v, s] = split(highSSignature);
-      await expectRevertCustomError(this.ecdsa.$recover(message, highSSignature), 'ECDSAInvalidSignatureS', [s]);
-      await expectRevertCustomError(
-        this.ecdsa.methods['$recover(bytes32,uint8,bytes32,bytes32)'](TEST_MESSAGE, r, v, s),
-        'ECDSAInvalidSignatureS',
-        [s],
-      );
-      expect(() => to2098Format(highSSignature)).to.throw("invalid signature 's' value");
+
+      const r = ethers.dataSlice(highSSignature, 0, 32);
+      const s = ethers.dataSlice(highSSignature, 32, 64);
+      const v = ethers.dataSlice(highSSignature, 64, 65);
+
+      await expect(this.mock.$recover(message, highSSignature))
+        .to.be.revertedWithCustomError(this.mock, 'ECDSAInvalidSignatureS')
+        .withArgs(s);
+      await expect(this.mock.getFunction('$recover(bytes32,uint8,bytes32,bytes32)')(TEST_MESSAGE, v, r, s))
+        .to.be.revertedWithCustomError(this.mock, 'ECDSAInvalidSignatureS')
+        .withArgs(s);
+      expect(() => toSignature(highSSignature)).to.throw('non-canonical s');
     });
   });
 });

+ 1 - 0
test/utils/cryptography/EIP712.test.js

@@ -1,4 +1,5 @@
 const { ethers } = require('hardhat');
+const { expect } = require('chai');
 const { getDomain, domainType, domainSeparator, hashTypedData } = require('../../helpers/eip712');
 const { getChainId } = require('../../helpers/chainid');
 const { mapValues } = require('../../helpers/iterate');

+ 44 - 31
test/utils/cryptography/MessageHashUtils.test.js

@@ -1,55 +1,68 @@
-require('@openzeppelin/test-helpers');
-const { toEthSignedMessageHash, toDataWithIntendedValidatorHash } = require('../../helpers/sign');
-const { domainSeparator, hashTypedData } = require('../../helpers/eip712');
-
+const { ethers } = require('hardhat');
 const { expect } = require('chai');
+const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
 
-const MessageHashUtils = artifacts.require('$MessageHashUtils');
+const { domainSeparator, hashTypedData } = require('../../helpers/eip712');
 
-contract('MessageHashUtils', function () {
-  beforeEach(async function () {
-    this.messageHashUtils = await MessageHashUtils.new();
+async function fixture() {
+  const mock = await ethers.deployContract('$MessageHashUtils');
+  return { mock };
+}
 
-    this.message = '0x' + Buffer.from('abcd').toString('hex');
-    this.messageHash = web3.utils.sha3(this.message);
-    this.verifyingAddress = web3.utils.toChecksumAddress(web3.utils.randomHex(20));
+describe('MessageHashUtils', function () {
+  beforeEach(async function () {
+    Object.assign(this, await loadFixture(fixture));
   });
 
-  context('toEthSignedMessageHash', function () {
+  describe('toEthSignedMessageHash', function () {
     it('prefixes bytes32 data correctly', async function () {
-      expect(await this.messageHashUtils.methods['$toEthSignedMessageHash(bytes32)'](this.messageHash)).to.equal(
-        toEthSignedMessageHash(this.messageHash),
-      );
+      const message = ethers.randomBytes(32);
+      const expectedHash = ethers.hashMessage(message);
+
+      expect(await this.mock.getFunction('$toEthSignedMessageHash(bytes32)')(message)).to.equal(expectedHash);
     });
 
     it('prefixes dynamic length data correctly', async function () {
-      expect(await this.messageHashUtils.methods['$toEthSignedMessageHash(bytes)'](this.message)).to.equal(
-        toEthSignedMessageHash(this.message),
-      );
+      const message = ethers.randomBytes(128);
+      const expectedHash = ethers.hashMessage(message);
+
+      expect(await this.mock.getFunction('$toEthSignedMessageHash(bytes)')(message)).to.equal(expectedHash);
+    });
+
+    it('version match for bytes32', async function () {
+      const message = ethers.randomBytes(32);
+      const fixed = await this.mock.getFunction('$toEthSignedMessageHash(bytes32)')(message);
+      const dynamic = await this.mock.getFunction('$toEthSignedMessageHash(bytes)')(message);
+
+      expect(fixed).to.equal(dynamic);
     });
   });
 
-  context('toDataWithIntendedValidatorHash', function () {
+  describe('toDataWithIntendedValidatorHash', function () {
     it('returns the digest correctly', async function () {
-      expect(
-        await this.messageHashUtils.$toDataWithIntendedValidatorHash(this.verifyingAddress, this.message),
-      ).to.equal(toDataWithIntendedValidatorHash(this.verifyingAddress, this.message));
+      const verifier = ethers.Wallet.createRandom().address;
+      const message = ethers.randomBytes(128);
+      const expectedHash = ethers.solidityPackedKeccak256(
+        ['string', 'address', 'bytes'],
+        ['\x19\x00', verifier, message],
+      );
+
+      expect(await this.mock.$toDataWithIntendedValidatorHash(verifier, message)).to.equal(expectedHash);
     });
   });
 
-  context('toTypedDataHash', function () {
+  describe('toTypedDataHash', function () {
     it('returns the digest correctly', async function () {
       const domain = {
         name: 'Test',
-        version: 1,
-        chainId: 1,
-        verifyingContract: this.verifyingAddress,
+        version: '1',
+        chainId: 1n,
+        verifyingContract: ethers.Wallet.createRandom().address,
       };
-      const structhash = web3.utils.randomHex(32);
-      const expectedDomainSeparator = await domainSeparator(domain);
-      expect(await this.messageHashUtils.$toTypedDataHash(expectedDomainSeparator, structhash)).to.equal(
-        hashTypedData(domain, structhash),
-      );
+      const structhash = ethers.randomBytes(32);
+      const expectedHash = hashTypedData(domain, structhash);
+
+      expect(await this.mock.$toTypedDataHash(domainSeparator(domain), structhash)).to.equal(expectedHash);
     });
   });
 });

+ 28 - 54
test/utils/cryptography/SignatureChecker.test.js

@@ -1,85 +1,59 @@
-const { toEthSignedMessageHash } = require('../../helpers/sign');
-
+const { ethers } = require('hardhat');
 const { expect } = require('chai');
+const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
+
+const TEST_MESSAGE = ethers.id('OpenZeppelin');
+const TEST_MESSAGE_HASH = ethers.hashMessage(TEST_MESSAGE);
 
-const SignatureChecker = artifacts.require('$SignatureChecker');
-const ERC1271WalletMock = artifacts.require('ERC1271WalletMock');
-const ERC1271MaliciousMock = artifacts.require('ERC1271MaliciousMock');
+const WRONG_MESSAGE = ethers.id('Nope');
+const WRONG_MESSAGE_HASH = ethers.hashMessage(WRONG_MESSAGE);
 
-const TEST_MESSAGE = web3.utils.sha3('OpenZeppelin');
-const WRONG_MESSAGE = web3.utils.sha3('Nope');
+async function fixture() {
+  const [signer, other] = await ethers.getSigners();
+  const mock = await ethers.deployContract('$SignatureChecker');
+  const wallet = await ethers.deployContract('ERC1271WalletMock', [signer]);
+  const malicious = await ethers.deployContract('ERC1271MaliciousMock');
+  const signature = await signer.signMessage(TEST_MESSAGE);
 
-contract('SignatureChecker (ERC1271)', function (accounts) {
-  const [signer, other] = accounts;
+  return { signer, other, mock, wallet, malicious, signature };
+}
 
+describe('SignatureChecker (ERC1271)', function () {
   before('deploying', async function () {
-    this.signaturechecker = await SignatureChecker.new();
-    this.wallet = await ERC1271WalletMock.new(signer);
-    this.malicious = await ERC1271MaliciousMock.new();
-    this.signature = await web3.eth.sign(TEST_MESSAGE, signer);
+    Object.assign(this, await loadFixture(fixture));
   });
 
-  context('EOA account', function () {
+  describe('EOA account', function () {
     it('with matching signer and signature', async function () {
-      expect(
-        await this.signaturechecker.$isValidSignatureNow(signer, toEthSignedMessageHash(TEST_MESSAGE), this.signature),
-      ).to.equal(true);
+      expect(await this.mock.$isValidSignatureNow(this.signer, TEST_MESSAGE_HASH, this.signature)).to.be.true;
     });
 
     it('with invalid signer', async function () {
-      expect(
-        await this.signaturechecker.$isValidSignatureNow(other, toEthSignedMessageHash(TEST_MESSAGE), this.signature),
-      ).to.equal(false);
+      expect(await this.mock.$isValidSignatureNow(this.other, TEST_MESSAGE_HASH, this.signature)).to.be.false;
     });
 
     it('with invalid signature', async function () {
-      expect(
-        await this.signaturechecker.$isValidSignatureNow(signer, toEthSignedMessageHash(WRONG_MESSAGE), this.signature),
-      ).to.equal(false);
+      expect(await this.mock.$isValidSignatureNow(this.signer, WRONG_MESSAGE_HASH, this.signature)).to.be.false;
     });
   });
 
-  context('ERC1271 wallet', function () {
-    for (const signature of ['isValidERC1271SignatureNow', 'isValidSignatureNow']) {
-      context(signature, function () {
+  describe('ERC1271 wallet', function () {
+    for (const fn of ['isValidERC1271SignatureNow', 'isValidSignatureNow']) {
+      describe(fn, function () {
         it('with matching signer and signature', async function () {
-          expect(
-            await this.signaturechecker[`$${signature}`](
-              this.wallet.address,
-              toEthSignedMessageHash(TEST_MESSAGE),
-              this.signature,
-            ),
-          ).to.equal(true);
+          expect(await this.mock.getFunction(`$${fn}`)(this.wallet, TEST_MESSAGE_HASH, this.signature)).to.be.true;
         });
 
         it('with invalid signer', async function () {
-          expect(
-            await this.signaturechecker[`$${signature}`](
-              this.signaturechecker.address,
-              toEthSignedMessageHash(TEST_MESSAGE),
-              this.signature,
-            ),
-          ).to.equal(false);
+          expect(await this.mock.getFunction(`$${fn}`)(this.mock, TEST_MESSAGE_HASH, this.signature)).to.be.false;
         });
 
         it('with invalid signature', async function () {
-          expect(
-            await this.signaturechecker[`$${signature}`](
-              this.wallet.address,
-              toEthSignedMessageHash(WRONG_MESSAGE),
-              this.signature,
-            ),
-          ).to.equal(false);
+          expect(await this.mock.getFunction(`$${fn}`)(this.wallet, WRONG_MESSAGE_HASH, this.signature)).to.be.false;
         });
 
         it('with malicious wallet', async function () {
-          expect(
-            await this.signaturechecker[`$${signature}`](
-              this.malicious.address,
-              toEthSignedMessageHash(TEST_MESSAGE),
-              this.signature,
-            ),
-          ).to.equal(false);
+          expect(await this.mock.getFunction(`$${fn}`)(this.malicious, TEST_MESSAGE_HASH, this.signature)).to.be.false;
         });
       });
     }