Guide

Balances

puzzle.balance() through the explorers provider for its chain. The one call that leaves the process. Base units as bigint. API keys never reach an error.

The call

import { b1000 } from "@agntn/puzzles/collections/b1000";

const balance = await b1000.require(71).balance();

balance.chain; // "bitcoin"
balance.confirmed; // 710022600n, base units
balance.unconfirmed; // 0n, a signed mempool delta when the provider reports one
balance.total(); // 710022600n
balance.totalUnits(); // 7.100226
balance.decimals; // 8, from @agntn/chains

Every puzzle answers balance(). Collections forward balance(query) and balanceById(id) to it. The providers come from @agntn/explorers and load on the first call, so importing the package carries no network code.

ChainProviderNeeds
Bitcoin, LitecoinMempool, mempool.space and litecoinspace.orgnothing
EthereumEtherscan V2an API key
DecredDcrdatanothing, and patience: dcrdata answers a fresh address in 6 to 11 seconds
Arweavethe arweave.net gatewaynothing
Monerononerejects with UnsupportedChainError before any request

Options

await puzzle.balance({
  apiKey: "your-etherscan-key",
  baseUrl: "https://mempool.example", // another instance of the same provider
  timeout: 30_000, // milliseconds, the providers default to 15 seconds
});

That's the whole options object. There's no fetch to inject. The providers read globalThis.fetch when they run, so a test stubs the global and a runtime brings its own. Ethereum without a key rejects with BalanceProviderError before it touches the network. Etherscan would reject it anyway, just slower.

Errors

import {
  BalanceError,
  BalanceProviderError,
  InvalidAddressError,
  UnsupportedChainError,
} from "@agntn/puzzles";

All three descend from BalanceError, which descends from PuzzlesError. InvalidAddressError is a provider or @agntn/chains saying the address doesn't fit. UnsupportedChainError is Monero. BalanceProviderError is everything else: a transport failure, a not numeric balance, a rejected key. An API key never appears in an error message. It's replaced with REDACTED, and the original error isn't attached as cause, so it can't leak through a logger either.

CLI and tools

puzzles balance b1000/71                       # b1000/71: 7.100226 BTC
puzzles balance arweave/weave11 --api-key    # Ethereum, or set ETHERSCAN_API_KEY
puzzles balance b1000/71 --json                # { chain, confirmed, unconfirmed, decimals }

puzzles_balance is the same call for a model. It's the only tool annotated as open world, because it's the only one that reaches beyond the bundled dataset.

On this site

Every puzzle page and the playground show a live balance. The browser doesn't call the explorers itself. The docs worker does, through /api/balance/<id>, with the same puzzle.balance() call and its own Etherscan key. Each answer is cached for five minutes, so a busy page is one request to mempool.space, not a thousand. A failure comes back as the library's own message, Balance lookup failed: …, under the same status code the tool would set isError for.

@agntn/puzzles·MIT license· Public data about public puzzles. Balances come from the chains' explorers through the worker, cached for five minutes. Every key here was public before it landed in a record.