Address & balances

Once a player is signed in they have an embedded Solana wallet. This page covers the two things your game UI needs from it: the address (to show and to receive deposits) and the balances (to show how much they have). Prebuilt components handle both; the hooks are there when you want your own layout.

Assumes the player is authenticated — see Login & sign-in.

The wallet object

useWallet() returns the signed-in player’s wallet, or null while they’re anonymous or the wallet is still loading. The two fields you’ll use constantly are id (for reading balances) and address (the base58 Solana pubkey).

TS
const wallet = useWallet();
// IWallet | null
// {
//   id: string;        // wallet id — pass to useWalletBalance
//   address: string;   // base58 Solana pubkey
//   custody: "operator" | "self";
//   status: "active" | "exported" | "archived";
//   ...
// }
Guard for null
useWallet() is null until the player is authenticated and the wallet has loaded. Render a placeholder while wallet == null rather than assuming it’s there.

Showing the address

Drop in WalletAddress for a ready-made card with truncation, a copy button, and an optional QR slot. It reads the wallet itself and renders nothing when there isn’t one.

TS
import { WalletAddress } from "@iamgame/wallet-sdk";

<WalletAddress label="Deposit address" truncateChars={6} />
PropDefaultWhat it does
label"Wallet Address"Heading above the address.
truncateChars6Leading + trailing chars kept in the truncated form.
showCopytrueShow the copy-to-clipboard button.
renderQrRender a QR from the address, e.g. renderQr={(a) => <QRCode value={a} />}. Omit to skip the QR.
themeIAMGame defaultToken overrides (background, foreground, muted, surface, border, primary, radius, fontFamily).

Rolling your own? Read wallet.address and truncate it with the exported shortAddress helper — the same one the component uses, so display stays consistent.

TS
import { useWallet, shortAddress } from "@iamgame/wallet-sdk";

function AddressPill() {
  const wallet = useWallet();
  if (!wallet) return null;
  return (
    <button onClick={() => navigator.clipboard.writeText(wallet.address)}>
      {shortAddress(wallet.address, 4)}   {/* e.g. "9xQe...k3Vf" */}
    </button>
  );
}

Reading balances

Balances come from useWalletBalance(walletId, pollMs). Pass the wallet’s id — not its address — and it polls the API on an interval, returning the latest snapshot (or null until the first fetch lands).

TS
const wallet = useWallet();
const balance = useWalletBalance(wallet?.id ?? null, 10_000); // poll every 10s

// IWalletBalance | null
// {
//   walletId: string;
//   address: string;
//   tokens: ITokenBalance[];   // one entry per token the wallet holds
//   asOf: string;              // ISO timestamp of this snapshot
// }

Default poll interval is 15000ms (15s). Pass a smaller number for a livelier balance during active play, larger to ease off the API. Passing null for the wallet id (e.g. while anonymous) turns polling off.

The tokens array

Each entry in tokens is an ITokenBalance. Amounts are raw base units as a stringified BigInt, never a float — so always format with the SDK helper, never parseFloat.

TS
// ITokenBalance
{
  mint: string;      // "SOL", or an SPL mint address like USDC's
  amount: string;    // raw base units, stringified BigInt (lamports / micro-USDC / …)
  decimals: number;  // 9 for SOL, 6 for USDC
  symbol?: string;   // "USDC" when known
}

formatTokenAmount(token) turns the raw amount into a human decimal string using the token’s own decimals, and getTokenLabel(token) gives you a display label — the token’s symbol if known, “SOL”, or a truncated mint as a fallback.

TS
import { useWallet, useWalletBalance, formatTokenAmount, getTokenLabel } from "@iamgame/wallet-sdk";

function Balances() {
  const wallet = useWallet();
  const balance = useWalletBalance(wallet?.id ?? null);

  if (!wallet) return null;
  if (!balance) return <span>Loading…</span>;

  return (
    <ul>
      {balance.tokens.map((token) => (
        <li key={token.mint}>
          {getTokenLabel(token)}: {formatTokenAmount(token)}
        </li>
      ))}
    </ul>
  );
}

Labeling known mints (USDC)

getTokenLabel already surfaces a friendly label when the balance carries a symbol. When you want to single out one currency — say, only show the player’s USDC — filter the tokens array by mint. A mint is just the token’s address; the SOL entry uses the literal “SOL”.

TS
// Your app knows which mint is USDC for the environment you're in
// (devnet USDC and mainnet USDC have different mint addresses — read it from your config).
const USDC_MINT = process.env.NEXT_PUBLIC_USDC_MINT!;

const usdc = balance?.tokens.find((t) => t.mint === USDC_MINT);
const usdcDisplay = usdc ? formatTokenAmount(usdc) : "0"; // e.g. "12.5"
Amounts are integers — see Core concepts
SOL is in lamports (109), USDC in micro-USDC (106). Never do currency math on the formatted decimal; operate on the raw amount with BigInt and format only for display. Background in Core concepts.

Drop-in balance component

Don’t need custom layout? WalletBalance renders the whole list with polling, a refresh button, and a last-updated stamp built in.

TS
import { WalletBalance } from "@iamgame/wallet-sdk";

// Show every token, refresh every 10s:
<WalletBalance pollMs={10_000} />

// Or restrict to specific mints and go compact:
<WalletBalance filterMints={[USDC_MINT]} label="Cash" />
PropDefaultWhat it does
pollMs15000Balance refresh interval.
filterMintsallShow only these mints; omit to show everything the wallet holds.
showRefreshtrueShow the manual refresh button.
label"Balance"Heading text.
compactfalseSingle-line SOL-only display, no card chrome.

Where to go next

  • Deposit — fund the wallet you just showed the address for.
  • Withdraw & export — send funds out or hand the player self-custody.