"use client"; import { type UseQueryOptions, useQuery } from "@tanstack/react-query"; import { Text, type TextProps } from "react-native"; import type { Chain } from "../../../../../chains/types.js"; import type { SupportedFiatCurrency } from "../../../../../pay/convert/type.js"; import { useActiveWalletChain } from "../../../../../react/core/hooks/wallets/useActiveWalletChain.js"; import { getAddress } from "../../../../../utils/address.js"; import { useAccountContext } from "../../../../core/account/provider.js"; import { type AccountBalanceInfo, formatAccountFiatBalance, formatAccountTokenBalance, loadAccountBalance, } from "../../../../core/utils/account.js"; /** * Props for the AccountBalance component * @component * @wallet */ export interface AccountBalanceProps extends Omit { /** * The network to fetch balance on * If not passed, the component will use the current chain that the wallet is connected to (`useActiveWalletChain()`) */ chain?: Chain; /** * By default this component will fetch the balance for the native token on a given chain * If you want to fetch balance for an ERC20 token, use the `tokenAddress` props */ tokenAddress?: string; /** * A function to format the balance's display value * use this function to transform the balance display value like round up the number * Particularly useful to avoid overflowing-UI issues */ formatFn?: (props: AccountBalanceInfo) => string; /** * This component will be shown while the balance of the account is being fetched * If not passed, the component will return `null`. * * You can/should pass a loading sign or spinner to this prop. * @example * ```tsx * } * /> * ``` */ loadingComponent?: React.ComponentType; /** * This component will be shown if the balance fails to be retreived * If not passed, the component will return `null`. * * You can/should pass a descriptive text/component to this prop, indicating that the * balance was not fetched successfully * @example * ```tsx * * ``` */ fallbackComponent?: React.ComponentType; /** * Optional `useQuery` params */ queryOptions?: Omit< UseQueryOptions, "queryFn" | "queryKey" >; /** * Show the token balance in a supported fiat currency (e.g "USD") */ showBalanceInFiat?: SupportedFiatCurrency; } /** * This component fetches and shows the balance of the wallet address on a given chain. * It inherits all the attributes of a HTML component, hence you can style it just like how you would style a normal * * * @example * ### Basic usage * ```tsx * import { AccountProvider, AccountBalance } from "thirdweb/react"; * import { ethereum } from "thirdweb/chains"; * * * * * ``` * Result: * ```html * 1.091435 ETH * ``` * * * ### Format the balance (round up, shorten etc.) * The AccountBalance component accepts a `formatFn` which takes in an object of type `AccountBalanceInfo` and outputs a string * The function is used to modify the display value of the wallet balance (either in crypto or fiat) * * ```tsx * import type { AccountBalanceInfo } from "thirdweb/react"; * import { formatNumber } from "thirdweb/utils"; * * const format = (props: AccountInfoBalance):string => `${formatNumber(props.balance, 1)} ${props.symbol.toLowerCase()}` * * * ``` * * Result: * ```html * 1.1 eth // the balance is rounded up to 1 decimal and the symbol is lowercased * ``` * * ### Show a loading sign when the balance is being fetched * ```tsx * import { AccountProvider, AccountBalance } from "thirdweb/react"; * * * } * /> * * ``` * * ### Fallback to something when the balance fails to resolve * ```tsx * * * * ``` * * ### Custom query options for useQuery * This component uses `@tanstack-query`'s useQuery internally. * You can use the `queryOptions` prop for more fine-grained control * ```tsx * * ``` * * @component * @wallet * @beta */ export function AccountBalance({ chain, tokenAddress, loadingComponent, fallbackComponent, queryOptions, formatFn, showBalanceInFiat, ...restProps }: AccountBalanceProps) { const { address, client } = useAccountContext(); const walletChain = useActiveWalletChain(); const chainToLoad = chain || walletChain; const balanceQuery = useQuery({ queryFn: async (): Promise => loadAccountBalance({ address: getAddress(address), chain: chainToLoad, client, showBalanceInFiat, tokenAddress: tokenAddress ? getAddress(tokenAddress) : undefined, }), queryKey: [ "internal_account_balance", chainToLoad?.id || -1, address, { tokenAddress }, showBalanceInFiat, ] as const, retry: false, ...queryOptions, }); if (balanceQuery.isLoading) { return loadingComponent || null; } if (balanceQuery.data === undefined) { return fallbackComponent || null; } // Prioritize using the formatFn from users if (formatFn) { return {formatFn(balanceQuery.data)}; } if (showBalanceInFiat) { return ( {formatAccountFiatBalance({ ...balanceQuery.data, decimals: 2 })} ); } return ( {formatAccountTokenBalance({ ...balanceQuery.data, decimals: balanceQuery.data.balance < 1 ? 3 : 2, })} ); }