BigBlocks

Token List

Displays BSV20/BSV21 fungible token holdings with balances, icons, and token details fetched from the 1Sat API

Loading

Empty

Installation

bunx shadcn@latest add https://registry.bigblocks.dev/r/token-list.json

Usage

Provide a BSV payment address and an array of token IDs. Token details and balances are fetched automatically from the 1Sat API on mount.

import { TokenList } from "@/components/blocks/token-list"
 
export function WalletTokens({ address }: { address: string }) {
  return (
    <TokenList
      address={address}
      tokenIds={["abc123_0", "def456_0"]}
      onSelect={(token) => console.log("Selected:", token.symbol)}
    />
  )
}

When address is null or tokenIds is empty, the list renders an empty state without making any network requests.

Props

TokenList

PropTypeDefaultDescription
addressstring | nullrequiredBSV payment address to fetch token holdings for. Pass null when no wallet is connected.
tokenIdsstring[]Token IDs (inscription IDs) to fetch balances for. When omitted or empty, no tokens are fetched.
apiUrlstring"https://api.1sat.app"Override the 1Sat API base URL
autoFetchbooleantrueWhether to fetch automatically on mount and when address or tokenIds change
onSelect(token: TokenHolding) => voidCalled when a token row is clicked. When provided, rows become interactive with hover state and keyboard support.
skeletonCountnumber3Number of skeleton rows to render while loading
classNamestringAdditional CSS classes applied to the wrapping card

Data Types

TokenHolding

The shape of each token in the list, also passed to onSelect:

interface TokenHolding {
  tokenId: string       // Token contract/inscription ID
  symbol: string        // Token symbol or ticker
  type: "BSV20" | "BSV21"  // Token standard
  balance: string       // Raw balance string (before decimal adjustment)
  decimals: number      // Decimal precision for the token
  iconUrl: string | null // ORDFS URL for the token icon, or null
}

Balances are stored as raw strings to avoid floating-point precision loss. The UI component applies decimal formatting automatically using BigInt arithmetic.

States

TokenList handles four display states automatically:

StateTriggerRendered UI
LoadingisLoading && tokens.length === 0Skeleton rows matching skeletonCount
Errorerror !== nullError card with the error message
Emptytokens.length === 0 (after load)Empty state card with a coin icon
Populatedtokens.length > 0Scrollable list of token rows

Hook

useTokenList can be used directly to fetch token data and render a completely custom UI.

import { useTokenList } from "@/components/blocks/token-list"
 
function CustomTokenDisplay({ address }: { address: string }) {
  const { tokens, isLoading, error, refetch } = useTokenList({
    address,
    tokenIds: ["abc123_0"],
  })
 
  if (isLoading) return <p>Loading tokens...</p>
  if (error) return <p>Error: {error.message} <button onClick={refetch}>Retry</button></p>
 
  return (
    <ul>
      {tokens.map((token) => (
        <li key={token.tokenId}>
          {token.symbol}: {token.balance}
        </li>
      ))}
    </ul>
  )
}

useTokenList Options

OptionTypeDefaultDescription
addressstring | nullrequiredBSV payment address to fetch holdings for
tokenIdsstring[]Token IDs to fetch. No requests are made when this is empty or omitted.
apiUrlstring"https://api.1sat.app"Override the 1Sat API base URL
autoFetchbooleantrueAutomatically fetch on mount and when dependencies change

useTokenList Return

PropertyTypeDescription
tokensTokenHolding[]List of resolved token holdings
isLoadingbooleantrue while the initial fetch is in progress
errorError | nullError from the last failed fetch, or null
refetch() => voidManually trigger a refetch without changing dependencies

API Endpoints Used

The hook fetches from two 1Sat API endpoints per token:

  • GET /1sat/bsv21/:tokenId — token metadata (symbol, decimals, icon)
  • GET /1sat/bsv21/:tokenId/p2pkh/:address/balance — confirmed and pending balances

Balances from both confirmed and pending are summed. A 404 on the balance endpoint (no holdings for that address) is treated as a zero balance rather than an error.