Overview
The BalanceDisplay component provides a clean, card-based interface for displaying wallet balance information. It handles loading and error states gracefully and can optionally show the balance for a specific selected asset.
Import
import { BalanceDisplay } from '@/components/BalanceDisplay' ;
Props
balances
BalancesResponse | null
required
The balance data returned from the useBalances hook. Contains total balance and per-asset breakdown.
Indicates whether balance data is currently being fetched. Shows skeleton loaders when true.
Error message to display if balance fetching fails. Shows an error alert when present.
selectedAssetId
string
default: "undefined"
Optional aggregated asset ID (e.g., "ob:usdc") to display a specific asset’s balance alongside the total.
Type Definitions
interface BalanceDisplayProps {
balances : any ; // BalancesResponse from API
loading : boolean ;
error : string | null ;
selectedAssetId ?: string ;
}
// BalancesResponse structure
interface BalancesResponse {
totalBalance : {
fiatValue : number ;
};
balanceByAggregatedAsset : BalanceByAssetDto [];
}
interface BalanceByAssetDto {
aggregatedAssetId : string ; // e.g., "ob:usdc"
balance : string ; // BigInt as string
fiatValue : number ;
individualAssetBalances : IndividualAssetBalance [];
}
Usage
Basic Usage
import { BalanceDisplay } from '@/components/BalanceDisplay' ;
import { useBalances } from '@/lib/hooks/useBalances' ;
import { usePredictedAddress } from '@/lib/contexts/PredictedAddressContext' ;
export default function WalletPage () {
const { predictedAddress } = usePredictedAddress ();
const { balances , loading , error } = useBalances ( predictedAddress );
return (
< BalanceDisplay
balances = { balances }
loading = { loading }
error = { error }
/>
);
}
With Selected Asset
import { useState } from 'react' ;
import { BalanceDisplay } from '@/components/BalanceDisplay' ;
import { useBalances } from '@/lib/hooks/useBalances' ;
export default function AssetSelector () {
const [ selectedAsset , setSelectedAsset ] = useState ( 'ob:usdc' );
const { balances , loading , error } = useBalances ( predictedAddress );
return (
< div >
< AssetSelect onChange = { setSelectedAsset } value = { selectedAsset } />
< BalanceDisplay
balances = { balances }
loading = { loading }
error = { error }
selectedAssetId = { selectedAsset }
/>
</ div >
);
}
Component States
Loading
Error
Success
No Data
Displays skeleton loaders while fetching data: < div className = "space-y-2" >
< Skeleton className = "h-4 w-32" />
< Skeleton className = "h-10 w-full" />
</ div >
Shows an error alert when balance fetching fails: < Alert variant = "destructive" >
< p className = "text-sm" > Error loading balances: { error } </ p >
</ Alert >
Displays balance information in a card: < Card className = "p-4" >
< div className = "flex justify-between" >
< h3 > Total Balance </ h3 >
< span > $1,234.56 </ span >
</ div >
{ /* Selected asset info if provided */ }
</ Card >
Returns null when no balance data is available (not loading, no error): if ( ! balances ) {
return null ;
}
Features
Total Balance Display
The component always shows the total portfolio value in USD:
< div className = "flex justify-between items-center" >
< h3 className = "text-sm font-medium" > Total Balance </ h3 >
< span className = "font-medium" >
$ { balances . totalBalance ?. fiatValue . toFixed ( 2 ) }
</ span >
</ div >
Selected Asset Details
When selectedAssetId is provided, the component displays additional information:
Asset symbol (extracted from the aggregated asset ID)
Token balance (converted from wei using 18 decimals)
USD value of the selected asset
{ selectedBalance && (
< div className = "flex justify-between mt-2 border-t pt-2" >
< h3 className = "text-sm font-medium" > Selected Asset </ h3 >
< div className = "text-right" >
< div className = "font-medium" >
{ parseFloat ( selectedBalance . balance ) / 10 ** 18 }{ ' ' }
{ selectedBalance . aggregatedAssetId . split ( ':' )[ 1 ] }
</ div >
< div className = "text-xs text-gray-500" >
$ { selectedBalance . fiatValue ?. toFixed ( 2 ) }
</ div >
</ div >
</ div >
)}
Balance Calculation
The component finds the selected asset’s balance using the aggregated asset ID:
let selectedBalance = null ;
if ( selectedAssetId && balances . balanceByAggregatedAsset ) {
selectedBalance = balances . balanceByAggregatedAsset . find (
( b : any ) => b . aggregatedAssetId === selectedAssetId
);
}
Styling
The component uses a card layout with consistent spacing:
Card padding: p-4
Vertical spacing: space-y-2
Border between sections: border-t pt-2 mt-2
Text sizes: text-sm for labels, text-xs for secondary info
All fiat values are displayed with exactly 2 decimal places:
Token balances are stored as strings representing wei (BigInt) and converted for display:
{ parseFloat ( selectedBalance . balance ) / 10 ** 18 }
The component currently hardcodes 18 decimals for token conversion. This works for most ERC20 tokens but may not be accurate for tokens with different decimal places (e.g., USDC uses 6 decimals).
Integration with Hooks
The component is designed to work seamlessly with the useBalances hook:
const { balances , loading , error } = useBalances ( predictedAddress );
< BalanceDisplay
balances = { balances }
loading = { loading }
error = { error }
/>
Accessibility
Semantic HTML with proper heading levels (h3)
Clear visual hierarchy with font weights and sizes
Error messages are announced to screen readers via the Alert component
Loading states prevent confusion during data fetching
AssetList - Detailed asset breakdown with chain distribution
ConnectButton - Wallet connection with balance modal
Best Practices
Always provide loading and error props
The component relies on these props to show appropriate UI states. Never pass static false values.
Handle null balances gracefully
The component returns null when no data is available, so ensure parent components handle this case.
Use with authentication checks
Only render this component when a user is authenticated and has a wallet address.