Use this page to read rand_getSupply correctly and to reason about issuance: where RAND comes from, where it goes, and the identity that makes a shielded chain’s supply exact even though nobody can add up its notes.
Where RAND comes from and goes
A shielded chain cannot sum its own money: a note’s value lives inside its commitment. What is public is every crossing of the pool’s boundary. There are five kinds, seven on a chain with program vaults (RPL-2, since chain 20), and on chain 21 (fullnode v0.8.0, live since 2026-10-12) the multisig accounts and the perps engine add their own:
| direction | crossing | when |
|---|---|---|
| in | a genesis deposit note (alloc in the genesis file, opened since chain 14) | block 0 |
| in | a faucet mint (Mint, up to 100 RAND, validator-signed, carrying the note’s opening since chain 14) | testnet chains with "faucet": true; since chain 20 only to 16 allowlisted wallets, signed by one of the 18 genesis-listed minters, at most 10,000 RAND per 1,000-block epoch |
| in | a validator’s Withdraw, which pays released stake and rewards into a note | any time |
| out | a bundle’s fee: through chain 20 all of it into the block proposer’s rewards; on chain 21 the floor is destroyed (burned, counted in base_fees_burned) and only the tip goes to the proposer | every bundle |
| out | a bundle’s burn_r: a Bond burning stake into a validator’s public stake | every bond |
| out | an Invoke’s burn_r: RAND deposited into a program’s public vault | since chain 20 (v0.6.8) |
| in | a vault payout: RAND an Invoke pays out of a program’s vault as a note, its amount and recipient public | since chain 20 (v0.6.8) |
| out | a CreateMultisig’s or MultisigDeposit’s burn_r: RAND paid into a multisig account’s public balance (multisig_rand_in) | chain 21, since v0.8.0 |
| in | a MultisigPay’s notes: RAND an account pays out, amount and recipient public (multisig_rand_out) | chain 21, since v0.8.0 |
| out / in | a PerpDeposit’s burn into a trading account and a perp payout note the chain computes (perps_rand_out, perps_rand_held); on chain 21 the one market’s collateral is zUSD, so these read 0 there | chain 21, since v0.8.0 |
On a chain whose genesis sets tokens.burn_registration_fee, as chain 21’s does (and every chain’s since 15), a token registration’s 1 RAND registration_fee also leaves the pool through burned, but enters no register entry: it is destroyed, and the proposer keeps only the bundle base. On chain 21 the genesis fees section (burn_base and burn_floor) does the same to every bundle’s settled floor — the 0.001 RAND base of a transfer, a deploy’s per-word term, a call’s gas and byte terms, a bridge burn’s 0.01 RAND — so the proposer keeps the tip alone, fee − floor, and fees_paid moves by that (Fees).
Beside the pool sits the validator register, which holds amounts in the clear because consensus weight must be public. Stake in the register is real RAND: it can be unbonded and withdrawn back into a note. Since chain 20 the program vaults are a second public balance beside it: RAND an invoke deposits stays countable there until a payout returns it to the pool as a note. Chain 21 adds a third, the multisig accounts (Transactions): a per-asset balance spent by a threshold of Dilithium2 keys, seeded at genesis with the foundation treasury and open to anyone’s CreateMultisig for a 1 RAND fee.
There are no block rewards and no inflation on chain 21. The chain’s first scheduled issuance is the block-aggregation subsidy — a decaying amount of new RAND per sealed block, paid to the aggregator whose proof covers it — and it is not active on chain 21 (Proofs); v0.8.0 also carries a dollar-indexed form of it (fees.usd_subsidy, priced at a RAND/USD figure the validator set votes with SetRandPrice), which chain 21 does not switch on. Chain 21 is a fresh chain, live 2026-10-12 (genesis cut 10:22 UTC, the 26 validators switched 10:31 UTC), cut from chain 20 (fullnode v0.8.0, a hard fork). Its genesis issued 129,938.879078887 RAND: 26,000 staked across the 26 genesis validators, 3,938.879078887 in seven deposit notes, the balances of the seven operator wallets carried from chain 20 (a scan of the chain-20 pool at heights 822,056 to 822,229), and 100,000 RAND of new issuance in the foundation treasury, a multisig account under 2 of 3 signers (id d5fd1883…1958, rand_getSupply.multisig_issued). What other wallets held on chain 20, about 36,025 RAND (35,040 RAND of faucet mints to the allowlisted recipients, payments to users), was not carried, since a note cannot be re-created without its owner’s address; nor were 2.606 RAND of unwithdrawn validator rewards. Three more genesis notes carry the bridge’s 100.001999 zUSD (10, 89.002999 and 0.999), equal to the custody locked on its four backings, which is not RAND and is outside this audit. rand_getSupply on chain 21 reads genesis_deposited 3,938,879,078,887 units, genesis_staked 26,000,000,000,000, multisig_issued 100,000,000,000,000 and invariant_holds: true (height 2,178, 2026-10-12). Chain 20, cut 2026-10-01 from chain 19’s state at height 7,258, had issued 31,184.243719258 RAND: the same 26,000 staked and 5,184.243719258 in seven notes, beside two notes of 99 zUSD. Chain 15, cut 2026-09-26, issued 23,000 RAND at genesis, 18,000 staked across 18 validators and 5,000 in five notes; its eight later validators bonded out of existing notes, which issued nothing. Every faucet mint since adds to the issued figure, and every burned fee takes from it.
Tokens are outside this audit
The crossings above are RAND’s. An RPL token, bridged or native, never crosses the RAND boundary: asset index 0 is reserved for RAND and the token registry never hands it out, so a BridgeAttest, a TokenMint or a registration’s initial mint always deposits a note of some other asset, and a BridgeBurn’s or a TokenBurn’s burn_a always destroys one (RAND burned through burn_a is refused). fees_paid and burned are counted from a bundle’s fee and burn_r alone, and a token transfer inside the pool moves no counter at all, since its asset is not even public. Since chain 20 an Invoke can also take a token in through burn_a: into the program’s vault (deposit), which leaves its supply unchanged, or destroyed (burn), for the program’s own token only. The bridge’s fee notes (since chain 20, 10 bps of each zUSD deposit and burn, to a treasury address) are zUSD, never RAND: issued and backed, so a deposit’s backing and the supply move by the gross and a burn’s fall by amount − fee. A token’s own supply is the registry’s public count: rand_getTokenSupply returns total_supply and, for a bridged token, each backing’s locked amount, which sum to it (RPL tokens). On chain 21 the perps engine’s collateral is zUSD too, so a trading account’s deposits and payouts are token crossings, outside this audit (the perps_rand_* counters exist for a market collateralised in RAND and read 0).
The audit identity
Six monotonic counters in units (1 RAND = 10⁹), three that record what was destroyed, two for the program vaults (since chain 20), and on chain 21 five for the multisig accounts and two for the perps engine:
| counter | sums |
|---|---|
genesis_deposited | the alloc deposit notes in the genesis file |
genesis_staked | the stakes genesis seeded the register with |
faucet_minted | every accepted Mint |
withdraw_deposited | the notes accepted Withdraws created (amount − 0.001 RAND each) |
fees_paid | every bundle fee |
burned | every bundle burn_r (a Bond’s stake, since chain 20 an invoke’s vault deposit, on chain 21 a multisig creation’s or deposit’s), every burned registration fee, and on chain 21 every burned fee floor |
slashed | slashed aggregator bonds (block aggregation; 0 while it is off) |
registration_fees_burned | the registration fees burned under tokens.burn_registration_fee, also inside burned |
base_fees_burned | the bundle floors burned under the genesis fees section (chain 21: burn_base with burn_floor), also inside burned; "0" on every earlier chain |
program_rand_out | RAND notes program vaults have paid out (RPL-2; 0 without a program_state section) |
program_rand_held | RAND the vaults still hold: every invoke’s burn_r less program_rand_out. It got there through burned, so it is the vaults’ side of that counter |
multisig_issued | the multisig accounts’ genesis balances (chain 21: the 100,000 RAND treasury) — issuance, beside genesis_staked |
multisig_rand_in | every burn_r a CreateMultisig or MultisigDeposit put in; inside burned, pool to register |
multisig_rand_out | every RAND note a MultisigPay created: value entering the pool, beside withdraw_deposited |
multisig_base_out | the 0.001 RAND bases payments paid out of accounts into proposers’ rewards: register to register, not a crossing |
multisig_rand_held | issued + rand_in − rand_out − base_out: what the accounts hold, the register’s side of total_supply |
perps_rand_out, perps_rand_held | the same pair for the perps engine’s trading accounts, for a market collateralised in RAND; 0 on chain 21, whose market is on zUSD |
pool_value = genesis_deposited + faucet_minted + withdraw_deposited
+ program_rand_out + multisig_rand_out + perps_rand_out − fees_paid − burned
register_total = Σ over the register of (stake + pending + rewards)
total_supply = pool_value + register_total + program_rand_held + multisig_rand_held + perps_rand_held
issued = genesis_deposited + genesis_staked + faucet_minted + multisig_issued
invariant: total_supply == issued − slashed − registration_fees_burned − base_fees_burned
pool_value is what entered minus what left — the same number as the sum of the notes, which nobody can compute. A withdraw’s 0.001 RAND base is not a crossing: it moves from one register row to the proposer’s, and only the note crossed. invariant_holds being false is a consensus bug or a damaged database, never a legitimate state. The program, multisig and perps terms are zero on a chain without the program_state, multisig or perps section, and base_fees_burned without a fees section, so the identity is chain 19’s there; on chain 20 only the program terms were live, and on chain 21 all of them. A genesis vesting section, which no chain has had, would add one more public register the same way (JSON-RPC).
Followed through one bond and one withdraw of 1,000 RAND on a chain that deposited 2,000 and staked 4,000:
| after | burned | withdraw_deposited | fees_paid | pool_value | register_total | total_supply |
|---|---|---|---|---|---|---|
| genesis | 0 | 0 | 0 | 2,000 | 4,000 | 6,000 |
| a bond of 1,000 (fee 0.001) | 1,000 | 0 | 0.001 | 999.999 | 5,000.001 | 6,000 |
| its unbond | 1,000 | 0 | 0.001 | 999.999 | 5,000.001 | 6,000 |
| its withdraw of 1,000 | 1,000 | 999.999 | 0.001 | 1,999.998 | 4,000.002 | 6,000 |
Reading it
curl -s http://127.0.0.1:8545 -H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"rand_getSupply","params":[]}'
{ "height": 1998,
"genesis_deposited": "2000000000000", "genesis_staked": "4000000000000",
"faucet_minted": "0", "withdraw_deposited": "999999000000",
"fees_paid": "1000000", "burned": "1000000000000",
"slashed": "0", "registration_fees_burned": "0", "base_fees_burned": "0",
"pool_value": "1999998000000", "register_total": "4000002000000",
"total_supply": "6000000000000", "invariant_holds": true }
Every amount is a decimal string of units, because a JSON number is not an exact integer past 2⁵³. The full object carries more fields than shown here, among them the faucet’s per-epoch pair faucet_epoch and faucet_minted_in_epoch, which chain 21 checks against its budget, program_rand_out and program_rand_held (since v0.6.8), base_fees_burned, the five multisig_* and the two perps_* counters (since v0.8.0), and four vesting_* counters that read "0" on chain 21, whose genesis has no vesting section (JSON-RPC). The explorer serves the same object at GET /supply and the headline figures in GET /stats.
How exact it is
For everything the chain did itself the counters are exact: each is incremented in the code path that applies the action, from the action’s own public fields.
Two entries used to rest on a claim, and chain 14 turns both into checks (audit v3, POOL-1):
- A faucet
Mintcarries the note’s opening. Besidecmandamountthe action publishespk,timeandr. Admission recomputes the commitment of(pk, no sender, amount, asset 0, time, r)and refuses a mismatch withthe mint's commitment does not open to its published note and amount, and holdstimeto the bundle window. The minter signs all seven fields under the domainrand-mint-2. Before this, a validator could publish one amount and seal a note worth another. The cost is that a faucet note’s ownerpkis public, as a withdraw’s payout is;rand_mint’s parameters are unchanged. - A genesis
allocnote carries its opening too. On a chain with atokenssection every row is{ cm, envelope, amount, opening: { pk, time, r } }, and a node recomputes the commitment, at asset 0 unless the opening names a listed token’sasset, and refuses a genesis file whosecmdoes not match. Only asset-0 rows count towardsgenesis_deposited. An opaquecmwas the one way a genesis author could put an unbacked note, of any asset, into the pool without anyone able to check it. A genesis note’s owner and amount are therefore public by design once the file is published; only when and into what it is later spent stays private.rand-node genesisalways writes the opening.
On a chain cut without a tokens section the older rule still holds: genesis_deposited is the sum of the declared amount fields, trusted from the genesis file. Both genesis amounts are in the genesis hash, so every node agrees on them either way.
The counters are deliberately not consensus state — the state root does not hash them — and rand-node verify --mode quick recomputes every one by replaying the chain, reporting a mismatch if the stored snapshot disagrees.