From 7d7cbc201ab8eecf2d94da96dfed37a010c81c36 Mon Sep 17 00:00:00 2001 From: Michael Tsitrin Date: Mon, 9 Sep 2024 11:52:02 +0300 Subject: [PATCH 1/5] setting cosmos as allowed string for verifyingContract field --- src/wallet.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet.ts b/src/wallet.ts index 45b4951..a5d2a6e 100644 --- a/src/wallet.ts +++ b/src/wallet.ts @@ -500,7 +500,7 @@ WalletMiddlewareOptions): JsonRpcMiddleware { */ function validateVerifyingContract(data: string) { const { domain: { verifyingContract } = {} } = parseTypedMessage(data); - if (verifyingContract && !isValidHexAddress(verifyingContract)) { + if (verifyingContract && !(isValidHexAddress(verifyingContract) || verifyingContract == 'cosmos')) { throw rpcErrors.invalidInput(); } } From 04eda4803afea9f056a62e942fc7d70d8e6975c8 Mon Sep 17 00:00:00 2001 From: Michael Tsitrin Date: Tue, 10 Sep 2024 10:48:48 +0300 Subject: [PATCH 2/5] fixed and linter --- src/wallet.test.ts | 45 +++++++++++++++++++++++++++++++++++++++++++++ src/wallet.ts | 12 +++++++++++- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/src/wallet.test.ts b/src/wallet.test.ts index a58efb8..3482818 100644 --- a/src/wallet.test.ts +++ b/src/wallet.test.ts @@ -478,6 +478,51 @@ describe('wallet', () => { }); }); + it('should not throw if verifyingContract is "cosmos"', async () => { + const { engine } = createTestSetup(); + const getAccounts = async () => testAddresses.slice(); + const witnessedMsgParams: TypedMessageParams[] = []; + const processTypedMessageV3 = async (msgParams: TypedMessageParams) => { + witnessedMsgParams.push(msgParams); + // Assume testMsgSig is the expected signature result + return testMsgSig; + }; + + engine.push(createWalletMiddleware({ getAccounts, processTypedMessageV3 })); + + const message = { + types: { + EIP712Domain: [ + { name: 'name', type: 'string' }, + { name: 'version', type: 'string' }, + { name: 'chainId', type: 'uint256' }, + { name: 'verifyingContract', type: 'address' }, + ], + }, + primaryType: 'EIP712Domain', + message: {}, + domain: { + verifyingContract: 'cosmos', + }, + }; + + const stringifiedMessage = JSON.stringify(message); + + const payload = { + method: 'eth_signTypedData_v3', + params: [testAddresses[0], stringifiedMessage], // Assuming testAddresses[0] is a valid address from your setup + }; + + const promise = pify(engine.handle).call(engine, payload); + const result = await promise; + expect(result).toStrictEqual({ + id: undefined, + jsonrpc: undefined, + result: + '0x68dc980608bceb5f99f691e62c32caccaee05317309015e9454eba1a14c3cd4505d1dd098b8339801239c9bcaac3c4df95569dcf307108b92f68711379be14d81c', + }); + }); + describe('signTypedDataV4', () => { const getMsgParams = (verifyingContract?: string) => ({ types: { diff --git a/src/wallet.ts b/src/wallet.ts index a5d2a6e..3d52d60 100644 --- a/src/wallet.ts +++ b/src/wallet.ts @@ -497,10 +497,20 @@ WalletMiddlewareOptions): JsonRpcMiddleware { * Validates verifyingContract of typedSignMessage. * * @param data - The data passed in typedSign request. + * This function allows the verifyingContract to be either: + * - A valid hex address + * - The string "cosmos" (as it is hard-coded in some Cosmos ecosystem's EVM adapters) + * - An empty string */ function validateVerifyingContract(data: string) { const { domain: { verifyingContract } = {} } = parseTypedMessage(data); - if (verifyingContract && !(isValidHexAddress(verifyingContract) || verifyingContract == 'cosmos')) { + if ( + verifyingContract && + !( + isValidHexAddress(verifyingContract) || + (verifyingContract as any) === 'cosmos' + ) + ) { throw rpcErrors.invalidInput(); } } From 19fa1378e117d39d99e0c56ca863f1034382cfc6 Mon Sep 17 00:00:00 2001 From: Michael Tsitrin Date: Tue, 10 Sep 2024 10:52:47 +0300 Subject: [PATCH 3/5] readability --- src/wallet.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/wallet.ts b/src/wallet.ts index 3d52d60..bd10e16 100644 --- a/src/wallet.ts +++ b/src/wallet.ts @@ -506,10 +506,8 @@ function validateVerifyingContract(data: string) { const { domain: { verifyingContract } = {} } = parseTypedMessage(data); if ( verifyingContract && - !( - isValidHexAddress(verifyingContract) || - (verifyingContract as any) === 'cosmos' - ) + !isValidHexAddress(verifyingContract) && + (verifyingContract as any) !== 'cosmos' ) { throw rpcErrors.invalidInput(); } From 13ec4132060450abae0df99574f79e1404c42d79 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Mon, 23 Sep 2024 15:52:03 +0530 Subject: [PATCH 4/5] Update condition to match main branch --- src/wallet.test.ts | 29 +++++++++++++++++++++++++++++ src/wallet.ts | 6 ++++-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/wallet.test.ts b/src/wallet.test.ts index 3482818..da7a69a 100644 --- a/src/wallet.test.ts +++ b/src/wallet.test.ts @@ -642,6 +642,35 @@ describe('wallet', () => { '0x68dc980608bceb5f99f691e62c32caccaee05317309015e9454eba1a14c3cd4505d1dd098b8339801239c9bcaac3c4df95569dcf307108b92f68711379be14d81c', }); }); + + it('should not throw if request is permit with verifyingContract address equal to "cosmos"', async () => { + const { engine } = createTestSetup(); + const getAccounts = async () => testAddresses.slice(); + const witnessedMsgParams: TypedMessageParams[] = []; + const processTypedMessageV4 = async (msgParams: TypedMessageParams) => { + witnessedMsgParams.push(msgParams); + // Assume testMsgSig is the expected signature result + return testMsgSig; + }; + + engine.push( + createWalletMiddleware({ getAccounts, processTypedMessageV4 }), + ); + + const payload = { + method: 'eth_signTypedData_v4', + params: [testAddresses[0], JSON.stringify(getMsgParams('cosmos'))], + }; + + const promise = pify(engine.handle).call(engine, payload); + const result = await promise; + expect(result).toStrictEqual({ + id: undefined, + jsonrpc: undefined, + result: + '0x68dc980608bceb5f99f691e62c32caccaee05317309015e9454eba1a14c3cd4505d1dd098b8339801239c9bcaac3c4df95569dcf307108b92f68711379be14d81c', + }); + }); }); describe('sign', () => { diff --git a/src/wallet.ts b/src/wallet.ts index bd10e16..445e5e8 100644 --- a/src/wallet.ts +++ b/src/wallet.ts @@ -504,10 +504,12 @@ WalletMiddlewareOptions): JsonRpcMiddleware { */ function validateVerifyingContract(data: string) { const { domain: { verifyingContract } = {} } = parseTypedMessage(data); + // Explicit check for cosmos here has been added to address this issue + // https://github.com/MetaMask/eth-json-rpc-middleware/issues/new if ( verifyingContract && - !isValidHexAddress(verifyingContract) && - (verifyingContract as any) !== 'cosmos' + (verifyingContract as string) !== 'cosmos' && + !isValidHexAddress(verifyingContract) ) { throw rpcErrors.invalidInput(); } From fbf441ffeba266436530acf64a9d80fa352ce918 Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Wed, 2 Oct 2024 10:15:02 -0230 Subject: [PATCH 5/5] Remove duplicate copy of test --- src/wallet.test.ts | 45 --------------------------------------------- 1 file changed, 45 deletions(-) diff --git a/src/wallet.test.ts b/src/wallet.test.ts index da7a69a..27cd1fa 100644 --- a/src/wallet.test.ts +++ b/src/wallet.test.ts @@ -478,51 +478,6 @@ describe('wallet', () => { }); }); - it('should not throw if verifyingContract is "cosmos"', async () => { - const { engine } = createTestSetup(); - const getAccounts = async () => testAddresses.slice(); - const witnessedMsgParams: TypedMessageParams[] = []; - const processTypedMessageV3 = async (msgParams: TypedMessageParams) => { - witnessedMsgParams.push(msgParams); - // Assume testMsgSig is the expected signature result - return testMsgSig; - }; - - engine.push(createWalletMiddleware({ getAccounts, processTypedMessageV3 })); - - const message = { - types: { - EIP712Domain: [ - { name: 'name', type: 'string' }, - { name: 'version', type: 'string' }, - { name: 'chainId', type: 'uint256' }, - { name: 'verifyingContract', type: 'address' }, - ], - }, - primaryType: 'EIP712Domain', - message: {}, - domain: { - verifyingContract: 'cosmos', - }, - }; - - const stringifiedMessage = JSON.stringify(message); - - const payload = { - method: 'eth_signTypedData_v3', - params: [testAddresses[0], stringifiedMessage], // Assuming testAddresses[0] is a valid address from your setup - }; - - const promise = pify(engine.handle).call(engine, payload); - const result = await promise; - expect(result).toStrictEqual({ - id: undefined, - jsonrpc: undefined, - result: - '0x68dc980608bceb5f99f691e62c32caccaee05317309015e9454eba1a14c3cd4505d1dd098b8339801239c9bcaac3c4df95569dcf307108b92f68711379be14d81c', - }); - }); - describe('signTypedDataV4', () => { const getMsgParams = (verifyingContract?: string) => ({ types: {