diff --git a/.changeset/pretty-geese-win.md b/.changeset/pretty-geese-win.md new file mode 100644 index 00000000000..cb9ff7ecca0 --- /dev/null +++ b/.changeset/pretty-geese-win.md @@ -0,0 +1,5 @@ +--- +"thirdweb": patch +--- + +Add Pay Modal integration in `useSendAndConfirmTransaction` hook similar to `useSendTransaction` hook diff --git a/packages/thirdweb/src/exports/react.native.ts b/packages/thirdweb/src/exports/react.native.ts index 1d324b111db..8b5e5cfdac1 100644 --- a/packages/thirdweb/src/exports/react.native.ts +++ b/packages/thirdweb/src/exports/react.native.ts @@ -59,7 +59,7 @@ export { export type { TransactionButtonProps } from "../react/core/hooks/transaction/transaction-button-utils.js"; export { useEstimateGas } from "../react/core/hooks/transaction/useEstimateGas.js"; export { useEstimateGasCost } from "../react/core/hooks/transaction/useEstimateGasCost.js"; -export { useSendAndConfirmTransaction } from "../react/core/hooks/transaction/useSendAndConfirmTransaction.js"; +export { useSendAndConfirmTransactionCore as useSendAndConfirmTransaction } from "../react/core/hooks/transaction/useSendAndConfirmTransaction.js"; export { useSendBatchTransaction } from "../react/core/hooks/transaction/useSendBatchTransaction.js"; // transaction export type { diff --git a/packages/thirdweb/src/exports/react.ts b/packages/thirdweb/src/exports/react.ts index 3656d528b4b..c267cd95716 100644 --- a/packages/thirdweb/src/exports/react.ts +++ b/packages/thirdweb/src/exports/react.ts @@ -66,7 +66,6 @@ export { export type { TransactionButtonProps } from "../react/core/hooks/transaction/transaction-button-utils.js"; export { useEstimateGas } from "../react/core/hooks/transaction/useEstimateGas.js"; export { useEstimateGasCost } from "../react/core/hooks/transaction/useEstimateGasCost.js"; -export { useSendAndConfirmTransaction } from "../react/core/hooks/transaction/useSendAndConfirmTransaction.js"; export { useSendBatchTransaction } from "../react/core/hooks/transaction/useSendBatchTransaction.js"; // transaction export type { @@ -101,7 +100,6 @@ export { useConnectionManager } from "../react/core/providers/connection-manager // Social export { useSocialProfiles } from "../react/core/social/useSocialProfiles.js"; export type { AccountBalanceInfo } from "../react/core/utils/account.js"; - // utils export { createContractQuery } from "../react/core/utils/createQuery.js"; // tokens @@ -126,6 +124,7 @@ export { WalletProvider, type WalletProviderProps, } from "../react/core/wallet/provider.js"; +export { useSendAndConfirmTransaction } from "../react/web/hooks/transaction/useSendAndConfirmTransaction.js"; export { useSendTransaction } from "../react/web/hooks/transaction/useSendTransaction.js"; export { useAutoConnect } from "../react/web/hooks/wallets/useAutoConnect.js"; export { useLinkProfile } from "../react/web/hooks/wallets/useLinkProfile.js"; diff --git a/packages/thirdweb/src/react/core/hooks/transaction/useSendAndConfirmTransaction.ts b/packages/thirdweb/src/react/core/hooks/transaction/useSendAndConfirmTransaction.ts index 73a812f00a6..ff37a5d8f4a 100644 --- a/packages/thirdweb/src/react/core/hooks/transaction/useSendAndConfirmTransaction.ts +++ b/packages/thirdweb/src/react/core/hooks/transaction/useSendAndConfirmTransaction.ts @@ -54,7 +54,7 @@ type SendAndConfirmTransactionConfig = { * ``` * @transaction */ -export function useSendAndConfirmTransaction( +export function useSendAndConfirmTransactionCore( config: SendAndConfirmTransactionConfig = {}, ): UseMutationResult< TransactionReceipt, diff --git a/packages/thirdweb/src/react/web/hooks/transaction/useSendAndConfirmTransaction.tsx b/packages/thirdweb/src/react/web/hooks/transaction/useSendAndConfirmTransaction.tsx new file mode 100644 index 00000000000..3442395e85e --- /dev/null +++ b/packages/thirdweb/src/react/web/hooks/transaction/useSendAndConfirmTransaction.tsx @@ -0,0 +1,104 @@ +import { type UseMutationResult, useMutation } from "@tanstack/react-query"; +import type { GaslessOptions } from "../../../../transaction/actions/gasless/types.js"; +import type { SendTransactionOptions } from "../../../../transaction/actions/send-transaction.js"; +import { waitForReceipt } from "../../../../transaction/actions/wait-for-tx-receipt.js"; +import type { TransactionReceipt } from "../../../../transaction/types.js"; +import type { SendTransactionPayModalConfig } from "../../../core/hooks/transaction/useSendTransaction.js"; +import { useSendTransaction } from "./useSendTransaction.js"; + +/** + * Configuration for the `useSendAndConfirmTransaction` hook. + */ +type SendAndConfirmTransactionConfig = { + /** + * Refer to [`SendTransactionPayModalConfig`](https://portal.thirdweb.com/references/typescript/v5/SendTransactionPayModalConfig) for more details. + */ + payModal?: SendTransactionPayModalConfig; + + /** + * Configuration for gasless transactions. + * Refer to [`GaslessOptions`](https://portal.thirdweb.com/references/typescript/v5/GaslessOptions) for more details. + */ + gasless?: GaslessOptions; +}; + +/** + * A hook to send a transaction and confirm the transaction with users's connected wallet + * @returns A mutation object to send a transaction. + * @example + * ```jsx + * import { useSendAndConfirmTransaction } from "thirdweb/react"; + * const { mutate: sendAndConfirmTx, data: transactionReceipt } = useSendAndConfirmTransaction(); + * + * // later + * sendAndConfirmTx(tx); + * ``` + * + * + * ### Gasless usage with [thirdweb Engine](https://portal.thirdweb.com/engine) + * ```tsx + * import { useSendAndConfirmTransaction } from "thirdweb/react"; + * const mutation = useSendAndConfirmTransaction({ + * gasless: { + * provider: "engine", + * relayerUrl: "https://thirdweb.engine-***.thirdweb.com/relayer/***", + * relayerForwarderAddress: "0x...", + * } + * }); + * ``` + * + * ### Gasless usage with OpenZeppelin + * ```tsx + * import { useSendAndConfirmTransaction } from "thirdweb/react"; + * const mutation = useSendAndConfirmTransaction({ + * gasless: { + * provider: "openzeppelin", + * relayerUrl: "https://...", + * relayerForwarderAddress: "0x...", + * } + * }); + * ``` + * + * ### Configuring the Pay Modal + * + * When the wallet does not have enough funds to send the transaction, a modal is shown to the user to buy the required funds and then continue with the transaction. + * + * You can configure the pay modal by passing a [`SendTransactionPayModalConfig`](https://portal.thirdweb.com/references/typescript/v5/SendTransactionPayModalConfig) object to the `payModal` config. + * + * ```tsx + * import { useSendAndConfirmTransaction } from "thirdweb/react"; + * + * const sendAndConfirmTx = useSendAndConfirmTransaction({ + * payModal: { + * theme: "light", + * }, + * }); + * ``` + * + * By default, the pay modal is enabled. You can disable it by passing `payModal: false` to the config. + * + * ```tsx + * import { useSendAndConfirmTransaction } from "thirdweb/react"; + * + * const sendAndConfirmTx = useSendAndConfirmTransaction({ + * payModal: false, + * }); + * ``` + * @transaction + */ +export function useSendAndConfirmTransaction( + config: SendAndConfirmTransactionConfig = {}, +): UseMutationResult< + TransactionReceipt, + Error, + SendTransactionOptions["transaction"] +> { + const sendTx = useSendTransaction(config); + return useMutation({ + mutationFn: async (transaction) => { + const receipt = await sendTx.mutateAsync(transaction); + const confirmedReceipt = await waitForReceipt(receipt); + return confirmedReceipt; + }, + }); +} diff --git a/packages/thirdweb/src/react/web/hooks/transaction/useSendTransaction.tsx b/packages/thirdweb/src/react/web/hooks/transaction/useSendTransaction.tsx index dc63713cc99..5e8d5cc80dd 100644 --- a/packages/thirdweb/src/react/web/hooks/transaction/useSendTransaction.tsx +++ b/packages/thirdweb/src/react/web/hooks/transaction/useSendTransaction.tsx @@ -93,6 +93,32 @@ import { TransactionModal } from "../../ui/TransactionButton/TransactionModal.js * }; * ``` * + * ### Configuring the Pay Modal + * + * When the wallet does not have enough funds to send the transaction, a modal is shown to the user to buy the required funds and then continue with the transaction. + * + * You can configure the pay modal by passing a [`SendTransactionPayModalConfig`](https://portal.thirdweb.com/references/typescript/v5/SendTransactionPayModalConfig) object to the `payModal` config. + * + * ```tsx + * import { useSendTransaction } from "thirdweb/react"; + * + * const sendTx = useSendTransaction({ + * payModal: { + * theme: "light", + * }, + * }); + * ``` + * + * By default, the pay modal is enabled. You can disable it by passing `payModal: false` to the config. + * + * ```tsx + * import { useSendTransaction } from "thirdweb/react"; + * + * const sendTx = useSendTransaction({ + * payModal: false, + * }); + * ``` + * * @transaction */ export function useSendTransaction(config: SendTransactionConfig = {}) { diff --git a/packages/thirdweb/src/react/web/ui/prebuilt/thirdweb/BuyDirectListingButton/index.tsx b/packages/thirdweb/src/react/web/ui/prebuilt/thirdweb/BuyDirectListingButton/index.tsx index daf77cce7c3..27f192a5ae0 100644 --- a/packages/thirdweb/src/react/web/ui/prebuilt/thirdweb/BuyDirectListingButton/index.tsx +++ b/packages/thirdweb/src/react/web/ui/prebuilt/thirdweb/BuyDirectListingButton/index.tsx @@ -8,8 +8,8 @@ import { getListing } from "../../../../../../extensions/marketplace/direct-list import type { BaseTransactionOptions } from "../../../../../../transaction/types.js"; import { useReadContract } from "../../../../../core/hooks/contract/useReadContract.js"; import type { TransactionButtonProps } from "../../../../../core/hooks/transaction/transaction-button-utils.js"; -import { useSendAndConfirmTransaction } from "../../../../../core/hooks/transaction/useSendAndConfirmTransaction.js"; import { useActiveAccount } from "../../../../../core/hooks/wallets/useActiveAccount.js"; +import { useSendAndConfirmTransaction } from "../../../../hooks/transaction/useSendAndConfirmTransaction.js"; import { TransactionButton } from "../../../TransactionButton/index.js"; export type BuyDirectListingButtonProps = Omit< @@ -104,7 +104,15 @@ export function BuyDirectListingButton(props: BuyDirectListingButtonProps) { }, }); - const { mutateAsync } = useSendAndConfirmTransaction(); + const { mutateAsync } = useSendAndConfirmTransaction({ + payModal: + typeof payModal === "object" + ? { + ...payModal, + metadata: payModal.metadata || payMetadata, + } + : payModal, + }); const prepareBuyTransaction = useCallback(async () => { if (!account) { diff --git a/packages/thirdweb/src/react/web/ui/prebuilt/thirdweb/ClaimButton/index.tsx b/packages/thirdweb/src/react/web/ui/prebuilt/thirdweb/ClaimButton/index.tsx index 7776526376c..9309f770d5a 100644 --- a/packages/thirdweb/src/react/web/ui/prebuilt/thirdweb/ClaimButton/index.tsx +++ b/packages/thirdweb/src/react/web/ui/prebuilt/thirdweb/ClaimButton/index.tsx @@ -10,8 +10,8 @@ import type { PreparedTransaction } from "../../../../../../transaction/prepare- import type { BaseTransactionOptions } from "../../../../../../transaction/types.js"; import type { Account } from "../../../../../../wallets/interfaces/wallet.js"; import { useReadContract } from "../../../../../core/hooks/contract/useReadContract.js"; -import { useSendAndConfirmTransaction } from "../../../../../core/hooks/transaction/useSendAndConfirmTransaction.js"; import { useActiveAccount } from "../../../../../core/hooks/wallets/useActiveAccount.js"; +import { useSendAndConfirmTransaction } from "../../../../hooks/transaction/useSendAndConfirmTransaction.js"; import { TransactionButton } from "../../../TransactionButton/index.js"; import type { ClaimButtonProps, @@ -126,7 +126,15 @@ export function ClaimButton(props: ClaimButtonProps) { tokenId: claimParams.type === "ERC1155" ? claimParams.tokenId : undefined, }); const account = useActiveAccount(); - const { mutateAsync } = useSendAndConfirmTransaction(); + const { mutateAsync } = useSendAndConfirmTransaction({ + payModal: + typeof payModal === "object" + ? { + ...payModal, + metadata: payModal.metadata || payMetadata, + } + : payModal, + }); return ( { if (!account) {