diff --git a/src/near-safe.ts b/src/near-safe.ts index 08c4efc..cbda8bb 100644 --- a/src/near-safe.ts +++ b/src/near-safe.ts @@ -377,35 +377,48 @@ export class NearSafe { * Decodes transaction data for a given EVM transaction and extracts relevant details. * * @param {EvmTransactionData} data - The raw transaction data to be decoded. - * @returns {{ chainId: number; costEstimate: string; transactions: MetaTransaction[] }} - An object containing the chain ID, estimated cost, and a list of decoded meta-transactions. + * @returns {DecodedMultisend} - An object containing the chain ID, estimated cost, and a list of decoded meta-transactions. */ decodeTxData(data: EvmTransactionData): DecodedMultisend { - // TODO: data.data may not always parse to UserOperation. We will have to handle the other cases. - const userOp: UserOperation = JSON.parse(data.data); - const { callGasLimit, maxFeePerGas, maxPriorityFeePerGas } = userOp; - const maxGasPrice = BigInt(maxFeePerGas) + BigInt(maxPriorityFeePerGas); - const { args } = decodeFunctionData({ - abi: this.safePack.m4337.abi, - data: userOp.callData, - }); + try { + const userOp: UserOperation = JSON.parse(data.data); + const { callGasLimit, maxFeePerGas, maxPriorityFeePerGas } = userOp; + const maxGasPrice = BigInt(maxFeePerGas) + BigInt(maxPriorityFeePerGas); + const { args } = decodeFunctionData({ + abi: this.safePack.m4337.abi, + data: userOp.callData, + }); - // Determine if singular or double! - const transactions = isMultisendTx(args) - ? decodeMulti(args[2] as string) - : [ - { - to: args[0], - value: args[1], - data: args[2], - operation: args[3], - } as MetaTransaction, - ]; - return { - chainId: data.chainId, - // This is an upper bound on the gas fees (could be lower) - costEstimate: formatEther(BigInt(callGasLimit) * maxGasPrice), - transactions, - }; + // Determine if singular or double! + const transactions = isMultisendTx(args) + ? decodeMulti(args[2] as string) + : [ + { + to: args[0], + value: args[1], + data: args[2], + operation: args[3], + } as MetaTransaction, + ]; + return { + chainId: data.chainId, + // This is an upper bound on the gas fees (could be lower) + costEstimate: formatEther(BigInt(callGasLimit) * maxGasPrice), + transactions, + }; + } catch (error: unknown) { + if (error instanceof SyntaxError) { + return { + chainId: data.chainId, + costEstimate: "0", + transactions: [], + message: data.data, + }; + } else { + const message = error instanceof Error ? error.message : String(error); + throw new Error(`decodeTxData: Unexpected error - ${message}`); + } + } } /** @@ -450,7 +463,8 @@ export class NearSafe { const [messageHash, _] = params as PersonalSignParams; const message = decodeSafeMessage(messageHash, safeInfo); return { - evmMessage: message.safeMessageMessage, + // TODO(bh2smith) this is a bit of a hack. + evmMessage: message.decodedMessage as string, payload: toPayload(message.safeMessageHash), hash: message.safeMessageHash, }; diff --git a/src/types.ts b/src/types.ts index a66c420..3649c0e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -250,6 +250,8 @@ export interface DecodedMultisend { costEstimate: string; /** The list of meta-transactions included in the multisend. */ transactions: MetaTransaction[]; + /** Raw Message to sign if no transactions present. */ + message?: string; } /** diff --git a/tests/e2e.spec.ts b/tests/e2e.spec.ts index 0886461..375e5ab 100644 --- a/tests/e2e.spec.ts +++ b/tests/e2e.spec.ts @@ -68,4 +68,43 @@ describe("Near Safe Requests", () => { }) ).resolves.not.toThrow(); }); + + it("adapter: decodeTxData", async () => { + // setup: + const chainId = 11155111; + const expectedMessage = + "Welcome to OpenSea!\n" + + "\n" + + "Click to sign in and accept the OpenSea Terms of Service (https://opensea.io/tos) and Privacy Policy (https://opensea.io/privacy).\n" + + "\n" + + "This request will not trigger a blockchain transaction or cost any gas fees.\n" + + "\n" + + "Wallet address:\n" + + "0xf057e37024abe7e6bc04fb4f00978613b5ca0241\n" + + "\n" + + "Nonce:\n" + + "aca09a1c-a800-4d71-98ed-547f7c59370c"; + + const signRequest = await adapter.encodeSignRequest({ + method: "personal_sign", + chainId, + params: [ + "0x57656c636f6d6520746f204f70656e536561210a0a436c69636b20746f207369676e20696e20616e642061636365707420746865204f70656e536561205465726d73206f662053657276696365202868747470733a2f2f6f70656e7365612e696f2f746f732920616e64205072697661637920506f6c696379202868747470733a2f2f6f70656e7365612e696f2f70726976616379292e0a0a5468697320726571756573742077696c6c206e6f742074726967676572206120626c6f636b636861696e207472616e73616374696f6e206f7220636f737420616e792067617320666565732e0a0a57616c6c657420616464726573733a0a3078663035376533373032346162653765366263303466623466303039373836313362356361303234310a0a4e6f6e63653a0a61636130396131632d613830302d346437312d393865642d353437663763353933373063", + "0xf057e37024abe7e6bc04fb4f00978613b5ca0241", + ], + }); + + expect(signRequest.evmData).toStrictEqual({ + chainId, + hash: "0xb3a14f9bd21518d7da23dba01ddf7c7ef45795ca1515f1b41b6f3455c862e22d", + data: expectedMessage, + }); + + expect(adapter.decodeTxData(signRequest.evmData)).toStrictEqual({ + chainId: 11155111, + costEstimate: "0", + transactions: [], + message: expectedMessage, + }); + }); });