Guides · Going to mainnet
Testnet forgives everything; mainnet forgives nothing. This is the staged cutover we recommend: prove the failure paths on testnet, cut keys and contracts over cleanly, then go live small and grow with evidence. Each stage gates the next — skip none of them.
The happy path proves your integration works. The drills prove it survives. Before cutting over, run the full loop and the three failure drills on testnet: a payer that goes silent mid-stream, a challenge filed during a thaw, and a forced settlement with zero cooperation from the counterparty. Each drill should pass twice in a row, unattended.
# all four pass, twice in a row, before any mainnet work vela drill happy-path # open → receipts → RAV → settle vela drill stopped-payer # payer goes silent mid-stream vela drill challenge-thaw # dispute a thawing withdrawal vela drill forced-settle # redeem at threshold, no help
Production signing keys are generated fresh for mainnet — never reused from testnet, no exceptions. Testnet keys have been pasted into CI logs, shell histories, and teammates' laptops for months; assume they are public. Generate the new keys hardware-backed where possible, and give payer and receiver roles separate keys so one leak never signs both sides.
# fresh keys, hardware-backed — testnet keys never migrate vela keys generate --env production --backend hsm vela keys list --env production # payer: 0x4c…e1 (hsm, created 2026-07-02) # receiver: 0xb8…07 (hsm, created 2026-07-02)
The cutover itself is a small config diff, which is exactly why it deserves review like a big one. Switch the network, switch the RPC, and pin the contracts package to an exact version — a floating tag that's fine on testnet is how production settles against contracts you never audited.
export default { - network: "vela-testnet", - contracts: "@velapay/contracts@next", - rpc: process.env.TESTNET_RPC, + network: "vela-mainnet", + contracts: "@velapay/contracts@2.4.1", // pinned + rpc: process.env.MAINNET_RPC, thaw: "72h" }
Day one on mainnet, every counterparty is new again — testnet history proves your code, not their credit. Start thresholds at pocket-change levels and escrows at a fraction of the real task budget, then grow both with clean settlement history using the sizing method in choosing trust thresholds. Painfully conservative for week one is the point.
const receiver = new Receiver({ threshold: "$25" // raise with history, not hope }) const escrow = await payer.openEscrow({ amount: "$100.00", // small until drills pass in prod thaw: "72h" })
Three alerts are non-negotiable before real money flows: a balance approaching its threshold, a RAV redemption that fails, and a gas budget being exceeded. The failed-redemption alert is the one teams skip and regret — a receiver that can't settle is quietly extending unlimited credit while everything looks healthy.
receiver.on("threshold:near", (e) => pager.warn(e)) receiver.on("redeem:failed", (e) => pager.page(e)) // a receiver that can't settle is extending unlimited credit payer.on("gas:budget-exceeded", (e) => pager.page(e))
Every line checked, in order, before the first production receipt is signed:
Be honest with yourself about this one: mainnet mistakes cost real money and cannot be rolled back. There is no faucet, no reset, no support ticket that reverses a settled RAV or an expired thaw you forgot to challenge. Rehearse the failure drills until they're boring — the demo path was never the risk.
The quickstart is the loop you'll be running with real money, and the API reference documents every config, key, and event option this checklist touches. Track contract releases on the changelog before you re-pin.