import { type TokenDTO } from '@explorins/pers-sdk'; import type { TokenBalance } from '@explorins/pers-sdk/web3'; /** * Token balance with associated token information for display * * This composite type combines blockchain balance data with token metadata, * making it easy to display token information in UI components without * needing to join data from multiple sources. * * ## CRITICAL: Token ID for Transactions * * For ERC721 (stamps) and ERC1155 (rewards), use `tokenBalance.tokenId` as the * `contractTokenId` when creating transactions. This is the **on-chain token ID** * assigned by the blockchain, NOT the metadata definition ID. * * | Token Type | tokenId meaning | Example | * |------------|-----------------|---------| * | ERC20 | `null` or `""` | N/A (fungible points) | * | ERC721 | NFT instance ID | `"46"` (on-chain ID) | * | ERC1155 | Token type ID | `"16"` (semi-fungible type) | * * @example Correct usage for token burn transaction * ```typescript * const stampBalance = tokenBalances.find(b => b.token.type === 'ERC721'); * * const request = buildPOSBurnRequest({ * amount: 1, * contractAddress: stampBalance.token.contractAddress, * contractTokenId: stampBalance.tokenBalance.tokenId, // ✅ Correct * chainId: stampBalance.token.chainId, * userId: userId, * businessId: businessId * }); * ``` * * * */ export interface TokenBalanceWithToken { /** * The balance data from blockchain * * **Important:** For NFT operations, use `tokenBalance.tokenId` as the `contractTokenId` * in transaction requests. This is the actual on-chain ID, not the metadata definition. */ tokenBalance: TokenBalance; /** The token definition (contract, symbol, metadata, etc.) */ token: TokenDTO; /** Human-readable formatted balance string (e.g., "100 XPL" or "3 stamps") */ balanceFormatted: string; } /** * Configuration options for useTokenBalances hook */ export interface UseTokenBalancesOptions { /** * Wallet address to check balances for (REQUIRED) * Apps must explicitly provide the wallet address - no automatic selection * Get from: user.wallets[selectedIndex].address */ accountAddress: string; /** Available tokens to check balances for (from SDK or filtered list) */ availableTokens?: TokenDTO[]; /** Whether to automatically load balances on mount/auth/token changes */ autoLoad?: boolean; /** * Fallback polling interval in milliseconds (0 = disabled) * @deprecated Use wallet events instead (refreshOnWalletEvents). Polling kept as fallback only. * @default 0 */ refreshInterval?: number; /** * Auto-refresh balances when wallet events are received (Transfer, Approval, etc.) * Requires `captureWalletEvents: true` in SDK config (default) * @default true */ refreshOnWalletEvents?: boolean; /** * Debounce time for wallet event-triggered refreshes (milliseconds) * Prevents rapid refresh calls during batch transfers * @default 1000 */ walletEventDebounceMs?: number; } /** * Token balance loading hook return value */ export interface UseTokenBalancesResult { /** Array of token balances with formatted display data */ tokenBalances: TokenBalanceWithToken[]; /** Whether balances are currently loading */ isLoading: boolean; /** Error message if loading failed */ error: string | null; /** Manually trigger balance refresh */ refresh: () => Promise; /** Whether the hook is available (SDK initialized, user authenticated, has wallet) */ isAvailable: boolean; /** User's wallet address being queried */ userAccountAddress: string | null; } /** * React hook for loading and managing token balances * * Automatically handles: * - Loading balances for ERC20 (fungible tokens/points) * - Loading collections for ERC721 (unique NFTs/stamps) * - Loading collections for ERC1155 (semi-fungible tokens/rewards) * - Parallel loading with error resilience * - Auto-refresh on authentication or token list changes * - Formatted balance strings for display * * This hook consolidates complex token balance loading logic that would otherwise * need to be duplicated across every app. It handles the differences between * fungible and non-fungible tokens, parallel loading, error handling, and * provides formatted display strings. * * @param options - Configuration options * @returns Token balances state and actions * * @example * **Basic Usage:** * ```typescript * import { useTokenBalances, useTokens, usePersSDK } from '@explorins/pers-sdk-react-native'; * import { useState, useEffect } from 'react'; * * function TokenBalancesScreen() { * const { user } = usePersSDK(); * const { getTokens } = useTokens(); * const [availableTokens, setAvailableTokens] = useState([]); * * // Load tokens on mount * useEffect(() => { * getTokens().then(result => setAvailableTokens(result.data)); * }, []); * * // App explicitly selects which wallet to use * const walletAddress = user?.wallets?.[0]?.address; * * const { * tokenBalances, * isLoading, * error, * refresh * } = useTokenBalances({ * accountAddress: walletAddress!, * availableTokens, * autoLoad: true * }); * * if (isLoading) return Loading balances...; * if (error) return Error: {error}; * * return ( * * {tokenBalances.map(({ token, balanceFormatted }) => ( * * {token.name}: {balanceFormatted} * * ))} *