Skip to content

Commit 0e479e2

Browse files
fix: Added 50k overhead to userOps estimation (#2677)
Co-authored-by: Joaquim Verges <joaquim.verges@gmail.com>
1 parent c385848 commit 0e479e2

34 files changed

+1069
-1024
lines changed

.changeset/unlucky-feet-learn.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@thirdweb-dev/wallets": patch
3+
"thirdweb": patch
4+
---
5+
6+
added 50k overhead to userOp gas estimations

legacy_packages/wallets/src/evm/connectors/smart-wallet/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export class SmartWalletConnector extends Connector<SmartWalletConnectionArgs> {
5252
this.config.bundlerUrl || `https://${this.chainId}.bundler.thirdweb.com`;
5353
const paymasterUrl =
5454
this.config.paymasterUrl ||
55-
`https://${this.chainId}.bundler.thirdweb.com/v2`;
55+
`https://${this.chainId}.bundler.thirdweb.com`;
5656
const entryPointAddress = config.entryPointAddress || ENTRYPOINT_ADDRESS;
5757
const localSigner = await params.personalWallet.getSigner();
5858
const providerConfig: ProviderConfig = {

legacy_packages/wallets/src/evm/connectors/smart-wallet/lib/base-api.ts

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -261,13 +261,9 @@ export abstract class BaseAccountAPI {
261261
} catch (error: any) {
262262
throw this.unwrapBundlerError(error);
263263
}
264-
partialOp.callGasLimit = BigNumber.from(estimates.callGasLimit);
265-
partialOp.verificationGasLimit = BigNumber.from(
266-
estimates.verificationGasLimit,
267-
);
268-
partialOp.preVerificationGas = BigNumber.from(
269-
estimates.preVerificationGas,
270-
);
264+
partialOp.callGasLimit = estimates.callGasLimit;
265+
partialOp.verificationGasLimit = estimates.verificationGasLimit;
266+
partialOp.preVerificationGas = estimates.preVerificationGas;
271267
} else if (gasless) {
272268
const paymasterResult =
273269
await this.paymasterAPI.getPaymasterAndData(partialOp);
@@ -296,13 +292,9 @@ export abstract class BaseAccountAPI {
296292
} catch (error: any) {
297293
throw this.unwrapBundlerError(error);
298294
}
299-
partialOp.callGasLimit = BigNumber.from(estimates.callGasLimit);
300-
partialOp.verificationGasLimit = BigNumber.from(
301-
estimates.verificationGasLimit,
302-
);
303-
partialOp.preVerificationGas = BigNumber.from(
304-
estimates.preVerificationGas,
305-
);
295+
partialOp.callGasLimit = estimates.callGasLimit;
296+
partialOp.verificationGasLimit = estimates.verificationGasLimit;
297+
partialOp.preVerificationGas = estimates.preVerificationGas;
306298
// need paymaster to re-sign after estimates
307299
if (paymasterAndData && paymasterAndData !== "0x") {
308300
const paymasterResult2 =
@@ -324,13 +316,9 @@ export abstract class BaseAccountAPI {
324316
throw this.unwrapBundlerError(error);
325317
}
326318

327-
partialOp.callGasLimit = BigNumber.from(estimates.callGasLimit);
328-
partialOp.verificationGasLimit = BigNumber.from(
329-
estimates.verificationGasLimit,
330-
);
331-
partialOp.preVerificationGas = BigNumber.from(
332-
estimates.preVerificationGas,
333-
);
319+
partialOp.callGasLimit = estimates.callGasLimit;
320+
partialOp.verificationGasLimit = estimates.verificationGasLimit;
321+
partialOp.preVerificationGas = estimates.preVerificationGas;
334322
}
335323

336324
return {

legacy_packages/wallets/src/evm/connectors/smart-wallet/lib/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export const ENTRYPOINT_ADDRESS = "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"; // v0.6
22
export const ERC6551_REGISTRY = "0x02101dfB77FDE026414827Fdc604ddAF224F0921";
3+
export const MANAGED_ACCOUNT_GAS_BUFFER = 50000;
34

45
export const ACCOUNT_CORE_ABI = [
56
{

legacy_packages/wallets/src/evm/connectors/smart-wallet/lib/http-rpc-client.ts

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
import { providers, utils } from "ethers";
1+
import { BigNumber, providers, utils } from "ethers";
22
import { UserOperationStruct } from "@account-abstraction/contracts";
33
import { isTwUrl } from "../../../utils/url";
44
import { hexlifyUserOp } from "./utils";
55
import { setAnalyticsHeaders } from "../../../utils/headers";
6+
import { EstimateUserOpGasRawResult, EstimateUserOpGasResult } from "../types";
7+
import { MANAGED_ACCOUNT_GAS_BUFFER } from "./constants";
68

79
export const DEBUG = false; // TODO set as public flag
810

@@ -112,14 +114,11 @@ export class HttpRpcClient {
112114
]);
113115
}
114116

115-
async estimateUserOpGas(userOp1: Partial<UserOperationStruct>): Promise<{
116-
preVerificationGas: string;
117-
verificationGas: string;
118-
verificationGasLimit: string;
119-
callGasLimit: string;
120-
}> {
117+
async estimateUserOpGas(
118+
userOp: Partial<UserOperationStruct>,
119+
): Promise<EstimateUserOpGasResult> {
121120
await this.initializing;
122-
const hexifiedUserOp = await hexlifyUserOp(userOp1);
121+
const hexifiedUserOp = await hexlifyUserOp(userOp);
123122
const jsonRequestData: [UserOperationStruct, string] = [
124123
hexifiedUserOp,
125124
this.entryPointAddress,
@@ -128,10 +127,20 @@ export class HttpRpcClient {
128127
"eth_estimateUserOperationGas",
129128
jsonRequestData,
130129
);
131-
return await this.userOpJsonRpcProvider.send(
132-
"eth_estimateUserOperationGas",
133-
[hexifiedUserOp, this.entryPointAddress],
134-
);
130+
const data: EstimateUserOpGasRawResult =
131+
await this.userOpJsonRpcProvider.send("eth_estimateUserOperationGas", [
132+
hexifiedUserOp,
133+
this.entryPointAddress,
134+
]);
135+
// adds gas buffer to callGasLimit to account for ManagedAccountFactory delegate calls
136+
return {
137+
preVerificationGas: BigNumber.from(data.preVerificationGas),
138+
verificationGas: BigNumber.from(data.verificationGas),
139+
verificationGasLimit: BigNumber.from(data.verificationGasLimit),
140+
callGasLimit: BigNumber.from(data.callGasLimit).add(
141+
MANAGED_ACCOUNT_GAS_BUFFER,
142+
),
143+
};
135144
}
136145

137146
async getUserOperationGasPrice(): Promise<{

legacy_packages/wallets/src/evm/connectors/smart-wallet/types.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,3 +157,17 @@ export interface UserOpResult {
157157
transactionHash: string;
158158
success: boolean;
159159
}
160+
161+
export type EstimateUserOpGasRawResult = {
162+
preVerificationGas: string;
163+
verificationGas: string;
164+
verificationGasLimit: string;
165+
callGasLimit: string;
166+
};
167+
168+
export type EstimateUserOpGasResult = {
169+
preVerificationGas: BigNumber;
170+
verificationGas: BigNumber;
171+
verificationGasLimit: BigNumber;
172+
callGasLimit: BigNumber;
173+
};

packages/thirdweb/src/auth/verifySignature.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ export type VerifySignatureParams = Prettify<
106106

107107
/**
108108
* Verifies the signature based on the provided options.
109+
* Handles smart contract wallet signatures and EOA signatures.
110+
* IMPORTANT: in order to check smart contract signatures, a chain and client must be provided.
109111
* @param options - The options for signature verification.
110112
* @returns A boolean indicating whether the signature is valid or not.
111113
* @example

packages/thirdweb/src/contract/actions/get-bytecode.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,14 @@ export function getBytecode<abi extends Abi>(
2626

2727
const prom = (async () => {
2828
const rpcRequest = getRpcClient(contract);
29-
return eth_getCode(rpcRequest, {
29+
const result = await eth_getCode(rpcRequest, {
3030
address: contract.address,
3131
blockTag: "latest",
3232
});
33+
if (result === "0x") {
34+
BYTECODE_CACHE.delete(contract);
35+
}
36+
return result;
3337
})();
3438
BYTECODE_CACHE.set(contract, prom);
3539
return prom;

packages/thirdweb/src/event/actions/get-events.test.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { describe, it, expect } from "vitest";
22
import { getContractEvents } from "./get-events.js";
33
import {
44
DOODLES_CONTRACT,
5-
USDC_CONTRACT,
5+
USDT_CONTRACT,
66
} from "../../../test/src/test-contracts.js";
77
import { transferEvent } from "../../extensions/erc721/__generated__/IERC721A/events/Transfer.js";
88
import { prepareEvent } from "../prepare-event.js";
@@ -12,28 +12,28 @@ import { prepareEvent } from "../prepare-event.js";
1212
describe.runIf(process.env.TW_SECRET_KEY)("getEvents", () => {
1313
it("should get all events", async () => {
1414
const events = await getContractEvents({
15-
contract: USDC_CONTRACT,
15+
contract: USDT_CONTRACT,
1616
fromBlock: 19139495n - 10n,
1717
});
18-
expect(events.length).toBe(87);
18+
expect(events.length).toBe(261);
1919
});
2020

2121
it("should get individual events with signature", async () => {
2222
const events = await getContractEvents({
23-
contract: USDC_CONTRACT,
24-
fromBlock: 19139495n - 10n,
23+
contract: USDT_CONTRACT,
24+
fromBlock: 19139495n - 100n,
2525
events: [
2626
prepareEvent({
2727
signature: "event Burn(address indexed burner, uint256 amount)",
2828
}),
2929
],
3030
});
31-
expect(events.length).toBe(2);
31+
expect(events.length).toMatchInlineSnapshot(`0`);
3232
});
3333

3434
it("should get specified events", async () => {
3535
const events = await getContractEvents({
36-
contract: USDC_CONTRACT,
36+
contract: USDT_CONTRACT,
3737
fromBlock: 19139495n - 10n,
3838
events: [
3939
prepareEvent({
@@ -68,7 +68,7 @@ describe.runIf(process.env.TW_SECRET_KEY)("getEvents", () => {
6868
}),
6969
],
7070
});
71-
expect(events.length).toBe(15);
71+
expect(events.length).toMatchInlineSnapshot(`9`);
7272
});
7373

7474
it("should get individual events with extension", async () => {

packages/thirdweb/src/extensions/common/read/getContractMetadata.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
import { describe, it, expect } from "vitest";
22
import { getContractMetadata } from "./getContractMetadata.js";
3-
import { USDC_CONTRACT } from "../../../../test/src/test-contracts.js";
3+
import { USDT_CONTRACT } from "../../../../test/src/test-contracts.js";
44

55
describe.runIf(process.env.TW_SECRET_KEY)("shared.getContractMetadata", () => {
66
it("should return the correct contract metadata", async () => {
77
const metadata = await getContractMetadata({
8-
contract: USDC_CONTRACT,
8+
contract: USDT_CONTRACT,
99
});
1010
expect(metadata).toMatchInlineSnapshot(`
1111
{
12-
"name": "USD Coin",
13-
"symbol": "USDC",
12+
"name": "Tether USD",
13+
"symbol": "USDT",
1414
}
1515
`);
1616
});

0 commit comments

Comments
 (0)