@@ -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,35 @@ 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+ }
196251
197252 return (
198253 < span { ...restProps } >
199- { displayValue } { balanceQuery . data . symbol }
254+ { formatAccountBalanceForButton ( balanceQuery . data ) }
200255 </ span >
201256 ) ;
202257}
258+
259+ /**
260+ * Format the display balance for both crypto and fiat, in the Details button and Modal
261+ * If both crypto balance and fiat balance exist, we have to keep the string very short to avoid UI issues.
262+ * @internal
263+ */
264+ function formatAccountBalanceForButton (
265+ props : AccountBalanceFormatParams ,
266+ ) : string {
267+ if ( props . fiatBalance && props . fiatSymbol ) {
268+ // Need to keep them short to avoid UI overflow issues
269+ const formattedTokenBalance = formatNumber ( props . tokenBalance , 1 ) ;
270+ const num = formatNumber ( props . fiatBalance , 0 ) ;
271+ const formattedFiatBalance = shortenLargeNumber ( num ) ;
272+ return `${ formattedTokenBalance } ${ props . tokenSymbol } (${ props . fiatSymbol } ${ formattedFiatBalance } )` ;
273+ }
274+ const formattedTokenBalance = formatNumber (
275+ props . tokenBalance ,
276+ props . tokenBalance < 1 ? 5 : 4 ,
277+ ) ;
278+ return `${ formattedTokenBalance } ${ props . tokenSymbol } ` ;
279+ }
0 commit comments