Reference

Smart Contracts

RAIL20 runs on Base (chain 8453), Robinhood Chain (chain 4663, an Arbitrum Orbit L2), Arbitrum (chain 42161), and BNB Smart Chain (chain 56). It runs one independent zero-knowledge privacy pool contract per token on each chain - there is no single shared vault. Each chain has a native-asset pool plus stablecoin pool(s): USDC on Base, USDG on Robinhood, USDC on Arbitrum, and both USDC + USDT on BSC (18-decimal Binance-Peg tokens).

Contract Addresses

Base (chain 8453)

ContractAddressDescription
ETH Pool0x99205B045Fcf5ff2689ad6B038abF3dd35b79ddEZK privacy pool for ETH - UTXO commitments, Merkle tree, nullifiers
USDC Pool0x764FF96EabEeF2b197f845dD8D39441Ac68FDE11ZK privacy pool for USDC
USDC (token)0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913USDC token held by the USDC pool

Robinhood Chain (chain 4663)

ContractAddressDescription
ETH Pool0xBf6a26CEF8f6251B46Cc09D03A0f12Df5972d163ZK privacy pool for ETH on Robinhood
USDG Pool0x04F8d8E4C3f23fE2BfB80c6C46b7d7dfE6f853F0ZK privacy pool for USDG (Global Dollar)
USDG (token)0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168Global Dollar token held by the USDG pool

Arbitrum (chain 42161)

ContractAddressDescription
ETH Pool0xBf6a26CEF8f6251B46Cc09D03A0f12Df5972d163ZK privacy pool for ETH on Arbitrum
USDC Pool0x04F8d8E4C3f23fE2BfB80c6C46b7d7dfE6f853F0ZK privacy pool for native USDC on Arbitrum
USDC (token)0xaf88d065e77c8cC2239327C5EDb3A432268e5831Native Circle USDC (6 decimals) held by the USDC pool

Arbitrum and Robinhood share the same pool addresses because each pool was deployed from the same address at the same account nonce on both chains - the resulting contract address is deterministic and identical across chains. The deployments are independent contracts on separate chains; there is no shared state.

BNB Smart Chain (chain 56)

ContractAddressDescription
BNB Pool0xBf6a26CEF8f6251B46Cc09D03A0f12Df5972d163ZK privacy pool for native BNB
USDC Pool0x04F8d8E4C3f23fE2BfB80c6C46b7d7dfE6f853F0ZK privacy pool for Binance-Peg USDC (18 decimals)
USDT Pool0x8d484006882fDe2604968D5BE24D092DbAE2C04bZK privacy pool for Binance-Peg USDT (18 decimals)
USDC (token)0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580dBinance-Peg USDC token held by the USDC pool
USDT (token)0x55d398326f99059fF775485246999027B3197955Binance-Peg USDT token held by the USDT pool

BSC's BNB and USDC pool proxies share the Robinhood/Arbitrum addresses (same deployer + nonce, deterministic CREATE); the USDT pool is unique to BSC. Binance-Peg USDC and USDT are both 18 decimals, unlike the 6-decimal USDC on Base/Arbitrum - the client and API pin TOKEN_DECIMALS=18 for these pools. Same-chain BNB ↔ USDC/USDT swaps route via PancakeSwap V3 (router 0x13f4EA83D0bd40E75C8222255bc855a974568Dd4).

Fee recipient: 0xd962c7a67b74EEe8D418189f7917A18577A4775d. Relayer fees for every pool are paid to this address.

Staking & Governance

The RAIL20 staking and governance system lets stakers lock $RAIL20 to earn a share of protocol fees paid in WETH and to gain voting power over on-chain governance. Live on Robinhood Chain (4663).

ContractAddressDescription
$RAIL20 token0xda9176…e460Governance and staking token
Staking0x35F33E…fb5APer-position stake, daily snapshots, 30-day unlock cooldown
Voting0x0Ada81…f40dProposal lifecycle: create, sponsor, callVote, vote, execute
Delegator0x799067…9761Permissioned executor; owner is Voting
Treasury (proxy)0x73F795…C1BFHolds protocol fees; admin is Delegator
GovernorRewards (proxy)0x260c4a…e7BbDistributes treasury funds to stakers by voting power
ProxyAdmin0x123D13…Fa1cUpgrade admin for Treasury and GovernorRewards; owner is Delegator
Reward token (WETH)0x0Bd7D3…aD73Rewards are distributed in WETH

Governance parameters (identical to upstream): sponsorship threshold 500,000 $RAIL20 within a 30-day window, voting quorum 2,000,000, 30-day unlock cooldown, snapshot interval 1 day, reward distribution interval 14 days, distribution rate 2% of the treasury balance per interval (intervalBP = 200).

