Skip to main content

Deployment runbook

Deploy order matters: contracts → indexer → frontend (each needs the previous one's output).

0. Prerequisites

  • A funded deployer key on the target chain (testnet faucet).
  • pnpm install at the repo root.
  • A v2-style router for fee conversion. Testnet uses the included mock (deployed by the script); mainnet uses the chain's real Uniswap deployment.

1. Contracts (Foundry)

cd contracts
cp ../.env.example .env # DEPLOYER_PRIVATE_KEY, PROTOCOL_TREASURY, BLOCKSCOUT_API_KEY
forge test # full suite should pass

# Testnet 46630 — deploys the protocol + 18 mock stocks + mock router, funds & allow-lists them
forge script script/DeployTestnet.s.sol \
--rpc-url robinhood_testnet --broadcast --verify \
--gas-estimate-multiplier 600 --slow
Testnet gas estimation

Robinhood testnet's L1-data-gas component is under-weighted by the node's eth_estimateGas — small transactions out-of-gas at default multipliers. Always pass --gas-estimate-multiplier 600 --slow.

The script writes contracts/deployments/<chainId>.json with every address. To seed the demo MAG7 index afterwards: forge script script/SeedDemo.s.sol ….

Register basket assets on an existing deployment (owner-gated):

cast send <FEE_REGISTRY> "setAssets(address[],bool)" "[0x…,0x…]" true \
--rpc-url robinhood_testnet --private-key 0x…

2. Indexer (Ponder on Railway)

railway login
railway init # or connect the GitHub repo, root = indexer/
railway add --database postgres # provisions DATABASE_URL

Service variables (from deployments/<chainId>.json):

CHAIN_ID=46630
PONDER_RPC_URL=https://rpc.testnet.chain.robinhood.com
INDEX_FACTORY_ADDRESS=<indexFactory>
FEE_PROCESSOR_ADDRESS=<feeProcessor>
PROTOCOL_TREASURY_ADDRESS=<protocolTreasury>
START_BLOCK=<deploy block>

Deploy with railway up (config in indexer/railway.json: start command pnpm start --schema $RAILWAY_DEPLOYMENT_ID, healthcheck /ready).

Transient RPC flakes

Deploys can crash-loop on transient Orbit-RPC BlockNotFoundError during realtime sync — the code is fine; re-run railway up until a deploy stabilizes to SUCCESS.

3. Frontend (Next.js on Vercel)

The app lives in web/ and is self-contained. Either import the repo in Vercel with Root Directory = web, or:

cd web && vercel --prod

After a fresh contract deploy, sync web/lib/contracts.ts and web/lib/shared.ts (ASSETS[<chainId>]) from the new deployments/<chainId>.json, and set the NEXT_PUBLIC_* env vars (chain id, indexer URL, contract addresses — inlined at build time, so redeploy after changing).

4. Optional: convert poker

keeper/keeper.ts is a permissionless convenience cron — it computes safe min-outs and calls FeeProcessor.convert for indexes with accrued fees. Run it anywhere (pnpm start), or don't: anyone can convert.

5. Mainnet promotion gate

Promote to 4663 only after all of: the tokenized-equity custody spike passes, professional re-audit of the high-severity areas, US-person geo-gating + legal review for stock baskets, oracle/TWAP-floored min-outs wired for permissionless conversion, and mainnet stock-buy routing confirmed (RFQ/0x vs AMM pools). See Security.