diff --git a/test/helpers/eip712.js b/test/helpers/eip712.js index 0dd78b7e0..f09272b28 100644 --- a/test/helpers/eip712.js +++ b/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], ); } diff --git a/test/helpers/sign.js b/test/helpers/sign.js deleted file mode 100644 index d537116bb..000000000 --- a/test/helpers/sign.js +++ /dev/null @@ -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, -}; diff --git a/test/utils/cryptography/ECDSA.test.js b/test/utils/cryptography/ECDSA.test.js index f164ef196..3c0ce76c6 100644 --- a/test/utils/cryptography/ECDSA.test.js +++ b/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 = ethers.id('OpenZeppelin'); +const WRONG_MESSAGE = ethers.id('Nope'); +const NON_HASH_MESSAGE = '0xabcd'; -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); +function toSignature(signature) { + return ethers.Signature.from(signature); } -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'); - } +async function fixture() { + const [signer] = await ethers.getSigners(); + const mock = await ethers.deployContract('$ECDSA'); + return { signer, mock }; } -contract('ECDSA', function (accounts) { - const [other] = accounts; - +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 .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'); }); }); }); diff --git a/test/utils/cryptography/EIP712.test.js b/test/utils/cryptography/EIP712.test.js index dfad67906..9d0bdae9a 100644 --- a/test/utils/cryptography/EIP712.test.js +++ b/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'); diff --git a/test/utils/cryptography/MessageHashUtils.test.js b/test/utils/cryptography/MessageHashUtils.test.js index b38e945da..f20f5a3ca 100644 --- a/test/utils/cryptography/MessageHashUtils.test.js +++ b/test/utils/cryptography/MessageHashUtils.test.js @@ -1,55 +1,68 @@ -require('@openzeppelin/test-helpers'); -const { toEthSignedMessageHash, toDataWithIntendedValidatorHash } = require('../../helpers/sign'); +const { ethers } = require('hardhat'); +const { expect } = require('chai'); +const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); + const { domainSeparator, hashTypedData } = require('../../helpers/eip712'); -const { expect } = require('chai'); +async function fixture() { + const mock = await ethers.deployContract('$MessageHashUtils'); + return { mock }; +} -const MessageHashUtils = artifacts.require('$MessageHashUtils'); - -contract('MessageHashUtils', function () { +describe('MessageHashUtils', function () { beforeEach(async function () { - this.messageHashUtils = await MessageHashUtils.new(); - - this.message = '0x' + Buffer.from('abcd').toString('hex'); - this.messageHash = web3.utils.sha3(this.message); - this.verifyingAddress = web3.utils.toChecksumAddress(web3.utils.randomHex(20)); + 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); }); }); }); diff --git a/test/utils/cryptography/SignatureChecker.test.js b/test/utils/cryptography/SignatureChecker.test.js index ba8b100d1..e6a08491a 100644 --- a/test/utils/cryptography/SignatureChecker.test.js +++ b/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 SignatureChecker = artifacts.require('$SignatureChecker'); -const ERC1271WalletMock = artifacts.require('ERC1271WalletMock'); -const ERC1271MaliciousMock = artifacts.require('ERC1271MaliciousMock'); +const TEST_MESSAGE = ethers.id('OpenZeppelin'); +const TEST_MESSAGE_HASH = ethers.hashMessage(TEST_MESSAGE); -const TEST_MESSAGE = web3.utils.sha3('OpenZeppelin'); -const WRONG_MESSAGE = web3.utils.sha3('Nope'); +const WRONG_MESSAGE = ethers.id('Nope'); +const WRONG_MESSAGE_HASH = ethers.hashMessage(WRONG_MESSAGE); -contract('SignatureChecker (ERC1271)', function (accounts) { - const [signer, other] = accounts; +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); + 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; }); }); }