Add fallback for gas fee estimation in zkSync (#8637)
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"thirdweb": patch
|
||||
---
|
||||
|
||||
newer zkos chains no longer support zks\_ rpc endpoints, fallback to evm std
|
||||
@@ -5,9 +5,13 @@ import {
|
||||
TEST_ACCOUNT_B,
|
||||
} from "../../../../test/src/test-wallets.js";
|
||||
import { zkSyncSepolia } from "../../../chains/chain-definitions/zksync-sepolia.js";
|
||||
import { defineChain } from "../../../chains/utils.js";
|
||||
import { deployPublishedContract } from "../../../extensions/prebuilts/deploy-published.js";
|
||||
import { prepareTransaction } from "../../prepare-transaction.js";
|
||||
import { sendEip712Transaction } from "./send-eip712-transaction.js";
|
||||
import {
|
||||
getZkGasFees,
|
||||
sendEip712Transaction,
|
||||
} from "./send-eip712-transaction.js";
|
||||
|
||||
describe("sendEip712Transaction", () => {
|
||||
// re-enable for testing, but disable for CI since it requires testnet funds
|
||||
@@ -52,4 +56,34 @@ describe("sendEip712Transaction", () => {
|
||||
expect(address).toBeDefined();
|
||||
expect(address.length).toBe(42);
|
||||
});
|
||||
|
||||
it("should fallback to standard EVM methods when zks_estimateFee is not available", async () => {
|
||||
// Chain 278701 is a zkSync chain that doesn't support zks_estimateFee
|
||||
const zkSyncChainWithoutZksSupport = defineChain(278701);
|
||||
|
||||
// Use a transaction with pre-defined gas to skip estimation (which requires balance)
|
||||
// This tests that the fallback path is taken for fee estimation
|
||||
const transaction = prepareTransaction({
|
||||
chain: zkSyncChainWithoutZksSupport,
|
||||
client: TEST_CLIENT,
|
||||
gas: 21000n, // pre-define gas to skip eth_estimateGas
|
||||
to: TEST_ACCOUNT_B.address,
|
||||
value: 0n,
|
||||
});
|
||||
|
||||
const gasFees = await getZkGasFees({
|
||||
transaction,
|
||||
from: TEST_ACCOUNT_A.address as `0x${string}`,
|
||||
});
|
||||
|
||||
// Verify fallback worked - should have valid gas values
|
||||
expect(gasFees.gas).toBeDefined();
|
||||
expect(gasFees.gas).toBeGreaterThan(0n);
|
||||
expect(gasFees.maxFeePerGas).toBeDefined();
|
||||
expect(gasFees.maxFeePerGas).toBeGreaterThan(0n);
|
||||
expect(gasFees.maxPriorityFeePerGas).toBeDefined();
|
||||
expect(gasFees.maxPriorityFeePerGas).toBeGreaterThan(0n);
|
||||
// Fallback should use 100k for gasPerPubdata
|
||||
expect(gasFees.gasPerPubdata).toBe(100000n);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -180,25 +180,59 @@ export async function getZkGasFees(args: {
|
||||
) {
|
||||
const rpc = getRpcClient(transaction);
|
||||
const params = await formatTransaction({ from, transaction });
|
||||
const result = (await rpc({
|
||||
// biome-ignore lint/suspicious/noExplicitAny: TODO add to RPC method types
|
||||
method: "zks_estimateFee" as any,
|
||||
// biome-ignore lint/suspicious/noExplicitAny: TODO add to RPC method types
|
||||
params: [replaceBigInts(params, toHex)] as any,
|
||||
})) as {
|
||||
gas_limit: string;
|
||||
max_fee_per_gas: string;
|
||||
max_priority_fee_per_gas: string;
|
||||
gas_per_pubdata_limit: string;
|
||||
};
|
||||
gas = toBigInt(result.gas_limit) * 2n; // overestimating to avoid issues when not accounting for paymaster extra gas ( we should really pass the paymaster input above for better accuracy )
|
||||
const baseFee = toBigInt(result.max_fee_per_gas);
|
||||
maxFeePerGas = baseFee * 2n; // bumping the base fee per gas to ensure fast inclusion
|
||||
maxPriorityFeePerGas = toBigInt(result.max_priority_fee_per_gas) || 1n;
|
||||
gasPerPubdata = toBigInt(result.gas_per_pubdata_limit) * 2n; // doubling for fast inclusion;
|
||||
if (gasPerPubdata < 50000n) {
|
||||
// enforce a minimum gas per pubdata limit
|
||||
gasPerPubdata = 50000n;
|
||||
|
||||
// Try zkSync-specific fee estimation first, fallback to standard EVM methods
|
||||
try {
|
||||
const result = (await rpc({
|
||||
// biome-ignore lint/suspicious/noExplicitAny: TODO add to RPC method types
|
||||
method: "zks_estimateFee" as any,
|
||||
// biome-ignore lint/suspicious/noExplicitAny: TODO add to RPC method types
|
||||
params: [replaceBigInts(params, toHex)] as any,
|
||||
})) as {
|
||||
gas_limit: string;
|
||||
max_fee_per_gas: string;
|
||||
max_priority_fee_per_gas: string;
|
||||
gas_per_pubdata_limit: string;
|
||||
};
|
||||
gas = toBigInt(result.gas_limit) * 2n; // overestimating to avoid issues when not accounting for paymaster extra gas ( we should really pass the paymaster input above for better accuracy )
|
||||
const baseFee = toBigInt(result.max_fee_per_gas);
|
||||
maxFeePerGas = baseFee * 2n; // bumping the base fee per gas to ensure fast inclusion
|
||||
maxPriorityFeePerGas = toBigInt(result.max_priority_fee_per_gas) || 1n;
|
||||
gasPerPubdata = toBigInt(result.gas_per_pubdata_limit) * 2n; // doubling for fast inclusion;
|
||||
if (gasPerPubdata < 50000n) {
|
||||
// enforce a minimum gas per pubdata limit
|
||||
gasPerPubdata = 50000n;
|
||||
}
|
||||
} catch {
|
||||
// Fallback to standard EVM methods if zks_estimateFee is not available
|
||||
const [{ estimateGas }, { getDefaultGasOverrides }] = await Promise.all([
|
||||
import("../estimate-gas.js"),
|
||||
import("../../../gas/fee-data.js"),
|
||||
]);
|
||||
|
||||
const [estimatedGas, gasOverrides] = await Promise.all([
|
||||
gas === undefined
|
||||
? estimateGas({ transaction, from })
|
||||
: Promise.resolve(gas),
|
||||
getDefaultGasOverrides(transaction.client, transaction.chain),
|
||||
]);
|
||||
|
||||
gas = estimatedGas * 2n; // overestimating similar to zkSync estimation
|
||||
if ("maxFeePerGas" in gasOverrides && gasOverrides.maxFeePerGas) {
|
||||
maxFeePerGas = gasOverrides.maxFeePerGas * 2n; // bumping for fast inclusion
|
||||
} else if ("gasPrice" in gasOverrides && gasOverrides.gasPrice) {
|
||||
maxFeePerGas = gasOverrides.gasPrice * 2n;
|
||||
}
|
||||
if (
|
||||
"maxPriorityFeePerGas" in gasOverrides &&
|
||||
gasOverrides.maxPriorityFeePerGas
|
||||
) {
|
||||
maxPriorityFeePerGas = gasOverrides.maxPriorityFeePerGas;
|
||||
} else {
|
||||
maxPriorityFeePerGas = 1n;
|
||||
}
|
||||
// Use 100k as default gasPerPubdata for non-zkSync chains
|
||||
gasPerPubdata = 100000n;
|
||||
}
|
||||
}
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user