Skip to content

Commit ea36c54

Browse files
feat: allow overriding callGasLimit for userops (#4948)
1 parent cf37bb5 commit ea36c54

File tree

3 files changed

+55
-23
lines changed

3 files changed

+55
-23
lines changed

.changeset/fast-weeks-appear.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"thirdweb": patch
3+
---
4+
5+
Allow overriding callGasLimit for userops

packages/thirdweb/src/wallets/smart/lib/calls.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,10 @@ export function prepareExecute(args: {
106106
transaction.value || 0n,
107107
transaction.data || "0x",
108108
],
109+
// if gas is specified for the inner tx, use that and add 21k for the execute call on the account contract
110+
// this avoids another estimateGas call when bundling the userOp
111+
// and also allows for passing custom gas limits for the inner tx
112+
gas: transaction.gas ? transaction.gas + 21000n : undefined,
109113
});
110114
}
111115

packages/thirdweb/src/wallets/smart/lib/userop.ts

Lines changed: 46 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ export async function createUnsignedUserOp(args: {
131131
accountContract: ThirdwebContract;
132132
adminAddress: string;
133133
sponsorGas: boolean;
134+
waitForDeployment?: boolean;
134135
overrides?: SmartWalletOptions["overrides"];
135136
}): Promise<UserOperationV06 | UserOperationV07> {
136137
const {
@@ -140,6 +141,7 @@ export async function createUnsignedUserOp(args: {
140141
adminAddress,
141142
overrides,
142143
sponsorGas,
144+
waitForDeployment = true,
143145
} = args;
144146
const chain = executeTx.chain;
145147
const client = executeTx.client;
@@ -154,23 +156,25 @@ export async function createUnsignedUserOp(args: {
154156
args.overrides?.entrypointAddress || ENTRYPOINT_ADDRESS_v0_6,
155157
);
156158

157-
const [isDeployed, callData, gasFees, nonce] = await Promise.all([
158-
isContractDeployed(accountContract),
159-
encode(executeTx),
160-
getGasFees({
161-
executeTx,
162-
bundlerOptions,
163-
chain,
164-
client,
165-
}),
166-
getAccountNonce({
167-
accountContract,
168-
chain,
169-
client,
170-
entrypointAddress: overrides?.entrypointAddress,
171-
getNonceOverride: overrides?.getAccountNonce,
172-
}),
173-
]);
159+
const [isDeployed, callData, callGasLimit, gasFees, nonce] =
160+
await Promise.all([
161+
isContractDeployed(accountContract),
162+
encode(executeTx),
163+
resolvePromisedValue(executeTx.gas),
164+
getGasFees({
165+
executeTx,
166+
bundlerOptions,
167+
chain,
168+
client,
169+
}),
170+
getAccountNonce({
171+
accountContract,
172+
chain,
173+
client,
174+
entrypointAddress: overrides?.entrypointAddress,
175+
getNonceOverride: overrides?.getAccountNonce,
176+
}),
177+
]);
174178

175179
const { maxFeePerGas, maxPriorityFeePerGas } = gasFees;
176180

@@ -185,8 +189,10 @@ export async function createUnsignedUserOp(args: {
185189
isDeployed,
186190
nonce,
187191
callData,
192+
callGasLimit,
188193
maxFeePerGas,
189194
maxPriorityFeePerGas,
195+
waitForDeployment,
190196
});
191197
}
192198

@@ -201,8 +207,10 @@ export async function createUnsignedUserOp(args: {
201207
isDeployed,
202208
nonce,
203209
callData,
210+
callGasLimit,
204211
maxFeePerGas,
205212
maxPriorityFeePerGas,
213+
waitForDeployment,
206214
});
207215
}
208216

@@ -262,8 +270,10 @@ async function populateUserOp_v0_7(args: {
262270
isDeployed: boolean;
263271
nonce: bigint;
264272
callData: Hex;
273+
callGasLimit?: bigint;
265274
maxFeePerGas: bigint;
266275
maxPriorityFeePerGas: bigint;
276+
waitForDeployment: boolean;
267277
}): Promise<UserOperationV07> {
268278
const {
269279
bundlerOptions,
@@ -275,17 +285,21 @@ async function populateUserOp_v0_7(args: {
275285
overrides,
276286
nonce,
277287
callData,
288+
callGasLimit,
278289
maxFeePerGas,
279290
maxPriorityFeePerGas,
291+
waitForDeployment,
280292
} = args;
281293
const { chain, client } = bundlerOptions;
282294

283295
let factory: string | undefined;
284296
let factoryData: Hex;
285-
// lock until account is deployed if needed to avoid 'sender already created' errors when sending multiple transactions in parallel
286297
if (isDeployed || isAccountDeploying(accountContract)) {
287298
factoryData = "0x";
288-
await waitForAccountDeployed(accountContract);
299+
if (waitForDeployment) {
300+
// lock until account is deployed if needed to avoid 'sender already created' errors when sending multiple transactions in parallel
301+
await waitForAccountDeployed(accountContract);
302+
}
289303
} else {
290304
factory = factoryContract.address;
291305
factoryData = await encode(
@@ -305,7 +319,7 @@ async function populateUserOp_v0_7(args: {
305319
callData,
306320
maxFeePerGas,
307321
maxPriorityFeePerGas,
308-
callGasLimit: 0n,
322+
callGasLimit: callGasLimit ?? 0n,
309323
verificationGasLimit: 0n,
310324
preVerificationGas: 0n,
311325
factory,
@@ -399,8 +413,10 @@ async function populateUserOp_v0_6(args: {
399413
isDeployed: boolean;
400414
nonce: bigint;
401415
callData: Hex;
416+
callGasLimit?: bigint;
402417
maxFeePerGas: bigint;
403418
maxPriorityFeePerGas: bigint;
419+
waitForDeployment: boolean;
404420
}): Promise<UserOperationV06> {
405421
const {
406422
bundlerOptions,
@@ -412,16 +428,20 @@ async function populateUserOp_v0_6(args: {
412428
overrides,
413429
nonce,
414430
callData,
431+
callGasLimit,
415432
maxFeePerGas,
416433
maxPriorityFeePerGas,
434+
waitForDeployment,
417435
} = args;
418436
const { chain, client } = bundlerOptions;
419437
let initCode: Hex;
420438

421-
// lock until account is deployed if needed to avoid 'sender already created' errors when sending multiple transactions in parallel
422439
if (isDeployed || isAccountDeploying(accountContract)) {
423440
initCode = "0x";
424-
await waitForAccountDeployed(accountContract);
441+
if (waitForDeployment) {
442+
// lock until account is deployed if needed to avoid 'sender already created' errors when sending multiple transactions in parallel
443+
await waitForAccountDeployed(accountContract);
444+
}
425445
} else {
426446
initCode = await getAccountInitCode({
427447
factoryContract: factoryContract,
@@ -439,7 +459,7 @@ async function populateUserOp_v0_6(args: {
439459
callData,
440460
maxFeePerGas,
441461
maxPriorityFeePerGas,
442-
callGasLimit: 0n,
462+
callGasLimit: callGasLimit ?? 0n,
443463
verificationGasLimit: 0n,
444464
preVerificationGas: 0n,
445465
paymasterAndData: "0x",
@@ -651,6 +671,7 @@ export async function createAndSignUserOp(options: {
651671
adminAccount: Account;
652672
client: ThirdwebClient;
653673
smartWalletOptions: SmartWalletOptions;
674+
waitForDeployment?: boolean;
654675
}) {
655676
const config = options.smartWalletOptions;
656677
const factoryContract = getContract({
@@ -708,6 +729,7 @@ export async function createAndSignUserOp(options: {
708729
adminAddress: options.adminAccount.address,
709730
sponsorGas: "sponsorGas" in config ? config.sponsorGas : config.gasless,
710731
overrides: config.overrides,
732+
waitForDeployment: options.waitForDeployment,
711733
});
712734
const signedUserOp = await signUserOp({
713735
client: options.client,
@@ -723,6 +745,7 @@ async function waitForAccountDeployed(accountContract: ThirdwebContract) {
723745
const startTime = Date.now();
724746
while (isAccountDeploying(accountContract)) {
725747
if (Date.now() - startTime > 60000) {
748+
clearAccountDeploying(accountContract); // clear the flag so it doesnt stay stuck in this state
726749
throw new Error(
727750
"Account deployment is taking too long (over 1 minute). Please try again.",
728751
);

0 commit comments

Comments
 (0)