diff --git a/test/data/confirmations/typed_sign.ts b/test/data/confirmations/typed_sign.ts index f02705a2540b..723ffcf7c2e0 100644 --- a/test/data/confirmations/typed_sign.ts +++ b/test/data/confirmations/typed_sign.ts @@ -163,6 +163,65 @@ export const orderSignatureMsg = { }, }; +export const permitMessageDataJson = { + types: { + EIP712Domain: [ + { + name: 'name', + type: 'string', + }, + { + name: 'version', + type: 'string', + }, + { + name: 'chainId', + type: 'uint256', + }, + { + name: 'verifyingContract', + type: 'address', + }, + ], + Permit: [ + { + name: 'owner', + type: 'address', + }, + { + name: 'spender', + type: 'address', + }, + { + name: 'value', + type: 'uint256', + }, + { + name: 'nonce', + type: 'uint256', + }, + { + name: 'deadline', + type: 'uint256', + }, + ], + }, + primaryType: 'Permit', + domain: { + name: 'MyToken', + version: '1', + verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC', + chainId: 1, + }, + message: { + owner: '0x935e73edb9ff52e23bac7f7e043a1ecd06d05477', + spender: '0x5B38Da6a701c568545dCfcB03FcB875f56beddC4', + value: 3000, + nonce: 0, + deadline: 50000000000, + }, +}; + export const permitSignatureMsg = { id: '0b1787a0-1c44-11ef-b70d-e7064bd7b659', securityAlertResponse: { @@ -174,7 +233,7 @@ export const permitSignatureMsg = { time: 1716826404122, type: TransactionType.signTypedData, msgParams: { - data: '{"types":{"EIP712Domain":[{"name":"name","type":"string"},{"name":"version","type":"string"},{"name":"chainId","type":"uint256"},{"name":"verifyingContract","type":"address"}],"Permit":[{"name":"owner","type":"address"},{"name":"spender","type":"address"},{"name":"value","type":"uint256"},{"name":"nonce","type":"uint256"},{"name":"deadline","type":"uint256"}]},"primaryType":"Permit","domain":{"name":"MyToken","version":"1","verifyingContract":"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC","chainId":1},"message":{"owner":"0x935e73edb9ff52e23bac7f7e043a1ecd06d05477","spender":"0x5B38Da6a701c568545dCfcB03FcB875f56beddC4","value":3000,"nonce":0,"deadline":50000000000}}', + data: JSON.stringify(permitMessageDataJson), from: '0x935e73edb9ff52e23bac7f7e043a1ecd06d05477', version: 'V4', requestId: 14, diff --git a/ui/pages/confirmations/utils/confirm.test.ts b/ui/pages/confirmations/utils/confirm.test.ts index b1f6494ca627..41a4992fa11f 100644 --- a/ui/pages/confirmations/utils/confirm.test.ts +++ b/ui/pages/confirmations/utils/confirm.test.ts @@ -1,9 +1,11 @@ import { ApprovalRequest } from '@metamask/approval-controller'; import { ApprovalType } from '@metamask/controller-utils'; import { TransactionType } from '@metamask/transaction-controller'; +import { cloneDeep } from 'lodash'; import { orderSignatureMsg, + permitMessageDataJson, permitSignatureMsg, unapprovedTypedSignMsgV4, } from '../../../../test/data/confirmations/typed_sign'; @@ -76,6 +78,22 @@ describe('confirm util', () => { ); expect(result).toStrictEqual(true); }); + + it('returns false if it is missing a valid spender address', () => { + const data = cloneDeep(permitMessageDataJson); + data.message.spender = ''; + + const mockMessage = { + ...permitSignatureMsg, + msgParams: { + data: JSON.stringify(data), + }, + } as SignatureRequestType; + + const result = isPermitSignatureRequest(mockMessage); + expect(result).toStrictEqual(false); + }); + it('returns false for request not of type permit signature', () => { const result = isPermitSignatureRequest( unapprovedTypedSignMsgV4 as SignatureRequestType, diff --git a/ui/pages/confirmations/utils/confirm.ts b/ui/pages/confirmations/utils/confirm.ts index 1fa7ff36085e..e23108e7e5f2 100644 --- a/ui/pages/confirmations/utils/confirm.ts +++ b/ui/pages/confirmations/utils/confirm.ts @@ -1,7 +1,7 @@ import { ApprovalRequest } from '@metamask/approval-controller'; import { ApprovalType } from '@metamask/controller-utils'; import { TransactionType } from '@metamask/transaction-controller'; -import { Json } from '@metamask/utils'; +import { isHexString, Json } from '@metamask/utils'; import { PRIMARY_TYPES_ORDER, PRIMARY_TYPES_PERMIT, @@ -77,7 +77,11 @@ export const isOrderSignatureRequest = (request: SignatureRequestType) => { }; /** - * Returns true if the request is a Permit Typed Sign signature request + * Returns true if the request appears to be an EIP-2612 Permit or Permit2 signTypedData signature request. + * Caution: This is a limited check. It may include other Permits which are not the specific types mentioned + * if they match the primaryType check and have a spender field. + * + * TODO: Improve detection of fields. * * @param request - The confirmation request to check */ @@ -91,10 +95,18 @@ export const isPermitSignatureRequest = (request?: Confirmation) => { ) { return false; } - const { primaryType } = parseTypedDataMessage( + + const { + message: { spender }, + primaryType, + } = parseTypedDataMessage( (request as SignatureRequestType).msgParams?.data as string, ); + if (!isHexString(spender)) { + return false; + } + return PRIMARY_TYPES_PERMIT.includes(primaryType); };