From 2e0d110e91239ff922062c588f74622de5a2bc92 Mon Sep 17 00:00:00 2001 From: Taylan Pince Date: Fri, 13 Feb 2026 22:21:48 +0100 Subject: [PATCH 1/3] fix(relayer): wrap feeOptions calldata for simulation --- .../relayer/src/relayer/rpc-relayer/index.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/services/relayer/src/relayer/rpc-relayer/index.ts b/packages/services/relayer/src/relayer/rpc-relayer/index.ts index d4cac8e71..89f1e88e0 100644 --- a/packages/services/relayer/src/relayer/rpc-relayer/index.ts +++ b/packages/services/relayer/src/relayer/rpc-relayer/index.ts @@ -137,15 +137,23 @@ export class RpcRelayer implements Relayer { to: Address.Address, calls: Payload.Call[], ): Promise<{ options: FeeOption[]; quote?: FeeQuote }> { + // NOTE: The relayer backend simulates a call to `to` with calldata `data`. + // For undeployed wallets, `to` may be the guest module, so `data` must be valid + // calldata for the guest module (i.e. execute(payload, stubSig)), not the raw + // v3 payload bytes. const callsStruct: Payload.Calls = { type: 'call', space: 0n, nonce: 0n, calls: calls } - const data = Payload.encode(callsStruct) + + const execute = AbiFunction.from('function execute(bytes calldata _payload, bytes calldata _signature)') + const payload = Payload.encode(callsStruct, to) + const stubSignature = '0x0001' + const data = AbiFunction.encodeData(execute, [Bytes.toHex(payload), stubSignature]) try { const result = await this.client.feeOptions( { wallet, to, - data: Bytes.toHex(data), + data, }, { ...(this.projectAccessKey ? { 'X-Access-Key': this.projectAccessKey } : undefined) }, ) From 3eabc06f19fb1a708eb7825b2d23f3f6196a4516 Mon Sep 17 00:00:00 2001 From: Taylan Pince Date: Fri, 13 Feb 2026 23:28:08 +0100 Subject: [PATCH 2/3] fix(relayer): request FeeOptions as wallet (match wallet-webapp-v3) --- .../relayer/src/relayer/rpc-relayer/index.ts | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/packages/services/relayer/src/relayer/rpc-relayer/index.ts b/packages/services/relayer/src/relayer/rpc-relayer/index.ts index 89f1e88e0..7d276423d 100644 --- a/packages/services/relayer/src/relayer/rpc-relayer/index.ts +++ b/packages/services/relayer/src/relayer/rpc-relayer/index.ts @@ -7,7 +7,7 @@ import { TransactionPrecondition, ETHTxnStatus, } from './relayer.gen.js' -import { Address, Hex, Bytes, AbiFunction } from 'ox' +import { Address, Hex } from 'ox' import { Constants, Payload, Network } from '@0xsequence/wallet-primitives' import { FeeOption, FeeQuote, OperationStatus, Relayer } from '../index.js' import { decodePrecondition } from '../../preconditions/index.js' @@ -137,23 +137,21 @@ export class RpcRelayer implements Relayer { to: Address.Address, calls: Payload.Call[], ): Promise<{ options: FeeOption[]; quote?: FeeQuote }> { - // NOTE: The relayer backend simulates a call to `to` with calldata `data`. - // For undeployed wallets, `to` may be the guest module, so `data` must be valid - // calldata for the guest module (i.e. execute(payload, stubSig)), not the raw - // v3 payload bytes. + // IMPORTANT: + // The relayer FeeOptions endpoint simulates `eth_call(to, data)`. + // wallet-webapp-v3 requests FeeOptions with `to = wallet` and `data = Payload.encode(calls, self=wallet)`. + // This works for undeployed wallets and avoids guest-module simulation pitfalls. const callsStruct: Payload.Calls = { type: 'call', space: 0n, nonce: 0n, calls: calls } - const execute = AbiFunction.from('function execute(bytes calldata _payload, bytes calldata _signature)') - const payload = Payload.encode(callsStruct, to) - const stubSignature = '0x0001' - const data = AbiFunction.encodeData(execute, [Bytes.toHex(payload), stubSignature]) + const feeOptionsTo = wallet + const data = Payload.encode(callsStruct, wallet) try { const result = await this.client.feeOptions( { wallet, - to, - data, + to: feeOptionsTo, + data: Hex.fromBytes(data), }, { ...(this.projectAccessKey ? { 'X-Access-Key': this.projectAccessKey } : undefined) }, ) From f8edbf91b37ed5e3c0a10e96c3fc993404886ff9 Mon Sep 17 00:00:00 2001 From: Taylan Pince Date: Fri, 13 Feb 2026 23:50:59 +0100 Subject: [PATCH 3/3] fix(relayer): restore AbiFunction import --- packages/services/relayer/src/relayer/rpc-relayer/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/services/relayer/src/relayer/rpc-relayer/index.ts b/packages/services/relayer/src/relayer/rpc-relayer/index.ts index 7d276423d..221d04496 100644 --- a/packages/services/relayer/src/relayer/rpc-relayer/index.ts +++ b/packages/services/relayer/src/relayer/rpc-relayer/index.ts @@ -7,7 +7,7 @@ import { TransactionPrecondition, ETHTxnStatus, } from './relayer.gen.js' -import { Address, Hex } from 'ox' +import { Address, Hex, AbiFunction } from 'ox' import { Constants, Payload, Network } from '@0xsequence/wallet-primitives' import { FeeOption, FeeQuote, OperationStatus, Relayer } from '../index.js' import { decodePrecondition } from '../../preconditions/index.js'