Skip to content

Commit bf3d4f3

Browse files
committed
[simplex]: sponsor re-delegation on Alchemy by tightening the paymaster verification gas limit
1 parent b04351b commit bf3d4f3

5 files changed

Lines changed: 48 additions & 8 deletions

File tree

sdk/packages/simplex/src/services/DelegationService.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,14 +165,29 @@ export class DelegationService {
165165
"Setting up EIP-7702 delegation via bundler with paymaster",
166166
)
167167

168+
// Fixed limits for the no-op delegation op — bundler estimation of EIP-7702
169+
// ops is unreliable (Alchemy echoes the input limits rather than simulating).
170+
//
171+
// A FRESH delegation (EOA has no code) burns far more verification gas on
172+
// first-time cold storage, so the proven 150k account + default 200k paymaster
173+
// limits clear rundler's verification-efficiency policy. A RE-delegation
174+
// (EOA already delegated) uses much less (warm slots, paymaster allowance
175+
// reused) — actual ~96k — so those loose limits fall below the 0.4 floor
176+
// (`actual / (accountVerif + paymasterVerif)`). Tighten both verification
177+
// limits for that case so the ratio clears 0.4 while still covering usage.
178+
const code = await this.clientManager.getPublicClient(chain).getCode({
179+
address: this.signer.account.address as HexString,
180+
})
181+
const isFreshEoa = !code || code === "0x"
182+
168183
const result = await this.userOpSender.trySendSponsored({
169184
chain,
170185
callData: "0x" as HexString,
171186
eip7702Auth: authorization,
172-
// Fixed limits for the no-op delegation op. The EOA has no code to
173-
// simulate on a first delegation, and bundler estimation of EIP-7702 ops
174-
// is unreliable (Alchemy echoes the input limits rather than simulating).
175-
gas: { verificationGasLimit: 150_000n, callGasLimit: 50_000n, preVerificationGas: 100_000n },
187+
gas: isFreshEoa
188+
? { verificationGasLimit: 150_000n, callGasLimit: 50_000n, preVerificationGas: 100_000n }
189+
: { verificationGasLimit: 80_000n, callGasLimit: 50_000n, preVerificationGas: 100_000n },
190+
paymasterVerificationGasLimit: isFreshEoa ? undefined : 110_000n,
176191
})
177192

178193
if (result) {

sdk/packages/simplex/src/services/UserOpSender.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,13 @@ export interface SponsoredUserOpRequest {
4040
* simulate so estimation is unreliable. When omitted, limits are estimated.
4141
*/
4242
gas?: UserOpGasLimits
43+
/**
44+
* Override for the Circle paymaster verification gas limit (default 200k).
45+
* Lower it for a known cheap op so rundler's verification-gas-limit efficiency
46+
* policy — which divides actual usage by `accountVerif + paymasterVerif` —
47+
* accepts the op (e.g. re-delegation).
48+
*/
49+
paymasterVerificationGasLimit?: bigint
4350
}
4451

4552
// Generous fallbacks used only when bundler gas estimation fails. The paymaster
@@ -83,7 +90,7 @@ export class UserOpSender {
8390
}
8491

8592
async trySendSponsored(req: SponsoredUserOpRequest): Promise<{ txHash: HexString } | null> {
86-
const { chain, callData, eip7702Auth, nonceKey = 0n, gas } = req
93+
const { chain, callData, eip7702Auth, nonceKey = 0n, gas, paymasterVerificationGasLimit } = req
8794

8895
const entryPoint = this.configService.getEntryPointAddress(chain)
8996
const bundlerUrl = this.configService.getBundlerUrl(chain)
@@ -119,6 +126,7 @@ export class UserOpSender {
119126
walletClient,
120127
signer: this.signer,
121128
configService: this.configService,
129+
paymasterVerificationGasLimit,
122130
})
123131
if (pm.type === "none") {
124132
this.logger.warn({ chain }, "Paymaster data unavailable; caller should fall back to native")

sdk/packages/simplex/src/services/paymaster/index.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export function hasPaymaster(chain: string, configService: FillerConfigService):
2323
* 2. None — returns "0x" (caller falls back to EntryPoint deposit)
2424
*/
2525
export async function buildPaymasterAndData(options: PaymasterOptions): Promise<PaymasterDataResult> {
26-
const { chain, solverAccount, publicClient, signer, configService } = options
26+
const { chain, solverAccount, publicClient, signer, configService, paymasterVerificationGasLimit } = options
2727

2828
const circleAddr = configService.getCirclePaymasterAddress(chain)
2929
if (!circleAddr) {
@@ -42,7 +42,15 @@ export async function buildPaymasterAndData(options: PaymasterOptions): Promise<
4242
return { paymasterAndData: "0x" as HexString, type: "none" }
4343
}
4444

45-
const pm = await buildCirclePaymasterData(publicClient, signer, solverAccount, circleAddr, chain, configService)
45+
const pm = await buildCirclePaymasterData(
46+
publicClient,
47+
signer,
48+
solverAccount,
49+
circleAddr,
50+
chain,
51+
configService,
52+
paymasterVerificationGasLimit,
53+
)
4654
return {
4755
paymasterAndData: packPaymasterAndData(pm),
4856
type: "circle",

sdk/packages/simplex/src/services/paymaster/provider/circle.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ export async function buildCirclePaymasterData(
2424
paymasterAddress: HexString,
2525
chain: string,
2626
configService: FillerConfigService,
27+
/**
28+
* Override for the paymaster verification gas limit. Defaults to the
29+
* Circle-recommended {@link VERIFICATION_GAS_LIMIT_CIRCLE}. A caller can pass a
30+
* tighter value for a known, cheap op (e.g. a re-delegation) so the bundler's
31+
* verification-gas-limit efficiency policy accepts it — see the delegation path.
32+
*/
33+
paymasterVerificationGasLimit: bigint = VERIFICATION_GAS_LIMIT_CIRCLE,
2734
): Promise<PaymasterResult> {
2835
const usdcAddress = configService.getUsdcAsset(chain)
2936
const usdcDecimals = configService.getUsdcDecimals(chain)
@@ -47,7 +54,7 @@ export async function buildCirclePaymasterData(
4754
return {
4855
paymaster: paymasterAddress,
4956
paymasterData,
50-
paymasterVerificationGasLimit: VERIFICATION_GAS_LIMIT_CIRCLE,
57+
paymasterVerificationGasLimit,
5158
paymasterPostOpGasLimit: POST_OP_GAS_LIMIT,
5259
}
5360
}

sdk/packages/simplex/src/services/paymaster/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ export interface PaymasterOptions {
2020
walletClient: WalletClient
2121
signer: { signTypedData: (typedData: unknown, chainId?: number) => Promise<HexString> }
2222
configService: FillerConfigService
23+
/** Override for the Circle paymaster verification gas limit (default 200k). */
24+
paymasterVerificationGasLimit?: bigint
2325
}
2426

2527
export interface PaymasterDataResult {

0 commit comments

Comments
 (0)