diff --git a/lib/format.ts b/lib/format.ts index b198d1e0..c41eb1e6 100644 --- a/lib/format.ts +++ b/lib/format.ts @@ -1,10 +1,13 @@ /** * Shared number formatting utilities for readable display. * - * formatPrice — token prices (small decimals, 4 sig digits) - * formatSupply — token supply / balances (large numbers, commas) + * formatPrice — token prices (small decimals, 4 sig digits) + * formatSupply — token supply / balances (large numbers, commas) + * formatTokenAmount — bigint token amounts with tiered precision */ +import { formatUnits } from "viem"; + /** Format a token price for display. Accepts a string or number. */ export function formatPrice(value: string | number): string { const v = typeof value === "string" ? parseFloat(value) : value; @@ -14,6 +17,22 @@ export function formatPrice(value: string | number): string { return v.toFixed(2); } +/** Format a raw bigint token amount for display with appropriate precision. */ +export function formatTokenAmount(value: bigint, decimals: number): string { + const num = Number(formatUnits(value, decimals)); + if (num === 0) return "0"; + if (num >= 1) { + return num.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 }); + } + if (num >= 0.001) { + return num.toFixed(4); + } + if (num >= 0.000001) { + return num.toFixed(6); + } + return num.toExponential(2); +} + /** Format a token supply or balance for display. Accepts a string or number. */ export function formatSupply(value: string | number): string { const v = typeof value === "string" ? parseFloat(value) : value; diff --git a/src/components/DonateWidget.tsx b/src/components/DonateWidget.tsx index be110139..8c0a5770 100644 --- a/src/components/DonateWidget.tsx +++ b/src/components/DonateWidget.tsx @@ -6,6 +6,7 @@ import { useQuery } from "@tanstack/react-query"; import { parseUnits, formatUnits } from "viem"; import { browserClient as publicClient } from "../../lib/rpc"; import { erc20Abi } from "../../lib/price"; +import { formatTokenAmount } from "../../lib/format"; import { storyFactoryAbi } from "../../lib/contracts/abi"; import { STORY_FACTORY, PLOT_TOKEN, RESERVE_LABEL, EXPLORER_URL } from "../../lib/contracts/constants"; import { indexFetch } from "../../lib/index-fetch"; @@ -149,7 +150,7 @@ export function DonateWidget({ storylineId, writerAddress }: DonateWidgetProps) {balance !== undefined && (

- Balance: {formatUnits(balance, 18)} {RESERVE_LABEL} + Balance: {formatTokenAmount(balance, 18)} {RESERVE_LABEL}

)} {insufficientBalance && ( @@ -161,7 +162,7 @@ export function DonateWidget({ storylineId, writerAddress }: DonateWidgetProps)

Donating{" "} - {formatUnits(parsedAmount, 18)} {RESERVE_LABEL} + {formatTokenAmount(parsedAmount, 18)} {RESERVE_LABEL} {" "} to {writerAddress ? : `story #${storylineId}`}

diff --git a/src/components/TradingWidget.tsx b/src/components/TradingWidget.tsx index be0c427e..34fd496e 100644 --- a/src/components/TradingWidget.tsx +++ b/src/components/TradingWidget.tsx @@ -6,6 +6,7 @@ import { useQuery } from "@tanstack/react-query"; import { parseUnits, formatUnits, type Address } from "viem"; import { browserClient as publicClient } from "../../lib/rpc"; import { mcv2BondAbi, erc20Abi } from "../../lib/price"; +import { formatTokenAmount } from "../../lib/format"; import { MCV2_BOND, PLOT_TOKEN, RESERVE_LABEL, EXPLORER_URL, ZAP_PLOTLINK, SUPPORTED_ZAP_TOKENS, ETH_ADDRESS, @@ -28,21 +29,6 @@ function applySlippage(amount: bigint, isBuy: boolean): bigint { const isZapAvailable = ZAP_PLOTLINK !== "0x0000000000000000000000000000000000000000"; -/** Format a raw bigint token amount for display with appropriate precision. */ -function formatTokenAmount(value: bigint, decimals: number): string { - const num = Number(formatUnits(value, decimals)); - if (num === 0) return "0"; - if (num >= 1) { - return num.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 }); - } - if (num >= 0.001) { - return num.toFixed(4); - } - if (num >= 0.000001) { - return num.toFixed(6); - } - return num.toExponential(2); -} /** Retry a writeContractAsync call once if it fails with a nonce error. */ async function retryOnNonceError(fn: () => Promise): Promise {