import type { AddressTokenBalance } from '@/services/api/addresses' import type { TokenAggregationMarketSnapshot, TokenAggregationTokenSnapshot } from '@/services/api/tokenAggregation' export interface ResolvedTokenMarket { priceUsd?: number liquidityUsd?: number source?: 'token-aggregation' | 'blockscout' | 'derived' pricingKind?: 'spot' | 'lp-share' isCanonicalClone?: boolean } export function isLikelyLpReceiptSymbol(symbol: string | undefined): boolean { if (!symbol) return false const upper = symbol.toUpperCase() return upper.includes('DLP') || upper.endsWith('-LP') || upper.startsWith('LP-') || upper === 'LP' } export function formatBalanceUsdLabel( priceUsd: number | undefined, balanceUsd: number | undefined, options?: { pricingKind?: 'spot' | 'lp-share'; atTransfer?: boolean }, ): string { if (options?.pricingKind === 'lp-share') { return 'LP share — USD N/A' } if (balanceUsd != null) { return options?.atTransfer ? `≈ ${formatUsd(balanceUsd)} (at transfer)` : `≈ ${formatUsd(balanceUsd)}` } if (priceUsd == null) { return 'USD unavailable' } return 'USD unavailable' } function formatUsd(value: number): string { return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', maximumFractionDigits: value >= 100 ? 0 : 2, }).format(value) } function parseUsd(value: string | number | null | undefined): number | undefined { if (value == null) return undefined const numeric = typeof value === 'number' ? value : Number(value) return Number.isFinite(numeric) && numeric > 0 ? numeric : undefined } export function resolveTokenMarketForBalance( balance: Pick, tokenMarkets: Record, ): ResolvedTokenMarket { const key = balance.token_address.toLowerCase() const aggregationSnapshot = tokenMarkets[key] const aggregationMarket: TokenAggregationMarketSnapshot | null | undefined = aggregationSnapshot?.market const aggregationPrice = aggregationMarket?.priceUsd const blockscoutPrice = parseUsd(balance.exchange_rate) const pricingKind = aggregationMarket?.pricingKind ?? (isLikelyLpReceiptSymbol(balance.token_symbol ?? aggregationSnapshot?.symbol) ? 'lp-share' : 'spot') const isCanonicalClone = aggregationMarket?.isCanonicalClone if (aggregationPrice != null && aggregationPrice > 0) { return { priceUsd: aggregationPrice, liquidityUsd: aggregationMarket?.liquidityUsd, source: 'token-aggregation', pricingKind, isCanonicalClone, } } if (blockscoutPrice != null) { return { priceUsd: blockscoutPrice, liquidityUsd: aggregationMarket?.liquidityUsd, source: 'blockscout', pricingKind, isCanonicalClone, } } return { priceUsd: undefined, liquidityUsd: aggregationMarket?.liquidityUsd, source: 'derived', pricingKind, isCanonicalClone, } } export function estimateTokenBalanceUsd( rawAmount: string, decimals: number, priceUsd: number | undefined, ): number | undefined { if (priceUsd == null || !(priceUsd > 0) || !rawAmount) return undefined try { const raw = BigInt(rawAmount) if (raw <= 0n) return 0 const scale = 10n ** BigInt(decimals) const whole = raw / scale const fraction = raw % scale const amount = Number(whole) + Number(fraction) / Number(scale) if (!Number.isFinite(amount)) return undefined return amount * priceUsd } catch { return undefined } }