@0xfutbol/id
Version:
React component library with shared providers for 0xFutbol ID
847 lines • 154 kB
JavaScript
'use strict';var jsxRuntime=require('react/jsx-runtime'),reactQuery=require('@tanstack/react-query'),React=require('react'),index=require('./index-gEYw6hWC.js'),decimals=require('./decimals-C_tN8eFN.js'),getCurrencyMetadata=require('./getCurrencyMetadata-D7CN05L7.js');require('@0xfutbol/id-sign'),require('react-use'),require('@0xfutbol/constants'),require('thirdweb'),require('@matchain/matchid-sdk-react'),require('@matchain/matchid-sdk-react/index.css'),require('react-dom'),require('./decimals-DNF80AHH.js');/**
* Sends a transaction using the provided wallet.
* @param options - The options for sending the transaction.
* @returns A promise that resolves to the confirmed transaction receipt.
* @throws An error if the wallet is not connected.
* @transaction
* @example
*
* ### Basic usage
* ```ts
* import { sendAndConfirmTransaction } from "thirdweb";
*
* const transactionReceipt = await sendAndConfirmTransaction({
* account,
* transaction
* });
* ```
*
* ### Gasless usage with [thirdweb Engine](https://portal.thirdweb.com/engine)
* ```ts
* const transactionReceipt = await sendAndConfirmTransaction({
* account,
* transaction,
* gasless: {
* provider: "engine",
* relayerUrl: "https://thirdweb.engine-***.thirdweb.com/relayer/***",
* relayerForwarderAddress: "0x...",
* }
* });
* ```
*
* ### Gasless usage with OpenZeppelin
* ```ts
* const transactionReceipt = await sendAndConfirmTransaction({
* account,
* transaction,
* gasless: {
* provider: "openzeppelin",
* relayerUrl: "https://...",
* relayerForwarderAddress: "0x...",
* }
* });
* ```
*/
async function sendAndConfirmTransaction(options) {
const submittedTx = await index.aD(options);
return index.aL(submittedTx);
}/**
* Hook to get a price quote for performing a "Buy with crypto" transaction that allows users to buy a token with another token - aka a swap.
*
* The price quote is an object of type [`BuyWithCryptoQuote`](https://portal.thirdweb.com/references/typescript/v5/BuyWithCryptoQuote).
* This quote contains the information about the purchase such as token amounts, processing fees, estimated time etc.
*
* This hook is a React Query wrapper of the [`getBuyWithCryptoQuote`](https://portal.thirdweb.com/references/typescript/v5/getBuyWithCryptoQuote) function.
* You can also use that function directly
*
* Once you have the quote, you can use the [`useSendTransaction`](https://portal.thirdweb.com/references/typescript/v5/useSendTransaction) function to send the purchase
* and [`useBuyWithCryptoStatus`](https://portal.thirdweb.com/references/typescript/v5/useBuyWithCryptoStatus) function to get the status of the swap transaction.
* @param params - object of type [`BuyWithCryptoQuoteQueryParams`](https://portal.thirdweb.com/references/typescript/v5/BuyWithCryptoQuoteQueryParams)
* @param queryParams - options to configure the react query
* @returns A React Query object which contains the data of type [`BuyWithCryptoQuote`](https://portal.thirdweb.com/references/typescript/v5/BuyWithCryptoQuote)
* @example
* ```tsx
* import { useBuyWithCryptoQuote, useBuyWithCryptoStatus, type BuyWithCryptoStatusQueryParams, useActiveAccount } from "thirdweb/react";
* import { sendTransaction } from 'thirdweb';
*
* function Component() {
* const buyWithCryptoQuoteQuery = useBuyWithCryptoQuote(swapParams);
* const [buyTxHash, setBuyTxHash] = useState<BuyWithCryptoStatusQueryParams | undefined>();
* const buyWithCryptoStatusQuery = useBuyWithCryptoStatus(buyTxHash ? {
* client,
* transactionHash: buyTxHash,
* }: undefined);
*
* async function handleBuyWithCrypto() {
* const account = useActiveAccount();
*
* // if approval is required
* if (buyWithCryptoQuoteQuery.data.approval) {
* const approveTx = await sendTransaction({
* transaction: swapQuote.data.approval,
* account: account,
* });
* await waitForApproval(approveTx);
* }
*
* // send the transaction to buy crypto
* // this promise is resolved when user confirms the transaction in the wallet and the transaction is sent to the blockchain
* const buyTx = await sendTransaction({
* transaction: swapQuote.data.transactionRequest,
* account: account,
* });
* await waitForApproval(buyTx);
*
* // set buyTx.transactionHash to poll the status of the swap transaction
* setBuyWithCryptoTx(buyTx.transactionHash);
* }
*
* return <button onClick={handleBuyWithCrypto}>Swap</button>
* }
* ```
* @buyCrypto
*/
function useBuyWithCryptoQuote(params, queryParams) {
return reactQuery.useQuery({
...queryParams,
queryKey: ["buyWithCryptoQuote", params],
refetchInterval: 20_000,
queryFn: () => {
if (!params) {
throw new Error("Swap params are required");
}
return index.aM(params);
},
enabled: !!params,
retry: false,
});
}/**
* Get a quote of type [`BuyWithFiatQuote`](https://portal.thirdweb.com/references/typescript/v5/BuyWithFiatQuote) to buy given token with fiat currency.
* This quote contains the information about the swap such as token amounts, processing fees, estimated time etc.
*
* ### Rendering the On-Ramp provider UI
* Once you have the `quote`, you can open the `quote.onRampLink` in a new tab - This will prompt the user to buy the token with fiat currency
*
* ### Determining the steps required
* If `quote.onRampToken.token` is same as `quote.toToken` ( same chain + same token address ) - This means that the token can be directly bought from the on-ramp provider.
* But if they are different, On-ramp provider will send the `quote.onRampToken` to the user's wallet address and a swap is required to swap it to the desired token onchain.
*
* You can use the [`isSwapRequiredPostOnramp`](https://portal.thirdweb.com/references/typescript/v5/isSwapRequiredPostOnramp) utility function to check if a swap is required after the on-ramp is done.
*
* ### Polling for the status
* Once you open the `quote.onRampLink` in a new tab, you can start polling for the status using [`getBuyWithFiatStatus`](https://portal.thirdweb.com/references/typescript/v5/getBuyWithFiatStatus) to get the status of the transaction.
*
* `getBuyWithFiatStatus` returns a status object of type [`BuyWithFiatStatus`](https://portal.thirdweb.com/references/typescript/v5/BuyWithFiatStatus).
*
* - If no swap is required - the status will become `"ON_RAMP_TRANSFER_COMPLETED"` once the on-ramp provider has sent the desired token to the user's wallet address. Once you receive this status, the process is complete.
* - If a swap is required - the status will become `"CRYPTO_SWAP_REQUIRED"` once the on-ramp provider has sent the tokens to the user's wallet address. Once you receive this status, you need to start the swap process.
*
* ### Swap Process
* On receiving the `"CRYPTO_SWAP_REQUIRED"` status, you can use the [`getPostOnRampQuote`](https://portal.thirdweb.com/references/typescript/v5/getPostOnRampQuote) function to get the quote for the swap of type [`BuyWithCryptoQuote`](https://portal.thirdweb.com/references/typescript/v5/BuyWithCryptoQuote).
*
* Once you have this quote - You can follow the same steps as mentioned in the [`getBuyWithCryptoQuote`](https://portal.thirdweb.com/references/typescript/v5/getBuyWithCryptoQuote) documentation to perform the swap.
*
* @param params - object of type [`GetBuyWithFiatQuoteParams`](https://portal.thirdweb.com/references/typescript/v5/GetBuyWithFiatQuoteParams)
* @returns Object of type [`BuyWithFiatQuote`](https://portal.thirdweb.com/references/typescript/v5/BuyWithFiatQuote) which contains the information about the quote such as processing fees, estimated time, converted token amounts, etc.
* @example
* Get a quote for buying 10 USDC on polygon chain (chainId: 137) with USD fiat currency:
*
* ```ts
* import { getBuyWithFiatQuote } from "thirdweb/pay";
*
* const quote = await getBuyWithFiatQuote({
* client: client, // thirdweb client
* fromCurrencySymbol: "USD", // fiat currency symbol
* toChainId: 137, // polygon chain id
* toAmount: "10", // amount of USDC to buy
* toTokenAddress: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359" // USDC token address in polygon chain
* toAddress: "0x...", // user's wallet address
* isTestMode: false, // whether to use onramp in test mode for testing purpose (defaults to false)
* });
*
* window.open(quote.onRampLink, "_blank");
* ```
* @buyCrypto
*/
async function getBuyWithFiatQuote(params) {
try {
const clientFetch = index.g(params.client);
const response = await clientFetch(index.aN(), {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: index.s({
toAddress: params.toAddress,
fromCurrencySymbol: params.fromCurrencySymbol,
toChainId: params.toChainId.toString(),
toTokenAddress: params.toTokenAddress,
fromAmount: params.fromAmount,
toAmount: params.toAmount,
maxSlippageBPS: params.maxSlippageBPS,
isTestMode: params.isTestMode,
purchaseData: params.purchaseData,
fromAddress: params.fromAddress,
toGasAmountWei: params.toGasAmountWei,
preferredProvider: params.preferredProvider,
multiHopSupported: true,
}),
});
// Assuming the response directly matches the SwapResponse interface
if (!response.ok) {
const errorObj = await response.json();
if (errorObj && "error" in errorObj) {
throw errorObj;
}
throw new Error(`HTTP error! status: ${response.status}`);
}
return (await response.json()).result;
}
catch (error) {
console.error("Error getting buy with fiat quote", error);
throw error;
}
}/**
* Hook to get a price quote for performing a "Buy with Fiat" transaction that allows users to buy a token with fiat currency.
*
* The price quote is an object of type [`BuyWithFiatQuote`](https://portal.thirdweb.com/references/typescript/v5/BuyWithFiatQuote).
* This quote contains the information about the purchase such as token amounts, processing fees, estimated time etc.
*
* This hook is a React Query wrapper of the [`getBuyWithFiatQuote`](https://portal.thirdweb.com/references/typescript/v5/getBuyWithFiatQuote) function.
* You can also use that function directly
*
* Once you have the `quote`, you can open a new window with `quote.onRampLink` to allow the user to buy the token with fiat currency.
* and [`useBuyWithFiatStatus`](https://portal.thirdweb.com/references/typescript/v5/useBuyWithFiatStatus) function to start polling for the status of this transaction.
*
* @param params - object of type [`GetBuyWithFiatQuoteParams`](https://portal.thirdweb.com/references/typescript/v5/GetBuyWithFiatQuoteParams)
* @param queryParams - options to configure the react query
* @returns A React Query object which contains the data of type [`BuyWithFiatQuote`](https://portal.thirdweb.com/references/typescript/v5/BuyWithFiatQuote)
* @example
* ```ts
* import { NATIVE_TOKEN_ADDRESS } from "thirdweb";
* import { base } from "thirdweb/chains";
* import { useBuyWithFiatQuote } from "thirdweb/react";
*
* // get a quote for buying 0.01 base native token with USD fiat currency
* function Example() {
* const quote = useBuyWithFiatQuote({
* client: client, // thirdweb client
* fromCurrencySymbol: "USD", // fiat currency symbol
* toChainId: base.id, // base chain id
* toAmount: "0.01", // amount of token to buy
* toTokenAddress: NATIVE_TOKEN_ADDRESS, // native token
* toAddress: "0x...", // user's wallet address
* });
*
* return (
* <div>
* {quote.data && (
* <a href={quote.data.onRampLink} target="_blank">
* open onramp provider
* </a>
* )}
* </div>
* );
* }
* ```
* @buyCrypto
*/
function useBuyWithFiatQuote(params, queryOptions) {
return reactQuery.useQuery({
...queryOptions,
queryKey: ["useBuyWithFiatQuote", params],
queryFn: async () => {
if (!params) {
throw new Error("No params provided");
}
return getBuyWithFiatQuote(params);
},
enabled: !!params,
retry: false,
});
}function DirectPaymentModeScreen(props) {
const { payUiOptions, supportedDestinations, client, onContinue, payerAccount, } = props;
const theme = index.aO();
const activeWallet = index.aP();
const metadata = payUiOptions.metadata;
const paymentInfo = payUiOptions.paymentInfo;
const { data: chainData } = index.aQ(paymentInfo.chain);
const { data: sellerEns } = index.aR({
client,
address: paymentInfo.sellerAddress,
});
const totalCostQuery = reactQuery.useQuery({
queryKey: ["amount", paymentInfo],
queryFn: async () => {
let tokenDecimals = 18;
if (paymentInfo.token && !index.aS(paymentInfo.token)) {
tokenDecimals = await decimals.decimals({
contract: index.at({
address: paymentInfo.token.address,
chain: paymentInfo.chain,
client,
}),
});
}
let cost;
if ("amountWei" in paymentInfo) {
cost = index.Q(paymentInfo.amountWei, tokenDecimals);
}
else {
cost = paymentInfo.amount;
}
return cost;
},
});
const totalCost = totalCostQuery.data;
if (!chainData || totalCost === undefined) {
return jsxRuntime.jsx(index.aT, {});
}
const token = paymentInfo.token
? {
...paymentInfo.token,
icon: paymentInfo.token?.icon ||
supportedDestinations
.find((c) => c.chain.id === paymentInfo.chain.id)
?.tokens.find((t) => t.address.toLowerCase() ===
paymentInfo.token?.address.toLowerCase())?.icon,
}
: {
address: index.b2,
name: chainData.nativeCurrency.name,
symbol: chainData.nativeCurrency.symbol,
icon: chainData.icon?.url,
};
return (jsxRuntime.jsxs(index.aU, { p: "lg", children: [jsxRuntime.jsx(index.aV, { title: metadata?.name || "Payment Details" }), jsxRuntime.jsx(index.aW, { y: "lg" }), jsxRuntime.jsxs(index.aU, { children: [metadata?.image ? (jsxRuntime.jsx(index.aX, { client: client, src: metadata?.image, style: {
width: "100%",
borderRadius: index.aY.md,
backgroundColor: theme.colors.tertiaryBg,
} })) : activeWallet ? (jsxRuntime.jsxs(index.aU, { flex: "row", center: "both", style: {
padding: index.aY.md,
marginBottom: index.aY.md,
borderRadius: index.aY.md,
backgroundColor: theme.colors.tertiaryBg,
}, children: [jsxRuntime.jsx(index.aZ, { size: index.a_.xl, id: activeWallet.id, client: client }), jsxRuntime.jsx("div", { style: {
flexGrow: 1,
borderBottom: "6px dotted",
borderColor: theme.colors.secondaryIconColor,
marginLeft: index.aY.md,
marginRight: index.aY.md,
} }), jsxRuntime.jsx(index.a$, { client: client, size: index.a_.xl, chainIconUrl: chainData.icon?.url })] })) : null, jsxRuntime.jsx(index.aW, { y: "md" }), jsxRuntime.jsxs(index.aU, { flex: "row", children: [jsxRuntime.jsx(index.aU, { flex: "column", expand: true, children: jsxRuntime.jsx(index.b0, { size: "md", color: "primaryText", weight: 700, children: "Price" }) }), jsxRuntime.jsx(index.aU, { expand: true, children: jsxRuntime.jsxs(index.aU, { flex: "row", gap: "xs", center: "y", style: { justifyContent: "right" }, children: [jsxRuntime.jsx(index.b1, { chain: paymentInfo.chain, client: props.client, size: "sm", token: token }), jsxRuntime.jsxs(index.b0, { color: "primaryText", size: "md", weight: 700, children: [String(index.b3(Number(totalCost), 6)), " ", token.symbol] })] }) })] }), jsxRuntime.jsx(index.aW, { y: "md" }), jsxRuntime.jsx(index.b4, {}), jsxRuntime.jsx(index.aW, { y: "md" }), jsxRuntime.jsxs(index.aU, { flex: "row", children: [jsxRuntime.jsx(index.aU, { flex: "column", expand: true, children: jsxRuntime.jsx(index.b0, { size: "xs", color: "secondaryText", children: "Network" }) }), jsxRuntime.jsx(index.aU, { expand: true, children: jsxRuntime.jsxs(index.aU, { flex: "row", gap: "xs", center: "y", style: { justifyContent: "right" }, children: [jsxRuntime.jsx(index.a$, { chainIconUrl: chainData.icon?.url, size: "xs", client: props.client }), jsxRuntime.jsx(index.b0, { size: "xs", color: "secondaryText", style: { textAlign: "right" }, children: chainData.name })] }) })] }), jsxRuntime.jsx(index.aW, { y: "sm" }), jsxRuntime.jsxs(index.aU, { flex: "row", children: [jsxRuntime.jsx(index.aU, { flex: "column", expand: true, children: jsxRuntime.jsx(index.b0, { size: "xs", color: "secondaryText", children: "Seller" }) }), jsxRuntime.jsx(index.aU, { expand: true, children: jsxRuntime.jsx(index.aU, { flex: "row", gap: "xs", center: "y", style: { justifyContent: "right" }, children: jsxRuntime.jsx(index.b0, { size: "xs", color: "secondaryText", style: { textAlign: "right" }, children: sellerEns || index.b5(paymentInfo.sellerAddress) }) }) })] })] }), jsxRuntime.jsx(index.aW, { y: "xl" }), payerAccount ? (jsxRuntime.jsx(index.b6, { variant: "accent", fullWidth: true, onClick: () => {
index.b7({
event: "choose_payment_method_direct_payment_mode",
client,
walletAddress: payerAccount.address,
walletType: activeWallet?.id,
});
onContinue(totalCost, paymentInfo.chain, token);
}, children: "Choose Payment Method" })) : (jsxRuntime.jsx("div", { children: jsxRuntime.jsx(index.b8, { ...props.connectOptions, client: client, theme: theme, connectButton: {
style: {
width: "100%",
},
} }) }))] }));
}function useTransactionCostAndData(args) {
const { transaction, account, supportedDestinations } = args;
// Compute query key of the transaction first
const [txQueryKey, setTxQueryKey] = React.useState();
React.useEffect(() => {
Promise.all([
index.ap(transaction.value),
index.ap(transaction.erc20Value),
index.ap(transaction.to),
index.ao(transaction),
]).then(([value, erc20Value, to, data]) => {
setTxQueryKey({
value: value?.toString(),
erc20Value: erc20Value?.amountWei?.toString(),
erc20Currency: erc20Value?.tokenAddress,
to,
data,
});
});
}, [transaction]);
return reactQuery.useQuery({
queryKey: [
"transaction-cost",
transaction.chain.id,
account?.address,
txQueryKey,
],
queryFn: async () => {
if (!account) {
throw new Error("No payer account found");
}
const erc20Value = await index.ap(transaction.erc20Value);
if (erc20Value) {
const [tokenBalance, tokenMeta, gasCostWei] = await Promise.all([
index.b9({
address: account.address,
chain: transaction.chain,
client: transaction.client,
tokenAddress: erc20Value.tokenAddress,
}),
getCurrencyMetadata.getCurrencyMetadata({
contract: index.at({
address: erc20Value.tokenAddress,
chain: transaction.chain,
client: transaction.client,
}),
}),
index.ba(transaction, account?.address),
]);
const transactionValueWei = erc20Value.amountWei;
const walletBalance = tokenBalance;
const currency = {
address: erc20Value.tokenAddress,
name: tokenMeta.name,
symbol: tokenMeta.symbol,
icon: supportedDestinations
.find((c) => c.chain.id === transaction.chain.id)
?.tokens.find((t) => t.address.toLowerCase() ===
erc20Value.tokenAddress.toLowerCase())?.icon,
};
return {
token: currency,
decimals: tokenMeta.decimals,
walletBalance,
gasCostWei,
transactionValueWei,
};
}
const [nativeWalletBalance, chainMetadata, gasCostWei] = await Promise.all([
index.b9({
address: account.address,
chain: transaction.chain,
client: transaction.client,
}),
index.F(transaction.chain),
index.ba(transaction, account?.address),
]);
const walletBalance = nativeWalletBalance;
const transactionValueWei = (await index.ap(transaction.value)) || 0n;
return {
token: {
address: index.b2,
name: chainMetadata.nativeCurrency.name,
symbol: chainMetadata.nativeCurrency.symbol,
icon: chainMetadata.icon?.url,
},
decimals: 18,
walletBalance,
gasCostWei,
transactionValueWei,
};
},
enabled: !!transaction && !!txQueryKey,
refetchInterval: () => {
if (transaction.erc20Value) {
// if erc20 value is set, we don't need to poll
return undefined;
}
return 30_000;
},
});
}function TransactionModeScreen(props) {
const { payUiOptions, client, payerAccount, supportedDestinations, onContinue, } = props;
const { data: chainData, error: chainDataError, isLoading: chainDataLoading, refetch: chainDataRefetch, } = index.aQ(payUiOptions.transaction.chain);
const metadata = payUiOptions.metadata;
const { data: transactionCostAndData, error: transactionCostAndDataError, isLoading: transactionCostAndDataLoading, refetch: transactionCostAndDataRefetch, } = useTransactionCostAndData({
transaction: payUiOptions.transaction,
account: payerAccount,
supportedDestinations,
});
const theme = index.aO();
const activeWallet = index.aP();
const activeAccount = index.bb();
const sponsoredTransactionsEnabled = index.bc(activeWallet);
const balanceQuery = index.bd({
address: activeAccount?.address,
chain: payUiOptions.transaction.chain,
tokenAddress: index.aS(transactionCostAndData?.token || index.be)
? undefined
: transactionCostAndData?.token.address,
client: props.client,
}, {
enabled: !!transactionCostAndData,
});
if (transactionCostAndDataLoading || chainDataLoading) {
return jsxRuntime.jsx(index.aT, {});
}
if (!activeAccount) {
return (jsxRuntime.jsx(index.aU, { style: {
minHeight: "350px",
}, fullHeight: true, flex: "row", center: "both", children: jsxRuntime.jsxs(index.aU, { animate: "fadein", children: [jsxRuntime.jsx(index.aW, { y: "xxl" }), jsxRuntime.jsx(index.aU, { flex: "row", center: "x", children: jsxRuntime.jsx(index.bf, { size: index.a_["3xl"] }) }), jsxRuntime.jsx(index.aW, { y: "lg" }), jsxRuntime.jsx(index.b0, { center: true, color: "primaryText", size: "md", children: "Please connect a wallet to continue" }), jsxRuntime.jsx(index.aW, { y: "xl" }), jsxRuntime.jsx(index.aU, { flex: "row", center: "x", style: { width: "100%" }, children: jsxRuntime.jsx(index.b8, { client: client, theme: theme, ...props.connectOptions }) })] }) }));
}
if (transactionCostAndDataError || chainDataError) {
return (jsxRuntime.jsx(index.aU, { style: {
minHeight: "350px",
}, fullHeight: true, flex: "row", center: "both", children: jsxRuntime.jsx(index.bg, { title: transactionCostAndDataError?.message ||
chainDataError?.message ||
"Something went wrong", onTryAgain: transactionCostAndDataError
? transactionCostAndDataRefetch
: chainDataRefetch }) }));
}
if (!transactionCostAndData || !chainData) {
return jsxRuntime.jsx(index.aT, {});
}
const insufficientFunds = balanceQuery.data &&
balanceQuery.data.value < transactionCostAndData.transactionValueWei;
return (jsxRuntime.jsxs(index.aU, { p: "lg", children: [jsxRuntime.jsx(index.aV, { title: metadata?.name || "Transaction" }), jsxRuntime.jsx(index.aW, { y: "lg" }), jsxRuntime.jsxs(index.aU, { children: [metadata?.image ? (jsxRuntime.jsx(index.aX, { client: client, src: metadata?.image, style: {
width: "100%",
borderRadius: index.aY.md,
border: `1px solid ${theme.colors.borderColor}`,
backgroundColor: theme.colors.tertiaryBg,
} })) : activeAccount ? (jsxRuntime.jsxs(index.aU, { flex: "column", gap: "sm", children: [insufficientFunds && (jsxRuntime.jsx(index.b0, { size: "sm", color: "danger", style: { textAlign: "center" }, children: "Insufficient funds" })), jsxRuntime.jsxs(index.aU, { flex: "row", style: {
justifyContent: "space-between",
padding: index.aY.sm,
marginBottom: index.aY.sm,
borderRadius: index.aY.md,
backgroundColor: theme.colors.tertiaryBg,
border: `1px solid ${theme.colors.borderColor}`,
}, children: [jsxRuntime.jsx(index.bh, { address: activeAccount?.address, iconSize: "md", client: client }), balanceQuery.data ? (jsxRuntime.jsxs(index.aU, { flex: "row", gap: "3xs", center: "y", children: [jsxRuntime.jsx(index.b0, { size: "xs", color: "secondaryText", weight: 500, children: index.bi(balanceQuery.data, false) }), jsxRuntime.jsx(index.bj, { token: transactionCostAndData.token, chain: payUiOptions.transaction.chain, size: "xs", color: "secondaryText" })] })) : (jsxRuntime.jsx(index.bk, { width: "70px", height: index.bl.xs }))] })] })) : null, jsxRuntime.jsx(index.aW, { y: "md" }), jsxRuntime.jsxs(index.aU, { flex: "row", children: [jsxRuntime.jsx(index.aU, { flex: "column", expand: true, children: jsxRuntime.jsx(index.b0, { size: "md", color: "primaryText", weight: 700, children: "Price" }) }), jsxRuntime.jsx(index.aU, { expand: true, children: jsxRuntime.jsxs(index.aU, { flex: "row", gap: "xs", center: "y", style: { justifyContent: "right" }, children: [jsxRuntime.jsx(index.b1, { chain: payUiOptions.transaction.chain, client: props.client, size: "sm", token: transactionCostAndData.token }), jsxRuntime.jsxs(index.b0, { color: "primaryText", size: "md", weight: 700, children: [String(index.b3(Number(index.Q(transactionCostAndData.transactionValueWei, transactionCostAndData.decimals)), 6)), " ", transactionCostAndData.token.symbol] })] }) })] }), jsxRuntime.jsx(index.aW, { y: "md" }), jsxRuntime.jsx(index.b4, {}), jsxRuntime.jsx(index.aW, { y: "md" }), jsxRuntime.jsxs(index.aU, { flex: "row", children: [jsxRuntime.jsx(index.aU, { flex: "column", expand: true, children: jsxRuntime.jsx(index.b0, { size: "xs", color: "secondaryText", children: "Gas Fees" }) }), jsxRuntime.jsx(index.aU, { expand: true, children: jsxRuntime.jsx(index.aU, { flex: "row", gap: "xs", center: "y", style: { justifyContent: "right" }, children: jsxRuntime.jsx(index.b0, { color: sponsoredTransactionsEnabled ? "success" : "primaryText", size: "xs", children: sponsoredTransactionsEnabled
? "Sponsored"
: `${String(index.b3(Number(index.Q(transactionCostAndData.gasCostWei, chainData.nativeCurrency.decimals)), 6))} ${chainData.nativeCurrency.symbol}` }) }) })] }), jsxRuntime.jsx(index.aW, { y: "sm" }), jsxRuntime.jsxs(index.aU, { flex: "row", children: [jsxRuntime.jsx(index.aU, { flex: "column", expand: true, children: jsxRuntime.jsx(index.b0, { size: "xs", color: "secondaryText", children: "Network" }) }), jsxRuntime.jsx(index.aU, { expand: true, children: jsxRuntime.jsxs(index.aU, { flex: "row", gap: "xs", center: "y", style: { justifyContent: "right" }, children: [jsxRuntime.jsx(index.a$, { chainIconUrl: chainData.icon?.url, size: "xs", client: props.client }), jsxRuntime.jsx(index.b0, { size: "xs", color: "secondaryText", style: { textAlign: "right" }, children: chainData.name })] }) })] })] }), jsxRuntime.jsx(index.aW, { y: "xl" }), payerAccount ? (jsxRuntime.jsx(index.b6, { variant: "accent", fullWidth: true, onClick: () => {
let totalCostWei = insufficientFunds
? transactionCostAndData.transactionValueWei -
(balanceQuery.data?.value || 0n)
: transactionCostAndData.transactionValueWei;
if (transactionCostAndData.token.address === index.b2 &&
!sponsoredTransactionsEnabled) {
totalCostWei += transactionCostAndData.gasCostWei;
}
index.b7({
event: "choose_payment_method_transaction_mode",
client,
walletAddress: payerAccount.address,
walletType: activeWallet?.id,
});
onContinue(index.Q(totalCostWei, transactionCostAndData.decimals), payUiOptions.transaction.chain, transactionCostAndData.token);
}, children: "Choose Payment Method" })) : (jsxRuntime.jsx("div", { children: jsxRuntime.jsx(index.b8, { ...props.connectOptions, client: client, theme: theme, connectButton: {
style: {
width: "100%",
},
} }) }))] }));
}function CurrencySelection(props) {
return (jsxRuntime.jsxs(index.aU, { children: [jsxRuntime.jsx(index.aU, { p: "lg", children: jsxRuntime.jsx(index.aV, { title: "Pay with", onBack: props.onBack }) }), jsxRuntime.jsx(index.b4, {}), jsxRuntime.jsx(index.aW, { y: "lg" }), jsxRuntime.jsx(index.aU, { flex: "column", gap: "xs", px: "lg", children: index.bm.map((c) => {
return (jsxRuntime.jsxs(SelectCurrencyButton, { fullWidth: true, variant: "secondary", onClick: () => props.onSelect(c), gap: "sm", children: [index.bo(c, "lg"), jsxRuntime.jsxs(index.aU, { flex: "column", gap: "xxs", children: [jsxRuntime.jsx(index.b0, { color: "primaryText", children: c.shorthand }), jsxRuntime.jsx(index.b0, { size: "sm", children: c.name })] })] }, c.shorthand));
}) }), jsxRuntime.jsx(index.aW, { y: "lg" })] }));
}
const SelectCurrencyButton = /* @__PURE__ */ index.bn(index.b6)(() => {
const theme = index.aO();
return {
background: theme.colors.tertiaryBg,
justifyContent: "flex-start",
gap: index.aY.sm,
padding: index.aY.sm,
"&:hover": {
background: theme.colors.secondaryButtonBg,
transform: "scale(1.01)",
},
transition: "background 200ms ease, transform 150ms ease",
};
});const defaultMessage = "Unable to get price quote";
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
function getErrorMessage(err) {
if (typeof err.error === "object" && err.error.code) {
if (err.error.code === "MINIMUM_PURCHASE_AMOUNT") {
return {
code: "MINIMUM_PURCHASE_AMOUNT",
message: "Purchase amount is too low",
};
}
}
return {
code: "UNABLE_TO_GET_PRICE_QUOTE",
message: defaultMessage,
};
}/**
*
* @internal
*/
const Drawer = /* @__PURE__ */ React.forwardRef(function Drawer_(props, ref) {
return (jsxRuntime.jsx(DrawerContainer, { ref: ref, children: jsxRuntime.jsx(index.br, { children: jsxRuntime.jsxs(index.aU, { p: "lg", children: [jsxRuntime.jsx(index.bs, { children: jsxRuntime.jsx(index.bt, { type: "button", "aria-label": "Close", onClick: props.close, children: jsxRuntime.jsx(index.bu, { width: index.a_.md, height: index.a_.md, style: {
color: "inherit",
} }) }) }), props.children] }) }) }));
});
const DrawerContainer = /* @__PURE__ */ index.bp((_) => {
const theme = index.aO();
return {
zIndex: 10000,
borderTopLeftRadius: index.bv.xl,
borderTopRightRadius: index.bv.xl,
background: theme.colors.modalBg,
position: "absolute",
bottom: 0,
left: 0,
right: 0,
animation: `${drawerOpenAnimation} 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.1)`,
borderTop: `1px solid ${theme.colors.borderColor}`,
};
});
const drawerOpenAnimation = index.bw `
from {
opacity: 0;
transform: translateY(100px);
}
to {
opacity: 1;
transform: translateY(0);
}
`;
const DrawerOverlay = /* @__PURE__ */ index.bp((_) => {
const theme = index.aO();
return {
backgroundColor: theme.colors.modalOverlayBg,
zIndex: 9999,
position: "absolute",
inset: 0,
animation: `${index.bq} 400ms cubic-bezier(0.16, 1, 0.3, 1)`,
};
});
/**
*
* @internal
*/
function useDrawer() {
const [isOpen, _setIsOpen] = React.useState(false);
const drawerRef = React.useRef(null);
const drawerOverlayRef = React.useRef(null);
const closeDrawerAnimation = React.useCallback(() => {
return new Promise((resolve) => {
if (drawerRef.current) {
const animOptions = {
easing: "cubic-bezier(0.175, 0.885, 0.32, 1.1)",
fill: "forwards",
duration: 300,
};
const closeAnimation = drawerRef.current.animate([{ transform: "translateY(100%)", opacity: 0 }], animOptions);
drawerOverlayRef.current?.animate([{ opacity: 0 }], animOptions);
closeAnimation.onfinish = () => resolve();
}
else {
resolve();
}
});
}, []);
const setIsOpen = React.useCallback(async (value) => {
if (value) {
_setIsOpen(true);
}
else {
await closeDrawerAnimation();
_setIsOpen(false);
}
}, [closeDrawerAnimation]);
// close on outside click
React.useLayoutEffect(() => {
if (!isOpen) {
return;
}
const handleClick = (event) => {
if (drawerRef.current &&
event.target instanceof Node &&
!drawerRef.current.contains(event.target)) {
setIsOpen(false);
}
};
// avoid listening to the click event that opened the drawer by adding a frame delay
requestAnimationFrame(() => {
document.addEventListener("click", handleClick);
});
return () => {
document.removeEventListener("click", handleClick);
};
}, [isOpen, setIsOpen]);
return {
drawerRef,
drawerOverlayRef,
setIsOpen,
isOpen,
};
}/**
* @internal
*/
function formatSeconds(seconds) {
// hours and minutes
if (seconds > 3600) {
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
return `${hours} Hours ${minutes} Minutes`;
}
// minutes only
if (seconds > 60) {
const minutes = Math.ceil(seconds / 60);
return `${minutes} Minutes`;
}
return `${seconds}s`;
}function EstimatedTimeAndFees(props) {
const { estimatedSeconds, quoteIsLoading } = props;
return (jsxRuntime.jsxs(index.aU, { bg: "tertiaryBg", flex: "row", borderColor: "borderColor", style: {
borderRadius: index.bv.md,
borderTopLeftRadius: 0,
borderTopRightRadius: 0,
justifyContent: "space-between",
alignItems: "center",
borderWidth: "1px",
borderStyle: "solid",
}, children: [jsxRuntime.jsxs(index.aU, { flex: "row", center: "y", gap: "xxs", color: "accentText", p: "sm", children: [jsxRuntime.jsx(index.bx, { width: index.a_.sm, height: index.a_.sm }), quoteIsLoading ? (jsxRuntime.jsx(index.bk, { height: index.bl.xs, width: "50px", color: "borderColor" })) : (jsxRuntime.jsx(index.b0, { size: "xs", color: "secondaryText", children: estimatedSeconds !== undefined
? `~${formatSeconds(estimatedSeconds)}`
: "--" }))] }), jsxRuntime.jsxs(index.b6, { variant: "ghost", onClick: props.onViewFees, gap: "xs", children: [jsxRuntime.jsx(index.aU, { color: "accentText", flex: "row", center: "both", children: jsxRuntime.jsx(ViewFeeIcon, { size: index.a_.sm }) }), jsxRuntime.jsx(index.b0, { size: "xs", color: "secondaryText", children: "View Fees" })] })] }));
}
const ViewFeeIcon = (props) => {
return (jsxRuntime.jsxs("svg", { width: props.size, height: props.size, viewBox: "0 0 12 12", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", children: [jsxRuntime.jsx("path", { d: "M9.5 1.5H2.5C1.94772 1.5 1.5 1.94772 1.5 2.5V9.5C1.5 10.0523 1.94772 10.5 2.5 10.5H9.5C10.0523 10.5 10.5 10.0523 10.5 9.5V2.5C10.5 1.94772 10.0523 1.5 9.5 1.5Z", stroke: "currentColor", strokeLinecap: "round", strokeLinejoin: "round" }), jsxRuntime.jsx("path", { d: "M4.5 7.5L7.5 4.5", stroke: "currentColor", strokeLinecap: "round", strokeLinejoin: "round" })] }));
};/**
* Shows an amount "value" and renders the selected token and chain
* It also renders the buttons to select the token and chain
* It also renders the balance of active wallet for the selected token in selected chain
* @internal
*/
function PayWithCreditCard(props) {
return (jsxRuntime.jsxs(index.aU, { bg: "tertiaryBg", borderColor: "borderColor", flex: "row", style: {
borderRadius: index.bv.md,
borderBottomRightRadius: 0,
borderBottomLeftRadius: 0,
borderWidth: "1px",
borderStyle: "solid",
borderBottom: "none",
flexWrap: "nowrap",
justifyContent: "space-between",
alignItems: "center",
}, children: [jsxRuntime.jsxs(CurrencyButton, { variant: "ghost", onClick: props.onSelectCurrency, style: {
minHeight: "64px",
justifyContent: "flex-start",
minWidth: "50%",
}, gap: "sm", children: [index.bo(props.currency, "md"), jsxRuntime.jsxs(index.aU, { flex: "row", center: "y", gap: "xxs", color: "secondaryText", children: [jsxRuntime.jsx(index.b0, { color: "primaryText", children: props.currency.shorthand }), jsxRuntime.jsx(index.by, { width: index.a_.sm, height: index.a_.sm })] })] }), jsxRuntime.jsx("div", { style: {
flexGrow: 1,
flexShrink: 1,
display: "flex",
flexDirection: "column",
alignItems: "flex-end",
gap: index.aY.xxs,
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
justifyContent: "center",
paddingRight: index.aY.sm,
}, children: props.isLoading ? (jsxRuntime.jsx(index.bk, { width: "100px", height: index.bl.lg })) : (jsxRuntime.jsx(index.b0, { size: "lg", color: props.value ? "primaryText" : "secondaryText", children: props.value
? `${props.currency.symbol}${index.b3(Number(props.value), 6)}`
: "--" })) })] }));
}
const CurrencyButton = /* @__PURE__ */ index.bn(index.b6)(() => {
return {
"&[disabled]:hover": {
borderColor: "transparent",
},
};
});/**
* @internal
*/
function SwapFees(props) {
return (jsxRuntime.jsx(index.aU, { flex: "column", gap: "xs", style: {
alignItems: "flex-start",
}, children: props.quote.processingFees.map((fee) => {
const feeAmount = index.b3(Number(fee.amount), 6);
return (jsxRuntime.jsxs(index.aU, { flex: "row", gap: "xxs", children: [jsxRuntime.jsxs(index.b0, { color: "primaryText", size: "sm", children: [feeAmount === 0 ? "~" : "", feeAmount, " ", fee.token.symbol] }), jsxRuntime.jsxs(index.b0, { color: "secondaryText", size: "sm", children: ["($", (fee.amountUSDCents / 100).toFixed(2), ")"] })] }, `${fee.token.chainId}_${fee.token.tokenAddress}_${feeAmount}`));
}) }));
}
/**
* @internal
*/
function FiatFees(props) {
return (jsxRuntime.jsxs(index.aU, { flex: "column", gap: "xs", children: [jsxRuntime.jsxs("div", { style: {
display: "flex",
justifyContent: "space-between",
}, children: [jsxRuntime.jsx(index.b0, { inline: true, color: "secondaryText", children: "Amount" }), jsxRuntime.jsxs(index.b0, { color: "primaryText", inline: true, children: [index.b3(Number(props.quote.fromCurrency.amount), 2), " ", props.quote.fromCurrency.currencySymbol] })] }), props.quote.processingFees.map((fee, i) => {
const feeAmount = index.b3(Number(fee.amount), 6);
return (jsxRuntime.jsxs("div", { style: {
display: "flex",
justifyContent: "space-between",
}, children: [jsxRuntime.jsx(index.b0, { inline: true, color: "secondaryText", children: fee.feeType === "NETWORK" ? "Network Fee" : "Processing Fee" }), jsxRuntime.jsxs(index.b0, { color: "primaryText", inline: true, children: [feeAmount === 0 ? "~" : "", " ", feeAmount, " ", fee.currencySymbol] })] }, i));
}), jsxRuntime.jsx(index.aW, { y: "xxs" }), jsxRuntime.jsx(index.b4, {}), jsxRuntime.jsx(index.aW, { y: "xxs" }), jsxRuntime.jsxs("div", { style: {
display: "flex",
justifyContent: "space-between",
}, children: [jsxRuntime.jsx(index.b0, { inline: true, color: "secondaryText", children: "Total" }), jsxRuntime.jsxs(index.b0, { color: "primaryText", inline: true, children: [index.b3(Number(props.quote.fromCurrencyWithFees.amount), 6), " ", props.quote.fromCurrencyWithFees.currencySymbol] })] })] }));
}const FiatProviders = ["COINBASE", "STRIPE", "TRANSAK", "KADO"];/**
* @internal
*/
function Providers(props) {
return (jsxRuntime.jsx(index.aU, { expand: true, flex: "column", gap: "sm", style: {
alignItems: "flex-start",
}, children: FiatProviders.map((provider) => {
return (jsxRuntime.jsx(index.aU, { flex: "row", expand: true, style: {
justifyContent: "space-between",
}, children: jsxRuntime.jsx(index.b6, { fullWidth: true, onClick: () => props.onSelect(provider), variant: "link", children: jsxRuntime.jsx(index.bz, { color: props.preferredProvider === provider
? "primaryText"
: "secondaryText", size: "sm", hoverColor: "primaryText", children: provider.charAt(0).toUpperCase() +
provider.slice(1).toLowerCase() }) }) }, provider));
}) }));
}function FiatScreenContent(props) {
const { toToken, tokenAmount, payer, client, setScreen, toChain, showCurrencySelector, selectedCurrency, } = props;
const defaultRecipientAddress = props.payOptions?.paymentInfo?.sellerAddress;
const receiverAddress = defaultRecipientAddress || props.payer.account.address;
const { drawerRef, drawerOverlayRef, isOpen, setIsOpen } = useDrawer();
const [drawerScreen, setDrawerScreen] = React.useState("fees");
const buyWithFiatOptions = props.payOptions.buyWithFiat;
const [preferredProvider, setPreferredProvider] = React.useState(buyWithFiatOptions !== false
? buyWithFiatOptions?.preferredProvider ||
(localStorage.getItem(index.bA) ??
undefined)
: undefined);
const fiatQuoteQuery = useBuyWithFiatQuote(buyWithFiatOptions !== false && tokenAmount
? {
fromCurrencySymbol: selectedCurrency.shorthand,
toChainId: toChain.id,
toAddress: receiverAddress,
toTokenAddress: index.aS(toToken)
? index.b2
: toToken.address,
toAmount: tokenAmount,
client,
isTestMode: buyWithFiatOptions?.testMode,
purchaseData: props.payOptions.purchaseData,
fromAddress: payer.account.address,
preferredProvider: preferredProvider,
}
: undefined);
function handleSubmit() {
if (!fiatQuoteQuery.data) {
return;
}
setScreen({
id: "fiat-flow",
quote: fiatQuoteQuery.data,
});
}
function showFees() {
if (!fiatQuoteQuery.data) {
return;
}
setDrawerScreen("fees");
setIsOpen(true);
}
function showProviders() {
setDrawerScreen("providers");
setIsOpen(true);
}
const disableSubmit = !fiatQuoteQuery.data;
const errorMsg = !fiatQuoteQuery.isLoading && fiatQuoteQuery.error
? getErrorMessage(fiatQuoteQuery.error)
: undefined;
return (jsxRuntime.jsxs(index.aU, { flex: "column", gap: "lg", animate: "fadein", children: [isOpen && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(DrawerOverlay, { ref: drawerOverlayRef }), jsxRuntime.jsxs(Drawer, { ref: drawerRef, close: () => setIsOpen(false), children: [drawerScreen === "fees" && fiatQuoteQuery.data && (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx(index.b0, { size: "lg", color: "primaryText", children: "Fees" }), jsxRuntime.jsx(index.aW, { y: "lg" }), jsxRuntime.jsx(FiatFees, { quote: fiatQuoteQuery.data })] })), drawerScreen === "providers" && (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx(index.b0, { size: "lg", color: "primaryText", children: "Providers" }), jsxRuntime.jsx(index.aW, { y: "lg" }), jsxRuntime.jsx(Providers, { preferredProvider: preferredProvider || fiatQuoteQuery.data?.provider, onSelect: (provider) => {
setPreferredProvider(provider);
// save the pref in local storage
localStorage.setItem(index.bA, provider);
setIsOpen(false);
} })] }))] })] })), jsxRuntime.jsxs(index.aU, { flex: "column", gap: "sm", children: [jsxRuntime.jsx(index.b0, { size: "sm", children: "Pay with credit card" }), jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx(PayWithCreditCard, { isLoading: fiatQuoteQuery.isLoading, value: fiatQuoteQuery.data?.fromCurrencyWithFees.amount, client: client, currency: selectedCurrency, onSelectCurrency: showCurrencySelector }), jsxRuntime.jsxs(index.aU, { bg: "tertiaryBg", flex: "row", borderColor: "borderColor", style: {
paddingLeft: index.aY.md,
justifyContent: "space-between",
alignItems: "center",
borderWidth: "1px",
borderStyle: "solid",
borderBottom: "none",
}, children: [jsxRuntime.jsx(index.b0, { size: "xs", color: "secondaryText", children: "Provider" }), jsxRuntime.jsx(index.b6, { variant: "ghost", onClick: showProviders, children: jsxRuntime.jsxs(index.aU, { flex: "row", center: "y", gap: "xxs", color: "secondaryText", children: [jsxRuntime.jsx(index.b0, { size: "xs", children: preferredProvider
? `${preferredProvider.charAt(0).toUpperCase() + preferredProvider.slice(1).toLowerCase()}`
: fiatQuoteQuery.data?.provider
? `${fiatQuoteQuery.data?.provider.charAt(0).toUpperCase() + fiatQuoteQuery.data?.provider.slice(1).toLowerCase()}`
: "" }), jsxRuntime.jsx(index.by, { width: index.a_.sm, height: index.a_.sm })] }) })] }), jsxRuntime.jsx(EstimatedTimeAndFees, { quoteIsLoading: fiatQuoteQuery.isLoading, estimatedSeconds: fiatQuoteQuery.data?.estimatedDurationSeconds, onViewFees: showFees })