@@ -4,13 +4,24 @@ import { type UseQueryOptions, useQuery } from "@tanstack/react-query";
44import type React from "react" ;
55import type { JSX } from "react" ;
66import type { Chain } from "../../../../../chains/types.js" ;
7+ import { NATIVE_TOKEN_ADDRESS } from "../../../../../constants/addresses.js" ;
8+ import { convertCryptoToFiat } from "../../../../../exports/pay.js" ;
79import { useActiveWalletChain } from "../../../../../react/core/hooks/wallets/useActiveWalletChain.js" ;
8- import {
9- type GetWalletBalanceResult ,
10- getWalletBalance ,
11- } from "../../../../../wallets/utils/getWalletBalance.js" ;
10+ import { formatNumber } from "../../../../../utils/formatNumber.js" ;
11+ import { shortenLargeNumber } from "../../../../../utils/shortenLargeNumber.js" ;
12+ import { getWalletBalance } from "../../../../../wallets/utils/getWalletBalance.js" ;
1213import { useAccountContext } from "./provider.js" ;
1314
15+ /**
16+ * @internal
17+ */
18+ export type AccountBalanceFormatParams = {
19+ tokenBalance : number ;
20+ tokenSymbol : string ;
21+ fiatBalance ?: number ;
22+ fiatSymbol ?: string ;
23+ } ;
24+
1425/**
1526 * Props for the AccountBalance component
1627 * @component
@@ -33,7 +44,7 @@ export interface AccountBalanceProps
3344 * use this function to transform the balance display value like round up the number
3445 * Particularly useful to avoid overflowing-UI issues
3546 */
36- formatFn ?: ( num : number ) => number ;
47+ formatFn ?: ( props : AccountBalanceFormatParams ) => string ;
3748 /**
3849 * This component will be shown while the balance of the account is being fetched
3950 * If not passed, the component will return `null`.
@@ -67,9 +78,11 @@ export interface AccountBalanceProps
6778 * Optional `useQuery` params
6879 */
6980 queryOptions ?: Omit <
70- UseQueryOptions < GetWalletBalanceResult > ,
81+ UseQueryOptions < AccountBalanceFormatParams > ,
7182 "queryFn" | "queryKey"
7283 > ;
84+
85+ showFiatValue ?: "USD" ;
7386}
7487
7588/**
@@ -149,10 +162,11 @@ export interface AccountBalanceProps
149162export function AccountBalance ( {
150163 chain,
151164 tokenAddress,
152- formatFn,
153165 loadingComponent,
154166 fallbackComponent,
155167 queryOptions,
168+ formatFn,
169+ showFiatValue,
156170 ...restProps
157171} : AccountBalanceProps ) {
158172 const { address, client } = useAccountContext ( ) ;
@@ -164,20 +178,61 @@ export function AccountBalance({
164178 chainToLoad ?. id || - 1 ,
165179 address || "0x0" ,
166180 { tokenAddress } ,
181+ showFiatValue ,
167182 ] as const ,
168- queryFn : async ( ) => {
183+ queryFn : async ( ) : Promise < AccountBalanceFormatParams > => {
169184 if ( ! chainToLoad ) {
170185 throw new Error ( "chain is required" ) ;
171186 }
172187 if ( ! client ) {
173188 throw new Error ( "client is required" ) ;
174189 }
175- return getWalletBalance ( {
190+ const tokenBalanceData = await getWalletBalance ( {
176191 chain : chainToLoad ,
177192 client,
178193 address,
179194 tokenAddress,
180195 } ) ;
196+
197+ if ( ! tokenBalanceData ) {
198+ throw new Error (
199+ `Failed to retrieve ${ tokenAddress ? `token: ${ tokenAddress } ` : "native token" } balance for address: ${ address } on chainId:${ chainToLoad . id } ` ,
200+ ) ;
201+ }
202+
203+ if ( showFiatValue ) {
204+ const fiatData = await convertCryptoToFiat ( {
205+ fromAmount : Number ( tokenBalanceData . displayValue ) ,
206+ fromTokenAddress : tokenAddress || NATIVE_TOKEN_ADDRESS ,
207+ to : showFiatValue ,
208+ chain : chainToLoad ,
209+ client,
210+ } ) . catch ( ( ) => undefined ) ;
211+
212+ // We can never support 100% of token out there, so if something fails to resolve, it's expected
213+ // in that case just return the tokenBalance and symbol
214+ return {
215+ tokenBalance : Number ( tokenBalanceData . displayValue ) ,
216+ tokenSymbol : tokenBalanceData . symbol ,
217+ fiatBalance : fiatData ?. result ,
218+ fiatSymbol : fiatData ?. result
219+ ? new Intl . NumberFormat ( "en" , {
220+ style : "currency" ,
221+ currency : showFiatValue ,
222+ minimumFractionDigits : 0 ,
223+ maximumFractionDigits : 0 ,
224+ } )
225+ . formatToParts ( 0 )
226+ . find ( ( p ) => p . type === "currency" ) ?. value ||
227+ showFiatValue . toUpperCase ( )
228+ : undefined ,
229+ } ;
230+ }
231+
232+ return {
233+ tokenBalance : Number ( tokenBalanceData . displayValue ) ,
234+ tokenSymbol : tokenBalanceData . symbol ,
235+ } ;
181236 } ,
182237 ...queryOptions ,
183238 } ) ;
@@ -190,13 +245,46 @@ export function AccountBalance({
190245 return fallbackComponent || null ;
191246 }
192247
193- const displayValue = formatFn
194- ? formatFn ( Number ( balanceQuery . data . displayValue ) )
195- : balanceQuery . data . displayValue ;
248+ if ( formatFn ) {
249+ return < span { ...restProps } > { formatFn ( balanceQuery . data ) } </ span > ;
250+ }
251+
252+ const { tokenBalance, tokenSymbol, fiatBalance, fiatSymbol } =
253+ balanceQuery . data ;
254+
255+ if ( fiatBalance && fiatSymbol ) {
256+ return (
257+ < span { ...restProps } >
258+ { `${ tokenBalance } ${ tokenSymbol } (${ fiatSymbol } ${ fiatBalance } )` }
259+ </ span >
260+ ) ;
261+ }
196262
197263 return (
198264 < span { ...restProps } >
199- { displayValue } { balanceQuery . data . symbol }
265+ { formatAccountBalanceForButton ( balanceQuery . data ) }
200266 </ span >
201267 ) ;
202268}
269+
270+ /**
271+ * Format the display balance for both crypto and fiat, in the Details button and Modal
272+ * If both crypto balance and fiat balance exist, we have to keep the string very short to avoid UI issues.
273+ * @internal
274+ */
275+ function formatAccountBalanceForButton (
276+ props : AccountBalanceFormatParams ,
277+ ) : string {
278+ if ( props . fiatBalance && props . fiatSymbol ) {
279+ // Need to keep them short to avoid UI overflow issues
280+ const formattedTokenBalance = formatNumber ( props . tokenBalance , 1 ) ;
281+ const num = formatNumber ( props . fiatBalance , 0 ) ;
282+ const formattedFiatBalance = shortenLargeNumber ( num ) ;
283+ return `${ formattedTokenBalance } ${ props . tokenSymbol } (${ props . fiatSymbol } ${ formattedFiatBalance } )` ;
284+ }
285+ const formattedTokenBalance = formatNumber (
286+ props . tokenBalance ,
287+ props . tokenBalance < 1 ? 5 : 4 ,
288+ ) ;
289+ return `${ formattedTokenBalance } ${ props . tokenSymbol } ` ;
290+ }
0 commit comments