Ownership chain: Treasury, ProxyAdmin, and GovernorRewards are all owned or admin-controlled by Delegator; Delegator is owned by Voting. All privileged actions execute only through passed governance proposals.

Privacy Pool

Each pool is a self-contained ZK privacy contract for a single token. It holds shielded balances as UTXO notes, maintains a Poseidon commitment Merkle tree, verifies zk-SNARK proofs, and tracks spent nullifiers. On-chain, a spend reveals only commitments and nullifiers - never amounts or recipients. The proving circuit produces at most 2 outputs per transaction: outputs[0] is the recipient note and outputs[1] is the change note.

solidity
// Shield (deposit) into the pool: create up to 2 output commitments.
// USDC pool: caller approves the pool first. ETH pool: send value.
function shield(uint256 amount, uint256[2] calldata outputCommitments) external payable;

// Spend a note by proving ownership. A zk-SNARK proof reveals only the
// Merkle root, nullifiers, and new output commitments - never amounts.
// outputs[0] = recipient note, outputs[1] = change note (max 2 outputs).
function transact(
    uint256 root,
    uint256[] calldata nullifiers,
    uint256[2] calldata outputCommitments,
    address recipient,        // 0 = fully private (no on-chain withdraw)
    uint256 withdrawAmount,   // 0 = no public withdraw
    bytes calldata proof
) external;

UTXO Notes & Nullifiers

Shielding produces private UTXO notes. Each note commits to its value and owner as a Poseidon hash inserted into the pool's Merkle tree. To spend, the owner proves in zero knowledge that the note exists in the tree and publishes its nullifier; the nullifier marks the note spent without revealing which leaf it was. Double-spends are impossible because a nullifier can be published only once.

text
commitment  = Poseidon(amount, pubkey, blinding, mintAddress)
nullifier   = Poseidon(commitment, merklePath, signature)  // signature = Poseidon(privKey, commitment, merklePath)
merkleRoot  = Poseidon-based tree over all commitments (BN254 field)

Relayer Fee

Users submit proofs to a relayer, which broadcasts the transaction so the user pays no gas and their wallet never touches the chain. The relayer takes a flat fee plus a percentage; there is no on-chain fee-splitting contract. All fees go to the fee recipient address above.

PoolChainFlat feePercentage
ETH poolBase0.00025 ETH0.35%
USDC poolBase1 USDC0.35%
ETH poolRobinhood0.0002 ETH0.35%
USDG poolRobinhood0.5 USDG0.35%
ETH poolArbitrum0.00002 ETH0.35%
USDC poolArbitrum0.5 USDC0.35%

Integration Guide

typescript
import { createPublicClient, http } from "viem";
import { base } from "viem/chains";

const client = createPublicClient({ chain: base, transport: http() });

// Base (chain 8453)
const BASE_POOLS = {
  ETH:  "0x99205B045Fcf5ff2689ad6B038abF3dd35b79ddE",
  USDC: "0x764FF96EabEeF2b197f845dD8D39441Ac68FDE11",
};
// Robinhood Chain (chain 4663)
const ROBINHOOD_POOLS = {
  ETH:  "0xBf6a26CEF8f6251B46Cc09D03A0f12Df5972d163",
  USDG: "0x04F8d8E4C3f23fE2BfB80c6C46b7d7dfE6f853F0",
};
// Arbitrum (chain 42161) - same deterministic addresses as Robinhood
const ARBITRUM_POOLS = {
  ETH:  "0xBf6a26CEF8f6251B46Cc09D03A0f12Df5972d163",
  USDC: "0x04F8d8E4C3f23fE2BfB80c6C46b7d7dfE6f853F0",
};
// BNB Smart Chain (chain 56) - BNB + USDC share Robinhood/Arbitrum addresses; USDT is unique.
const BSC_POOLS = {
  BNB:  "0xBf6a26CEF8f6251B46Cc09D03A0f12Df5972d163",
  USDC: "0x04F8d8E4C3f23fE2BfB80c6C46b7d7dfE6f853F0",
  USDT: "0x8d484006882fDe2604968D5BE24D092DbAE2C04b",
};
const POOLS = BASE_POOLS;

// Read the current Merkle root of the ETH pool
const root = await client.readContract({
  address: POOLS.ETH,
  abi: poolAbi,
  functionName: "getLastRoot",
});

Backend API

The RAIL20 backend at https://api.rail20.org serves proof, balance, and settlement data used by the app:

EndpointPurpose
/api/healthService status
/api/balanceDerive shielded balance from notes
/api/assetsSupported tokens and pools
/api/deposit/preparePrepare a shield (deposit)
/api/withdrawRelay a private withdraw
/api/bridge/quoteCross-chain bridge quote
/api/swap, /api/swap-privateSwap-to-private flow
/api/intent-statusPoll cross-chain intent settlement