Rand Protocol
Documentation
Core concepts

Supply

How RAND is issued and destroyed, the public crossings of the pool boundary, chain 21's genesis issuance, what chain 21 burns, and the supply audit invariant every node checks.

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:

directioncrossingwhen
ina genesis deposit note (alloc in the genesis file, opened since chain 14)block 0
ina 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
ina validator’s Withdraw, which pays released stake and rewards into a noteany time
outa 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 proposerevery bundle
outa bundle’s burn_r: a Bond burning stake into a validator’s public stakeevery bond
outan Invoke’s burn_r: RAND deposited into a program’s public vaultsince chain 20 (v0.6.8)
ina vault payout: RAND an Invoke pays out of a program’s vault as a note, its amount and recipient publicsince chain 20 (v0.6.8)
outa 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
ina MultisigPay’s notes: RAND an account pays out, amount and recipient public (multisig_rand_out)chain 21, since v0.8.0
out / ina 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 therechain 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:

countersums
genesis_depositedthe alloc deposit notes in the genesis file
genesis_stakedthe stakes genesis seeded the register with
faucet_mintedevery accepted Mint
withdraw_depositedthe notes accepted Withdraws created (amount − 0.001 RAND each)
fees_paidevery bundle fee
burnedevery 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
slashedslashed aggregator bonds (block aggregation; 0 while it is off)
registration_fees_burnedthe registration fees burned under tokens.burn_registration_fee, also inside burned
base_fees_burnedthe 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_outRAND notes program vaults have paid out (RPL-2; 0 without a program_state section)
program_rand_heldRAND 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_issuedthe multisig accounts’ genesis balances (chain 21: the 100,000 RAND treasury) — issuance, beside genesis_staked
multisig_rand_inevery burn_r a CreateMultisig or MultisigDeposit put in; inside burned, pool to register
multisig_rand_outevery RAND note a MultisigPay created: value entering the pool, beside withdraw_deposited
multisig_base_outthe 0.001 RAND bases payments paid out of accounts into proposers’ rewards: register to register, not a crossing
multisig_rand_heldissued + rand_in − rand_out − base_out: what the accounts hold, the register’s side of total_supply
perps_rand_out, perps_rand_heldthe 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:

afterburnedwithdraw_depositedfees_paidpool_valueregister_totaltotal_supply
genesis0002,0004,0006,000
a bond of 1,000 (fee 0.001)1,00000.001999.9995,000.0016,000
its unbond1,00000.001999.9995,000.0016,000
its withdraw of 1,0001,000999.9990.0011,999.9984,000.0026,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 Mint carries the note’s opening. Beside cm and amount the action publishes pk, time and r. Admission recomputes the commitment of (pk, no sender, amount, asset 0, time, r) and refuses a mismatch with the mint's commitment does not open to its published note and amount, and holds time to the bundle window. The minter signs all seven fields under the domain rand-mint-2. Before this, a validator could publish one amount and seal a note worth another. The cost is that a faucet note’s owner pk is public, as a withdraw’s payout is; rand_mint’s parameters are unchanged.
  • A genesis alloc note carries its opening too. On a chain with a tokens section 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’s asset, and refuses a genesis file whose cm does not match. Only asset-0 rows count towards genesis_deposited. An opaque cm was 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 genesis always 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.

Last verified against fullnode commit c0878c3f (chain 21) on 2026-10-12. Where the code and this page disagree, the code is right; tell us.