The fastest read against the chain: one JSON-RPC call returns a DID’s native balance and current nonce. Token balances are a separate query because Sigil tokens live in their own state partition.

When to use this

  • Validate that a faucet drip landed.
  • Pre-flight an outgoing transfer before signing.
  • Indexer or wallet UI that needs a live balance.

Prerequisites

  • A reachable RPC endpoint.
  • A DID to query — the caller does not need to control it.

Recipe — TypeScript

const RPC_URL = process.env.SIGIL_RPC_URL ?? 'https://testnet-rpc.sigil.ml/rpc';

async function rpc<T>(method: string, params: unknown): Promise<T> {
  const res = await fetch(RPC_URL, {
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    body: JSON.stringify({ jsonrpc: '2.0', id: 1, method, params }),
  });
  const body = await res.json() as { result?: T; error?: { code: number; message: string } };
  if (body.error) throw new Error(`${body.error.code}: ${body.error.message}`);
  if (body.result === undefined) throw new Error('missing result');
  return body.result;
}

async function nativeBalance(did: string) {
  return rpc<{ did: string; balance: number; nonce: number }>('sigil_getBalance', { did });
}

async function tokenBalance(did: string, tokenId: string) {
  return rpc<{ did: string; token_id: string; balance: string }>(
    'sigil_getAccount',
    { did, token_id: tokenId },
  );
}

const did = process.env.QUERY_DID ?? 'did:oas:sigil:agent:alice...';
console.log(await nativeBalance(did));
console.log(await tokenBalance(did, 'tok:demo:points'));

Recipe — curl

curl -s "$SIGIL_RPC_URL" -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"sigil_getBalance",
       "params":{"did":"did:oas:sigil:agent:alice..."}}' | jq

curl -s "$SIGIL_RPC_URL" -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"sigil_getAccount",
       "params":{"did":"did:oas:sigil:agent:alice...","token_id":"tok:demo:points"}}' | jq

Expected output

{
  "did": "did:oas:sigil:agent:alice...",
  "balance": 99000000,
  "nonce": 12
}
For tokens, balance is a decimal string because supply is u128:
{
  "did": "did:oas:sigil:agent:alice...",
  "token_id": "tok:demo:points",
  "balance": "12345"
}

Formatting for display

Native balances are in micro-MINT (the smallest divisible unit). Divide by 1,000,000 for display.
function formatDisplay(baseUnits: number, symbol: string): string {
  const display = baseUnits / 1_000_000;
  return `${display.toFixed(6)} ${symbol}`;
}
formatDisplay(99_000_000, 'SIGIL'); // "99.000000 SIGIL"

Common errors

SymptomCauseFix
did not registeredDID never received any stateThe native balance is still queryable; nonce will be 0
token_id not foundToken class doesn’t existCheck spelling; query sigil_getAccount without token_id
balance returns 0 unexpectedlyStale RPC nodeQuery the direct endpoint or a different region

See also