diff --git a/test/account/Account.behavior.js b/test/account/Account.behavior.js index 9e000155..f61a45a0 100644 --- a/test/account/Account.behavior.js +++ b/test/account/Account.behavior.js @@ -15,7 +15,7 @@ function shouldBehaveLikeAccountCore() { describe('entryPoint', function () { it('should return the canonical entrypoint', async function () { await this.mock.deploy(); - expect(this.mock.entryPoint()).to.eventually.equal(entrypoint); + await expect(this.mock.entryPoint()).to.eventually.equal(entrypoint); }); }); @@ -106,7 +106,7 @@ function shouldBehaveLikeAccountHolder() { it('receives ERC1155 tokens from a single ID', async function () { await this.token.connect(this.other).safeTransferFrom(this.other, this.mock, ids[0], values[0], data); - expect( + await expect( this.token.balanceOfBatch( ids.map(() => this.mock), ids, @@ -115,7 +115,7 @@ function shouldBehaveLikeAccountHolder() { }); it('receives ERC1155 tokens from a multiple IDs', async function () { - expect( + await expect( this.token.balanceOfBatch( ids.map(() => this.mock), ids, @@ -123,7 +123,7 @@ function shouldBehaveLikeAccountHolder() { ).to.eventually.deep.equal(ids.map(() => 0n)); await this.token.connect(this.other).safeBatchTransferFrom(this.other, this.mock, ids, values, data); - expect( + await expect( this.token.balanceOfBatch( ids.map(() => this.mock), ids, @@ -143,7 +143,7 @@ function shouldBehaveLikeAccountHolder() { it('receives an ERC721 token', async function () { await this.token.connect(this.other).safeTransferFrom(this.other, this.mock, tokenId); - expect(this.token.ownerOf(tokenId)).to.eventually.equal(this.mock); + await expect(this.token.ownerOf(tokenId)).to.eventually.equal(this.mock); }); }); }); @@ -156,7 +156,7 @@ function shouldBehaveLikeAccountERC7821({ deployable = true } = {}) { await setBalance(this.mock.target, ethers.parseEther('1')); // account is not initially deployed - expect(ethers.provider.getCode(this.mock)).to.eventually.equal('0x'); + await expect(ethers.provider.getCode(this.mock)).to.eventually.equal('0x'); this.encodeUserOpCalldata = (...calls) => this.mock.interface.encodeFunctionData('execute', [ @@ -195,13 +195,14 @@ function shouldBehaveLikeAccountERC7821({ deployable = true } = {}) { .then(op => op.addInitCode()) .then(op => this.signUserOp(op)); - expect(this.mock.getNonce()).to.eventually.equal(0); + // Can't call the account to get its nonce before it's deployed + await expect(entrypoint.getNonce(this.mock.target, 0)).to.eventually.equal(0); await expect(entrypoint.handleOps([operation.packed], this.beneficiary)) .to.emit(entrypoint, 'AccountDeployed') .withArgs(operation.hash(), this.mock, this.factory, ethers.ZeroAddress) .to.emit(this.target, 'MockFunctionCalledExtra') .withArgs(this.mock, 17); - expect(this.mock.getNonce()).to.eventually.equal(1); + await expect(this.mock.getNonce()).to.eventually.equal(1); }); it('should revert if the signature is invalid', async function () { @@ -238,11 +239,11 @@ function shouldBehaveLikeAccountERC7821({ deployable = true } = {}) { }) .then(op => this.signUserOp(op)); - expect(this.mock.getNonce()).to.eventually.equal(0); + await expect(this.mock.getNonce()).to.eventually.equal(0); await expect(entrypoint.handleOps([operation.packed], this.beneficiary)) .to.emit(this.target, 'MockFunctionCalledExtra') .withArgs(this.mock, 42); - expect(this.mock.getNonce()).to.eventually.equal(1); + await expect(this.mock.getNonce()).to.eventually.equal(1); }); it('should support sending eth to an EOA', async function () { @@ -250,12 +251,12 @@ function shouldBehaveLikeAccountERC7821({ deployable = true } = {}) { .createUserOp({ callData: this.encodeUserOpCalldata({ target: this.other, value }) }) .then(op => this.signUserOp(op)); - expect(this.mock.getNonce()).to.eventually.equal(0); + await expect(this.mock.getNonce()).to.eventually.equal(0); await expect(entrypoint.handleOps([operation.packed], this.beneficiary)).to.changeEtherBalance( this.other, value, ); - expect(this.mock.getNonce()).to.eventually.equal(1); + await expect(this.mock.getNonce()).to.eventually.equal(1); }); it('should support batch execution', async function () { @@ -275,11 +276,11 @@ function shouldBehaveLikeAccountERC7821({ deployable = true } = {}) { }) .then(op => this.signUserOp(op)); - expect(this.mock.getNonce()).to.eventually.equal(0); + await expect(this.mock.getNonce()).to.eventually.equal(0); const tx = entrypoint.handleOps([operation.packed], this.beneficiary); await expect(tx).to.changeEtherBalances([this.other, this.target], [value1, value2]); await expect(tx).to.emit(this.target, 'MockFunctionCalledExtra').withArgs(this.mock, value2); - expect(this.mock.getNonce()).to.eventually.equal(1); + await expect(this.mock.getNonce()).to.eventually.equal(1); }); }); }); diff --git a/test/crosschain/axelar/AxelarGateway.test.js b/test/crosschain/axelar/AxelarGateway.test.js index 0de9d6ae..b2802f07 100644 --- a/test/crosschain/axelar/AxelarGateway.test.js +++ b/test/crosschain/axelar/AxelarGateway.test.js @@ -32,13 +32,13 @@ describe('AxelarGateway', function () { }); it('initial setup', async function () { - expect(this.srcGateway.localGateway()).to.eventually.equal(this.axelar); - expect(this.srcGateway.getEquivalentChain(this.CAIP2)).to.eventually.equal('local'); - expect(this.srcGateway.getRemoteGateway(this.CAIP2)).to.eventually.equal(getAddress(this.dstGateway)); + await expect(this.srcGateway.localGateway()).to.eventually.equal(this.axelar); + await expect(this.srcGateway.getEquivalentChain(this.CAIP2)).to.eventually.equal('local'); + await expect(this.srcGateway.getRemoteGateway(this.CAIP2)).to.eventually.equal(getAddress(this.dstGateway)); - expect(this.dstGateway.localGateway()).to.eventually.equal(this.axelar); - expect(this.dstGateway.getEquivalentChain(this.CAIP2)).to.eventually.equal('local'); - expect(this.dstGateway.getRemoteGateway(this.CAIP2)).to.eventually.equal(getAddress(this.srcGateway)); + await expect(this.dstGateway.localGateway()).to.eventually.equal(this.axelar); + await expect(this.dstGateway.getEquivalentChain(this.CAIP2)).to.eventually.equal('local'); + await expect(this.dstGateway.getRemoteGateway(this.CAIP2)).to.eventually.equal(getAddress(this.srcGateway)); }); it('workflow', async function () { diff --git a/test/helpers/erc7739.js b/test/helpers/erc7739.js index f27618c3..63b873af 100644 --- a/test/helpers/erc7739.js +++ b/test/helpers/erc7739.js @@ -77,7 +77,7 @@ class TypedDataSignHelper { ethers.concat([ signature, ethers.TypedDataEncoder.hashDomain(domain), // appDomainSeparator - ethers.TypedDataEncoder.hashStruct(this.contentsTypeName, this.allTypes, message.contents), // contentsHash + this.hashStruct(this.contentsTypeName, message.contents), // contentsHash ethers.toUtf8Bytes(this.contentDescr), ethers.toBeHex(this.contentDescr.length, 2), ]), diff --git a/test/utils/cryptography/ERC7739Signer.behavior.js b/test/utils/cryptography/ERC7739Signer.behavior.js index b6b4055c..4923f561 100644 --- a/test/utils/cryptography/ERC7739Signer.behavior.js +++ b/test/utils/cryptography/ERC7739Signer.behavior.js @@ -19,14 +19,14 @@ function shouldBehaveLikeERC7739Signer() { const hash = PersonalSignHelper.hash(text); const signature = await PersonalSignHelper.sign(this.signTypedData, text, this.domain); - expect(this.mock.isValidSignature(hash, signature)).to.eventually.equal(MAGIC_VALUE); + await expect(this.mock.isValidSignature(hash, signature)).to.eventually.equal(MAGIC_VALUE); }); it('returns false for an invalid personal signature', async function () { const hash = PersonalSignHelper.hash('Message the app expects'); const signature = await PersonalSignHelper.sign(this.signTypedData, 'Message signed is different', this.domain); - expect(this.mock.isValidSignature(hash, signature)).to.eventually.not.equal(MAGIC_VALUE); + await expect(this.mock.isValidSignature(hash, signature)).to.eventually.not.equal(MAGIC_VALUE); }); }); @@ -56,7 +56,7 @@ function shouldBehaveLikeERC7739Signer() { const hash = ethers.TypedDataEncoder.hash(this.appDomain, { Permit }, message.contents); const signature = await TypedDataSignHelper.sign(this.signTypedData, this.appDomain, { Permit }, message); - expect(this.mock.isValidSignature(hash, signature)).to.eventually.equal(MAGIC_VALUE); + await expect(this.mock.isValidSignature(hash, signature)).to.eventually.equal(MAGIC_VALUE); }); it('returns true for valid typed data signature (nested types)', async function () { @@ -72,7 +72,7 @@ function shouldBehaveLikeERC7739Signer() { const hash = TypedDataSignHelper.hash(this.appDomain, contentsTypes, message.contents); const signature = await TypedDataSignHelper.sign(this.signTypedData, this.appDomain, contentsTypes, message); - expect(this.mock.isValidSignature(hash, signature)).to.eventually.equal(MAGIC_VALUE); + await expect(this.mock.isValidSignature(hash, signature)).to.eventually.equal(MAGIC_VALUE); }); it('returns false for an invalid typed data signature', async function () { @@ -89,14 +89,13 @@ function shouldBehaveLikeERC7739Signer() { const hash = ethers.TypedDataEncoder.hash(this.appDomain, { Permit }, appContents); const signature = await TypedDataSignHelper.sign(this.signTypedData, this.appDomain, { Permit }, message); - expect(this.mock.isValidSignature(hash, signature)).to.eventually.not.equal(MAGIC_VALUE); + await expect(this.mock.isValidSignature(hash, signature)).to.eventually.not.equal(MAGIC_VALUE); }); }); - it('support detection', function () { - expect( - this.mock.isValidSignature('0x7739773977397739773977397739773977397739773977397739773977397739', ''), - ).to.eventually.equal('0x77390001'); + it('support detection', async function () { + const hash = '0x7739773977397739773977397739773977397739773977397739773977397739'; + await expect(this.mock.isValidSignature(hash, '0x')).to.eventually.equal('0x77390001'); }); }); } diff --git a/test/utils/cryptography/ERC7739Signer.test.js b/test/utils/cryptography/ERC7739Signer.test.js index 8fe0b711..05337e34 100644 --- a/test/utils/cryptography/ERC7739Signer.test.js +++ b/test/utils/cryptography/ERC7739Signer.test.js @@ -1,6 +1,6 @@ const { ethers } = require('hardhat'); const { shouldBehaveLikeERC7739Signer } = require('./ERC7739Signer.behavior'); -const { NonNativeSigner, P256SigningKey, RSASigningKey } = require('../../helpers/signers'); +const { NonNativeSigner, P256SigningKey, RSASHA256SigningKey } = require('../../helpers/signers'); describe('ERC7739Signer', function () { describe('for an ECDSA signer', function () { @@ -26,7 +26,7 @@ describe('ERC7739Signer', function () { describe('for an RSA signer', function () { before(async function () { - this.signer = new NonNativeSigner(RSASigningKey.random()); + this.signer = new NonNativeSigner(RSASHA256SigningKey.random()); this.mock = await ethers.deployContract('ERC7739SignerRSAMock', [ this.signer.signingKey.publicKey.e, this.signer.signingKey.publicKey.n, diff --git a/test/utils/cryptography/ERC7739Utils.test.js b/test/utils/cryptography/ERC7739Utils.test.js index 91c17ee6..cec843f4 100644 --- a/test/utils/cryptography/ERC7739Utils.test.js +++ b/test/utils/cryptography/ERC7739Utils.test.js @@ -2,26 +2,12 @@ const { expect } = require('chai'); const { ethers } = require('hardhat'); const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); -const { Permit, domainType } = require('@openzeppelin/contracts/test/helpers/eip712'); +const { Permit } = require('@openzeppelin/contracts/test/helpers/eip712'); const { PersonalSignHelper, TypedDataSignHelper } = require('../../helpers/erc7739'); // Helper for ERC20Permit applications const helper = TypedDataSignHelper.from({ Permit }); -function domainComponentsType(domain) { - return ethers.TypedDataEncoder.from({ EIP712Domain: domainType(domain) }) - .encodeType('EIP712Domain') - .replace(/EIP712Domain\((.*)\)/, (_, s) => s); -} - -function domainComponentsBytes(domain) { - return ethers.hexlify( - ethers - .getBytes(ethers.TypedDataEncoder.from({ EIP712Domain: domainType(domain) }).encodeData('EIP712Domain', domain)) - .slice(32), - ); -} - const fixture = async () => { const mock = await ethers.deployContract('$ERC7739Utils'); const domain = { @@ -65,9 +51,9 @@ describe('ERC7739Utils', function () { ethers.toBeHex(contentDescr.length, 2), ]); - expect(this.mock.$encodeTypedDataSig(signature, appSeparator, contentsHash, contentDescr)).to.eventually.equal( - encoded, - ); + await expect( + this.mock.$encodeTypedDataSig(signature, appSeparator, contentsHash, contentDescr), + ).to.eventually.equal(encoded); }); }); @@ -85,7 +71,7 @@ describe('ERC7739Utils', function () { ethers.toBeHex(contentDescr.length, 2), ]); - expect(this.mock.$decodeTypedDataSig(encoded)).to.eventually.deep.equal([ + await expect(this.mock.$decodeTypedDataSig(encoded)).to.eventually.deep.equal([ ethers.hexlify(signature), appSeparator, contentsHash, @@ -95,7 +81,7 @@ describe('ERC7739Utils', function () { it('returns default empty values if the signature is too short', async function () { const encoded = ethers.randomBytes(65); // DOMAIN_SEPARATOR (32 bytes) + CONTENTS (32 bytes) + CONTENTS_TYPE_LENGTH (2 bytes) - 1 - expect(this.mock.$decodeTypedDataSig(encoded)).to.eventually.deep.equal([ + await expect(this.mock.$decodeTypedDataSig(encoded)).to.eventually.deep.equal([ '0x', ethers.ZeroHash, ethers.ZeroHash, @@ -105,7 +91,7 @@ describe('ERC7739Utils', function () { it('returns default empty values if the length is invalid', async function () { const encoded = ethers.concat([ethers.randomBytes(64), '0x3f']); // Can't be less than 64 bytes - expect(this.mock.$decodeTypedDataSig(encoded)).to.eventually.deep.equal([ + await expect(this.mock.$decodeTypedDataSig(encoded)).to.eventually.deep.equal([ '0x', ethers.ZeroHash, ethers.ZeroHash, @@ -118,7 +104,7 @@ describe('ERC7739Utils', function () { it('should produce a personal signature EIP-712 nested type', async function () { const text = 'Hello, world!'; - expect(this.mock.$personalSignStructHash(ethers.hashMessage(text))).to.eventually.equal( + await expect(this.mock.$personalSignStructHash(ethers.hashMessage(text))).to.eventually.equal( ethers.TypedDataEncoder.hashStruct('PersonalSign', PersonalSignHelper.types, PersonalSignHelper.prepare(text)), ); }); @@ -131,14 +117,28 @@ describe('ERC7739Utils', function () { const contentsHash = helper.hashStruct('Permit', message.contents); const hash = helper.hashStruct('TypedDataSign', message); - expect( + const domainBytes = ethers.AbiCoder.defaultAbiCoder().encode( + ['bytes32', 'bytes32', 'uint256', 'address', 'bytes32'], + [ + ethers.id(this.domain.name), + ethers.id(this.domain.version), + this.domain.chainId, + this.domain.verifyingContract, + ethers.ZeroHash, + ], + ); + + await expect( this.mock.$typedDataSignStructHash( - helper.contentDescr, + helper.contentsTypeName, + ethers.Typed.string(helper.contentDescr), contentsHash, - domainComponentsType(this.domain), - domainComponentsBytes(this.domain), + domainBytes, ), ).to.eventually.equal(hash); + await expect( + this.mock.$typedDataSignStructHash(helper.contentDescr, contentsHash, domainBytes), + ).to.eventually.equal(hash); }); }); @@ -146,7 +146,7 @@ describe('ERC7739Utils', function () { it('should match', async function () { const typedDataSignType = ethers.TypedDataEncoder.from(helper.allTypes).encodeType('TypedDataSign'); - expect( + await expect( this.mock.$typedDataSignTypehash( helper.contentsTypeName, typedDataSignType.slice(typedDataSignType.indexOf(')') + 1), @@ -156,7 +156,6 @@ describe('ERC7739Utils', function () { }); describe('decodeContentsDescr', function () { - const forbiddenFirstChars = 'abcdefghijklmnopqrstuvwxyz('; const forbiddenChars = ', )\x00'; for (const { descr, contentDescr, contentTypeName, contentType } of [].concat( @@ -181,16 +180,16 @@ describe('ERC7739Utils', function () { contentDescr: 'SomeType', contentTypeName: null, }, - forbiddenFirstChars.split('').map(char => ({ - descr: `should return nothing if starts with [${char}] (implicit)`, - contentDescr: `${char}SomeType()`, + { + descr: 'should return nothing if starts with [(] (implicit)', + contentDescr: '(SomeType(address foo,uint256 bar)', contentTypeName: null, - })), - forbiddenFirstChars.split('').map(char => ({ - descr: `should return nothing if starts with [${char}] (explicit)`, - contentDescr: `${char}SomeType()${char}SomeType`, + }, + { + descr: 'should return nothing if starts with [(] (explicit)', + contentDescr: '(SomeType(address foo,uint256 bar)(SomeType', contentTypeName: null, - })), + }, forbiddenChars.split('').map(char => ({ descr: `should return nothing if contains [${char}] (implicit)`, contentDescr: `SomeType${char}(address foo,uint256 bar)`, @@ -203,7 +202,7 @@ describe('ERC7739Utils', function () { })), )) { it(descr, async function () { - expect(this.mock.$decodeContentsDescr(contentDescr)).to.eventually.deep.equal([ + await expect(this.mock.$decodeContentsDescr(contentDescr)).to.eventually.deep.equal([ contentTypeName ?? '', contentTypeName ? contentType ?? contentDescr : '', ]);