From 38f9fa739689f93ff819c1eab76c1b04d63e389a Mon Sep 17 00:00:00 2001 From: brown Date: Thu, 12 Dec 2024 14:52:46 -0500 Subject: [PATCH 1/3] feat(berajs): add gas limit estimation to prevent hitting gas limits set by validator nodes --- .../src/hooks/modules/dex/useCreatePool.ts | 7 ++-- .../useContractWrite/useBeraContractWrite.ts | 35 +++++++++++++------ packages/berajs/src/utils/const.ts | 1 + packages/berajs/src/utils/index.ts | 1 + 4 files changed, 30 insertions(+), 14 deletions(-) create mode 100644 packages/berajs/src/utils/const.ts diff --git a/packages/berajs/src/hooks/modules/dex/useCreatePool.ts b/packages/berajs/src/hooks/modules/dex/useCreatePool.ts index d6120f623..6bcb07e78 100755 --- a/packages/berajs/src/hooks/modules/dex/useCreatePool.ts +++ b/packages/berajs/src/hooks/modules/dex/useCreatePool.ts @@ -20,10 +20,9 @@ import { balancerPoolCreationHelperAbi } from "~/abi"; import { ADDRESS_ZERO } from "~/config"; import { IContractWrite } from "~/hooks/useContractWrite"; import { Token, TokenInput } from "~/types"; +import { DEFAULT_METAMASK_GAS_LIMIT } from "~/utils"; const DEFAULT_WEIGHTS_DUPLICATION_THRESHOLD = 0.005; -const DEFAULT_POOL_CREATE_GAS_LIMIT = 7_920_027n; // NOTE: this is the metamask gas limit, in experiments we find we can easily use 75% of this. - interface UseCreatePoolProps { poolCreateTokens: Token[]; initialLiquidityTokens: TokenInput[]; @@ -56,7 +55,7 @@ const createStablePoolArgs = ( poolSymbol: string, owner: string, amplification: number, - gasLimit: bigint = DEFAULT_POOL_CREATE_GAS_LIMIT, + gasLimit: bigint = DEFAULT_METAMASK_GAS_LIMIT, ) => { // Map and sort pool creation token addresses NOTE: we should never see BERA in this array. const sortedPoolCreateAddresses = poolCreateTokens @@ -135,7 +134,7 @@ const createWeightedPoolArgs = ( poolName: string, poolSymbol: string, owner: string, - gasLimit: bigint = DEFAULT_POOL_CREATE_GAS_LIMIT, + gasLimit: bigint = DEFAULT_METAMASK_GAS_LIMIT, ) => { // When joining weighted pools with BERA, we allow it as a joinPoolToken but never as a createPoolToken. const createPoolTokens: string[] = []; diff --git a/packages/berajs/src/hooks/useContractWrite/useBeraContractWrite.ts b/packages/berajs/src/hooks/useContractWrite/useBeraContractWrite.ts index c083f9257..a3b589a3f 100755 --- a/packages/berajs/src/hooks/useContractWrite/useBeraContractWrite.ts +++ b/packages/berajs/src/hooks/useContractWrite/useBeraContractWrite.ts @@ -10,6 +10,7 @@ import { type IUseContractWriteArgs, type useContractWriteApi, } from "./types"; +import { DEFAULT_METAMASK_GAS_LIMIT } from "~/utils"; const increaseByPercentage = (value: bigint, percentage: number) => { return value + (value * BigInt(percentage)) / BigInt(100); @@ -45,7 +46,8 @@ const useBeraContractWrite = ({ params, value = 0n, data, - gasLimit = 2000000n, + gasLimit = DEFAULT_METAMASK_GAS_LIMIT, + ...rest }: IContractWrite): Promise => { dispatch({ type: ActionEnum.LOADING }); onLoading?.(); @@ -60,18 +62,31 @@ const useBeraContractWrite = ({ gas: gasLimit, }); } else { + // Run simulation and gas estimation in parallel // TODO: figure out clean way to early detect errors and effectively show them on the UI - const { request } = await publicClient.simulateContract({ - address: address, - abi: abi, - functionName: functionName, - args: params, - value: value, - account: account, - }); + const [{ request }, wagmiPubEstimateContractGas] = await Promise.all([ + publicClient.simulateContract({ + address: address, + abi: abi, + functionName: functionName, + args: params, + value: value, + account: account, + }), + publicClient.estimateContractGas({ + address: address, + abi: abi, + functionName: functionName, + args: params, + value: value, + account: account, + }), + ]); + receipt = await writeContractAsync({ ...request, - gas: gasLimit ?? request.gas, + gas: + increaseByPercentage(wagmiPubEstimateContractGas, 10) ?? gasLimit, }); } diff --git a/packages/berajs/src/utils/const.ts b/packages/berajs/src/utils/const.ts new file mode 100644 index 000000000..64d7afc07 --- /dev/null +++ b/packages/berajs/src/utils/const.ts @@ -0,0 +1 @@ +export const DEFAULT_METAMASK_GAS_LIMIT = 7920027n; // NOTE: this is the metamask gas limit, https://github.com/MetaMask/metamask-extension/blob/06cf7459b963e26d0ae60312d4f9a342d07ab89d/ui/pages/confirmations/send/send.constants.js#L8 diff --git a/packages/berajs/src/utils/index.ts b/packages/berajs/src/utils/index.ts index a3ef42efb..8fab6bdbb 100755 --- a/packages/berajs/src/utils/index.ts +++ b/packages/berajs/src/utils/index.ts @@ -14,3 +14,4 @@ export * from "./formatTimestamps"; export * from "./tokenWrapping"; export * from "./errorMessages"; export * from "./isSubgraphStale"; +export * from "./const"; From 10b515385c7821f46ddd4fa9eddd9d03f320e4eb Mon Sep 17 00:00:00 2001 From: brown Date: Wed, 18 Dec 2024 09:09:19 -0500 Subject: [PATCH 2/3] feat(berajs): add simple nonce calculation --- .../src/hooks/useContractWrite/useBeraContractWrite.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/berajs/src/hooks/useContractWrite/useBeraContractWrite.ts b/packages/berajs/src/hooks/useContractWrite/useBeraContractWrite.ts index a3b589a3f..a5d00b952 100755 --- a/packages/berajs/src/hooks/useContractWrite/useBeraContractWrite.ts +++ b/packages/berajs/src/hooks/useContractWrite/useBeraContractWrite.ts @@ -52,14 +52,21 @@ const useBeraContractWrite = ({ dispatch({ type: ActionEnum.LOADING }); onLoading?.(); let receipt: Awaited>; - if (!publicClient) return; + if (!publicClient || !account) return; try { + // Get the next nonce for the account + const nonce = await publicClient.getTransactionCount({ + address: account, + blockTag: "pending", + }); + if (data) { receipt = await sendTransactionAsync({ data, to: address, value, gas: gasLimit, + nonce: nonce + 1, }); } else { // Run simulation and gas estimation in parallel @@ -87,6 +94,7 @@ const useBeraContractWrite = ({ ...request, gas: increaseByPercentage(wagmiPubEstimateContractGas, 10) ?? gasLimit, + nonce: nonce + 1, }); } From a0ea4bb043c0ff14f9b25740d618084a6952b91a Mon Sep 17 00:00:00 2001 From: brown Date: Thu, 19 Dec 2024 11:00:00 -0500 Subject: [PATCH 3/3] fix(berajs): fix nonce and estimation values --- .../useContractWrite/useBeraContractWrite.ts | 79 +++++++++++++------ packages/berajs/src/utils/const.ts | 6 +- 2 files changed, 58 insertions(+), 27 deletions(-) diff --git a/packages/berajs/src/hooks/useContractWrite/useBeraContractWrite.ts b/packages/berajs/src/hooks/useContractWrite/useBeraContractWrite.ts index a5d00b952..9598d5081 100755 --- a/packages/berajs/src/hooks/useContractWrite/useBeraContractWrite.ts +++ b/packages/berajs/src/hooks/useContractWrite/useBeraContractWrite.ts @@ -4,13 +4,13 @@ import { usePublicClient, useSendTransaction, useWriteContract } from "wagmi"; import { getErrorMessage, getRevertReason } from "~/utils/errorMessages"; import { ActionEnum, initialState, reducer } from "~/utils/stateReducer"; import { useBeraJs } from "~/contexts"; +import { DEFAULT_METAMASK_GAS_LIMIT } from "~/utils"; import { usePollTransactionCount } from "../usePollTransactionCount"; import { type IContractWrite, type IUseContractWriteArgs, type useContractWriteApi, } from "./types"; -import { DEFAULT_METAMASK_GAS_LIMIT } from "~/utils"; const increaseByPercentage = (value: bigint, percentage: number) => { return value + (value * BigInt(percentage)) / BigInt(100); @@ -46,7 +46,7 @@ const useBeraContractWrite = ({ params, value = 0n, data, - gasLimit = DEFAULT_METAMASK_GAS_LIMIT, + gasLimit, ...rest }: IContractWrite): Promise => { dispatch({ type: ActionEnum.LOADING }); @@ -61,40 +61,67 @@ const useBeraContractWrite = ({ }); if (data) { + // Add gas estimation for direct transactions + const estimatedGas = + gasLimit ?? + (await publicClient + .estimateGas({ + account, + to: address, + data, + value, + }) + .catch(() => DEFAULT_METAMASK_GAS_LIMIT)); + receipt = await sendTransactionAsync({ data, to: address, value, - gas: gasLimit, - nonce: nonce + 1, + gas: estimatedGas, + nonce: nonce, }); } else { // Run simulation and gas estimation in parallel // TODO: figure out clean way to early detect errors and effectively show them on the UI - const [{ request }, wagmiPubEstimateContractGas] = await Promise.all([ - publicClient.simulateContract({ - address: address, - abi: abi, - functionName: functionName, - args: params, - value: value, - account: account, - }), - publicClient.estimateContractGas({ - address: address, - abi: abi, - functionName: functionName, - args: params, - value: value, - account: account, - }), - ]); + const [simulationResult, gasEstimateResult] = + await Promise.allSettled([ + publicClient.simulateContract({ + address: address, + abi: abi, + functionName: functionName, + args: params, + value: value, + account: account, + }), + // Only estimate gas if no gasLimit is provided + ...(!gasLimit + ? [ + publicClient.estimateContractGas({ + address: address, + abi: abi, + functionName: functionName, + args: params, + value: value, + account: account, + }), + ] + : []), + ]); + + if (simulationResult.status === "rejected") { + throw simulationResult.reason; + } + + const estimatedGas = + gasLimit ?? + (gasEstimateResult.status === "fulfilled" + ? increaseByPercentage(gasEstimateResult.value, 10) + : DEFAULT_METAMASK_GAS_LIMIT); receipt = await writeContractAsync({ - ...request, - gas: - increaseByPercentage(wagmiPubEstimateContractGas, 10) ?? gasLimit, - nonce: nonce + 1, + ...simulationResult.value.request, + gas: estimatedGas, + nonce: nonce, }); } diff --git a/packages/berajs/src/utils/const.ts b/packages/berajs/src/utils/const.ts index 64d7afc07..487c6ede3 100644 --- a/packages/berajs/src/utils/const.ts +++ b/packages/berajs/src/utils/const.ts @@ -1 +1,5 @@ -export const DEFAULT_METAMASK_GAS_LIMIT = 7920027n; // NOTE: this is the metamask gas limit, https://github.com/MetaMask/metamask-extension/blob/06cf7459b963e26d0ae60312d4f9a342d07ab89d/ui/pages/confirmations/send/send.constants.js#L8 +/** + * Default gas limit used by MetaMask + * @see {@link https://github.com/MetaMask/metamask-extension/blob/06cf7459b963e26d0ae60312d4f9a342d07ab89d/ui/pages/confirmations/send/send.constants.js#L8} + */ +export const DEFAULT_METAMASK_GAS_LIMIT = 7920027n;