Skip to main content

Available Networks

toda is deployed as a pair of Anchor programs on Solana. The commitment program is the protocol; the staking program is dormant until the token launches.

Programs

ProgramIDPurpose
toda-commit5SwaBH3pXzEKhmL6pbqZ1JgeDiGaf6i5oVj8951RX9smPer-user commitment accounts
toda-stakingDPV1ytT8jGMwUx4FKZJRXs6MTCsxmTA8nQfkEtAM2kaeSelf-custody stake vaults

Both program IDs are declared in Anchor.toml for devnet and localnet.

Current cluster

toda-commit is live on devnet. Devnet state is not permanent and the cluster is reset periodically - anchor on devnet to build and test, not to make claims that must survive.

toda-staking is declared but not deployed to any cluster. Nothing requires it until staking activates; its test suite runs the compiled program in LiteSVM, without a validator. See Staking.

Cluster configuration

VariableDefaultNotes
SOLANA_RPC_URLhttps://api.devnet.solana.comAny Solana RPC endpoint
TODA_PROGRAM_ID-Required. The commitment program
ANCHOR_AUTHORITY_KEYPAIR./authority-keypair.jsonThe anchoring authority. Gitignored - never commit it
ANCHOR_INTERVAL_MS300000Anchor service sweep interval
SIWS_CHAIN_IDdevnetWritten into the SIWS message the wallet signs

Generate and fund a devnet authority with:

bin/dev-keypair.sh

The script is idempotent and the resulting keypair is gitignored.

Account layout

The commitment account is a fixed-size PDA. It does not grow with the log.

FieldBytesType
Anchor discriminator8-
owner32Pubkey
authority32Pubkey
merkle_root32[u8; 32]
memory_count8u64 (LE)
nonce8u64 (LE)
last_updated8i64 (LE)
bump1u8
Total129

A user with one memory and a user with ten million memories occupy the same 129 bytes.

Deriving addresses

Both PDAs derive from the owner's wallet, so any client can compute them without contacting toda.

import { PublicKey } from "@solana/web3.js";

// The commitment account.
const [commitPda] = PublicKey.findProgramAddressSync(
[Buffer.from("user"), owner.toBuffer()],
TODA_COMMIT_PROGRAM_ID,
);

// The stake authority (whose associated token account is the vault).
const [stakeAuthority] = PublicKey.findProgramAddressSync(
[Buffer.from("stake"), owner.toBuffer()],
TODA_STAKING_PROGRAM_ID,
);

@toda/solana exposes these as deriveUserCommitPda() and deriveStakeAuthorityPda().

Service ports

Local development defaults:

ServicePortIn docker-compose.yml
Dashboard3000
API3001
Anchor service-✅ (worker)
Extension API3002❌ run with bun run dev
Docs3003❌ run with bun run dev
Postgres5432

Protocol constants

Defined in @toda/config/constants and shared by every component. They are part of the wire format - see the commitment log.

DOMAIN_SEP_LEAF = "toda:leaf:v1"
DOMAIN_SEP_NODE = "toda:node:v1"
USER_COMMIT_SEED = "user"
STAKE_AUTHORITY_SEED = "stake"
BATCH_SEAL = { maxLeaves: 64, maxAgeMs: 300_000 }

Moving to mainnet

Anchoring on mainnet makes commitments permanent and publicly meaningful. Before you do:

  1. Deploy toda-commit to mainnet and set TODA_PROGRAM_ID accordingly.
  2. Point SOLANA_RPC_URL at a mainnet endpoint you control or pay for. Public endpoints rate-limit, and the anchor service reads the nonce before every commit.
  3. Set SIWS_CHAIN_ID=mainnet so the message users sign names the right cluster.
  4. Fund the authority keypair with enough SOL to cover sustained anchoring, and monitor its balance. An unfunded authority means anchoring silently stops - the tip goes stale while writes continue to succeed.
  5. Read production readiness first.
Commitments do not migrate

A commitment account is a PDA under a specific program ID on a specific cluster. Moving clusters means starting a new log - the devnet tip has no meaning on mainnet. Anchor on the cluster whose permanence you actually need.