From e7dd37fb1b52157647eff751f194c2b6c6e991b2 Mon Sep 17 00:00:00 2001 From: Joaquim Verges Date: Mon, 2 Dec 2024 14:15:00 +1300 Subject: [PATCH] fix: improve transaction cost estimation for pay modal --- .changeset/ten-rockets-stare.md | 5 +++ .../hooks/transaction/useSendTransaction.ts | 41 +------------------ .../screens/Buy/main/useBuyTxStates.ts | 30 +------------- packages/thirdweb/src/transaction/utils.ts | 33 +++++++++++++++ 4 files changed, 41 insertions(+), 68 deletions(-) create mode 100644 .changeset/ten-rockets-stare.md diff --git a/.changeset/ten-rockets-stare.md b/.changeset/ten-rockets-stare.md new file mode 100644 index 00000000000..ffaf2132167 --- /dev/null +++ b/.changeset/ten-rockets-stare.md @@ -0,0 +1,5 @@ +--- +"thirdweb": patch +--- + +Fix tx cost estimation for pay transaction modal diff --git a/packages/thirdweb/src/react/core/hooks/transaction/useSendTransaction.ts b/packages/thirdweb/src/react/core/hooks/transaction/useSendTransaction.ts index b8b281938cd..922a213eda5 100644 --- a/packages/thirdweb/src/react/core/hooks/transaction/useSendTransaction.ts +++ b/packages/thirdweb/src/react/core/hooks/transaction/useSendTransaction.ts @@ -1,14 +1,13 @@ import { type UseMutationResult, useMutation } from "@tanstack/react-query"; import type { Chain } from "../../../../chains/types.js"; -import { getGasPrice } from "../../../../gas/get-gas-price.js"; import type { BuyWithCryptoStatus } from "../../../../pay/buyWithCrypto/getStatus.js"; import type { BuyWithFiatStatus } from "../../../../pay/buyWithFiat/getStatus.js"; import type { FiatProvider } from "../../../../pay/utils/commonTypes.js"; -import { estimateGasCost } from "../../../../transaction/actions/estimate-gas-cost.js"; import type { GaslessOptions } from "../../../../transaction/actions/gasless/types.js"; import { sendTransaction } from "../../../../transaction/actions/send-transaction.js"; import type { WaitForReceiptOptions } from "../../../../transaction/actions/wait-for-tx-receipt.js"; import type { PreparedTransaction } from "../../../../transaction/prepare-transaction.js"; +import { getTransactionGasCost } from "../../../../transaction/utils.js"; import { resolvePromisedValue } from "../../../../utils/promise/resolve-promised-value.js"; import type { Wallet } from "../../../../wallets/interfaces/wallet.js"; import { getTokenBalance } from "../../../../wallets/utils/getTokenBalance.js"; @@ -215,7 +214,7 @@ export function useSendTransactionCore(args: { tokenAddress: _erc20Value.tokenAddress, }) : undefined, - getTotalTxCostForBuy(tx, account.address), + getTransactionGasCost(tx, account.address), ]); const gasSponsored = hasSponsoredTransactionsEnabled(wallet); @@ -248,39 +247,3 @@ export function useSendTransactionCore(args: { }, }); } - -async function getTotalTxCostForBuy(tx: PreparedTransaction, from?: string) { - try { - const gasCost = await estimateGasCost({ - transaction: tx, - from, - }); - - const bufferCost = gasCost.wei / 10n; - - // Note: get tx.value AFTER estimateGasCost - const txValue = await resolvePromisedValue(tx.value); - - // add 10% extra gas cost to the estimate to ensure user buys enough to cover the tx cost - return gasCost.wei + bufferCost + (txValue || 0n); - } catch { - if (from) { - // try again without passing from - return await getTotalTxCostForBuy(tx); - } - // fallback if both fail, use the tx value + 2M * gas price - const value = await resolvePromisedValue(tx.value); - - const gasPrice = await getGasPrice({ - client: tx.client, - chain: tx.chain, - }); - - const buffer = 2_000_000n * gasPrice; - - if (!value) { - return 0n + buffer; - } - return value + buffer; - } -} diff --git a/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/main/useBuyTxStates.ts b/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/main/useBuyTxStates.ts index 919da47731f..f913825b356 100644 --- a/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/main/useBuyTxStates.ts +++ b/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/main/useBuyTxStates.ts @@ -4,10 +4,9 @@ import { getChainMetadata } from "../../../../../../../chains/utils.js"; import { NATIVE_TOKEN_ADDRESS } from "../../../../../../../constants/addresses.js"; import { getContract } from "../../../../../../../contract/contract.js"; import { getCurrencyMetadata } from "../../../../../../../extensions/erc20/read/getCurrencyMetadata.js"; -import { getGasPrice } from "../../../../../../../gas/get-gas-price.js"; import { encode } from "../../../../../../../transaction/actions/encode.js"; -import { estimateGasCost } from "../../../../../../../transaction/actions/estimate-gas-cost.js"; import type { PreparedTransaction } from "../../../../../../../transaction/prepare-transaction.js"; +import { getTransactionGasCost } from "../../../../../../../transaction/utils.js"; import type { Hex } from "../../../../../../../utils/encoding/hex.js"; import { resolvePromisedValue } from "../../../../../../../utils/promise/resolve-promised-value.js"; import type { Account } from "../../../../../../../wallets/interfaces/wallet.js"; @@ -138,30 +137,3 @@ export function useTransactionCostAndData(args: { }, }); } - -async function getTransactionGasCost(tx: PreparedTransaction, from?: string) { - try { - const gasCost = await estimateGasCost({ - transaction: tx, - from, - }); - - const bufferCost = gasCost.wei / 10n; - - // Note: get tx.value AFTER estimateGasCost - // add 10% extra gas cost to the estimate to ensure user buys enough to cover the tx cost - return gasCost.wei + bufferCost; - } catch { - if (from) { - // try again without passing from - return await getTransactionGasCost(tx); - } - // fallback if both fail, use the tx value + 2M * gas price - const gasPrice = await getGasPrice({ - client: tx.client, - chain: tx.chain, - }); - - return 2_000_000n * gasPrice; - } -} diff --git a/packages/thirdweb/src/transaction/utils.ts b/packages/thirdweb/src/transaction/utils.ts index ca52bd5edd8..de8df87c4cd 100644 --- a/packages/thirdweb/src/transaction/utils.ts +++ b/packages/thirdweb/src/transaction/utils.ts @@ -1,4 +1,7 @@ import type { AbiFunction } from "abitype"; +import { getGasPrice } from "../gas/get-gas-price.js"; +import { estimateGasCost } from "./actions/estimate-gas-cost.js"; +import type { PreparedTransaction } from "./prepare-transaction.js"; /** * @internal @@ -11,3 +14,33 @@ export function isAbiFunction(item: unknown): item is AbiFunction { item.type === "function" ); } + +export async function getTransactionGasCost( + tx: PreparedTransaction, + from?: string, +) { + try { + const gasCost = await estimateGasCost({ + transaction: tx, + from, + }); + + const bufferCost = gasCost.wei / 10n; + + // Note: get tx.value AFTER estimateGasCost + // add 10% extra gas cost to the estimate to ensure user buys enough to cover the tx cost + return gasCost.wei + bufferCost; + } catch { + if (from) { + // try again without passing from + return await getTransactionGasCost(tx); + } + // fallback if both fail, use the tx value + 1M * gas price + const gasPrice = await getGasPrice({ + client: tx.client, + chain: tx.chain, + }); + + return 1_000_000n * gasPrice; + } +}