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..9598d5081 100755 --- a/packages/berajs/src/hooks/useContractWrite/useBeraContractWrite.ts +++ b/packages/berajs/src/hooks/useContractWrite/useBeraContractWrite.ts @@ -4,6 +4,7 @@ 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, @@ -45,33 +46,82 @@ const useBeraContractWrite = ({ params, value = 0n, data, - gasLimit = 2000000n, + gasLimit, + ...rest }: IContractWrite): Promise => { 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) { + // 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, + 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 } = await publicClient.simulateContract({ - 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: gasLimit ?? request.gas, + ...simulationResult.value.request, + gas: estimatedGas, + nonce: nonce, }); } diff --git a/packages/berajs/src/utils/const.ts b/packages/berajs/src/utils/const.ts new file mode 100644 index 000000000..487c6ede3 --- /dev/null +++ b/packages/berajs/src/utils/const.ts @@ -0,0 +1,5 @@ +/** + * 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; 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";