Benzo's mainnet go/no-go was executed on 2026-07-09: every gate below passed and
the Avalanche C-Chain (43114) deploy went live. This document is the record of that
decision — the checklist annotated with outcomes, the honest account of what was a
config flip versus genuinely new work, and the post-deploy items that remain.
Outcome: GO (2026-07-09). The Groth16 ceremony completed, the deploy:mainnet
guardrails passed on a C-Chain fork dry-run, the deploy broadcast, and the CCTP onramp
was proven end-to-end with a real cross-chain burn. The deploy:mainnet command still
refuses to send a transaction unless every guardrail passes — on 2026-07-09 they all
did (the verifiers are now a real ceremony build, not the dev setup).
Mainnet is C-Chain converter only. BenzoNet (the permissioned L1, chain id
68420) is excluded from mainnet and stays testnet-only.
Full manifests: contracts/deployments/avalanche.json
and packages/config/src/deployments/avalanche.json;
human-readable cross-network table in DEPLOYMENTS.md.
| Contract | Address (43114) |
|---|---|
EncryptedERC converter |
0x708d0b83461973F46041a36f588b8760dbC0Db0e |
Registrar |
0x902B8D5585A5124C9B9c001A95b7f520C07a79F2 |
BabyJubJub |
0x91eb19da5A7486b4AAb4a0e452299B7E6F3821F4 |
| registration / mint / transfer / withdraw / burn verifiers | 0x35b4C4…5CaA / 0xb0ea11…C972 / 0x4A7160…9f01 / 0xDf3caC…fdb7 / 0xCb59d3…9bc3d |
PrivateGiftEscrow |
0xb22c366e000165683A51C2630F6Ab818e5227C94 |
BenzoCCTPRouter |
0x83F26C562082e3c455938fd48162e990494a4caE |
USDC (tokenId 1) / EURC (tokenId 2) |
0xB97EF9Ef…48a6E / 0xC891EB4c…c2ACD |
| Auditor account | 0x5ba6F05b245C06c3a4C05e7bC4486dE3661393ea |
Deployer / current Ownable admin |
0x09b67991141146e2A43651C72CF6786eeb579846 |
Ceremony: Groth16 phase-2, drand quicknet round 30261477, transcript
ceremony/transcript.md. CCTP onramp settle tx:
0xc479b7c8d7a62fde5189d5c03b7f7fe8b5b4ad44afd42eea1aaf194c7556f8a3.
Benzo was built config-driven from the start: every address resolves from
@benzo/config / the deployment manifests, and networks are keyed by a staging
(testnet) vs production (mainnet) tier. So the majority of the cutover is data,
not code.
| Item | Testnet (staging) | Mainnet (production) |
|---|---|---|
| RPC | https://api.avax-test.network/ext/bc/C/rpc |
https://api.avax.network/ext/bc/C/rpc |
| Explorer | testnet.snowtrace.io |
snowtrace.io |
| chainId | 43113 |
43114 |
| CCTP domain | 1 |
1 (unchanged — a domain is chain-family) |
CCTP TokenMessengerV2 |
0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA |
0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d |
CCTP MessageTransmitterV2 |
0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275 |
0x81D40F21F12A8F0E3252Bccb954D722d4c464B64 |
| USDC | 0x5425890298aed601595a70AB815c96711a31Bc65 |
0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E |
| EURC | 0x5E44db7996c682E92a960b65AC713a54AD815c6B |
0xC891EB4cbdEFf6e073e859e987815Ed1505c2ACD |
| Attestation base | iris-api-sandbox.circle.com |
iris-api.circle.com |
| Secrets | staging APP_MASTER_KEY / OPS_PRIVATE_KEY / deployer / auditor / DATABASE_URL |
separate prod values for each |
Separate prod secrets are enforced by the backend config, not just convention: a
blob sealed with the staging APP_MASTER_KEY cannot be unsealed with the prod key
(AES-256-GCM tag mismatch), and CHAIN_ENV=avalanche pointed at Fuji addresses
throws at startup.
- Token-agnostic per-network deploy — the converter wraps whatever each network
declares; mainnet wraps real Circle USDC, never a
TestUSDC. avalanchenetwork wiring —hardhat.config.tsnetwork + Routescan mainnet verification path;eerc-deployments.tschainId guard,snowtrace.iolinks, and a{fuji, avalanche}verify set (the BenzoNet Blockscout path is unchanged).- Guard-railed
deploy:mainnet— a single command that refuses to proceed unless every guardrail passes (see below).
- Production Groth16 ceremony — complete (2026-07-09). The dev verifiers
(
contributionSettings.contributions: 0) were replaced by a real phase-2 ceremony: three sequential contributions on separate ephemeral machines, sealed with the public drand quicknet beacon (round 30261477); all five verifiers regenerated and the browser proving keys re-coupled. Transcript:ceremony/transcript.md. It was a single-coordinator, 3-machine run rather than an open multi-party ceremony — its soundness rests on the published, re-verifiable transcript plus the unbiasable beacon, not on a large set of independent external participants.
pnpm --filter @benzo/contracts deploy:mainnet (→ scripts/deploy/deploy-mainnet.ts,
guardrails in scripts/deploy/mainnet-guardrails.ts) aborts non-zero, sending no
transaction, on any of:
MAINNET_CONFIRM=1missing (checked first, before any RPC read).- RPC chainId ≠
43114. - Any wrapped token is
deploy-test, or USDC is not the existing mainnet USDC. - The verifiers are not a ceremony build (asserted against the committed ceremony marker — see #121 below).
PRIVATE_KEY(deployer) equalsPRIVATE_KEY_2(auditor), or either is missing.- Deployer AVAX balance is below the floor.
- The mainnet auditor key is not operator-provided (
MAINNET_AUDITOR_PUBKEY); the deploy never auto-generates it.
Proven by contracts/test/benzo/MainnetGuardrails.test.ts (each guardrail → abort,
no block mined). On 2026-07-09 all seven passed on a C-Chain fork dry-run with the
real ceremony build in place, and the guarded deploy then ran against mainnet.
contracts/scripts/ceremony/ceremony-marker.json records, per circuit, the sha256 of the
verifier .sol that a given trusted setup produced, plus build (dev vs
ceremony), the contribution count, and the beacon. It is now build: "ceremony"
(drand round 30261477), so guardrail #4 passes. scripts/ceremony/run-ceremony.ts
is the operator tooling that ran the real ceremony and rewrote the marker; the
regenerated verifiers were deployed to both Fuji and mainnet, and the transcript is
published at ceremony/transcript.md.
Each box maps to an M5 issue (#120 wiring + guardrails, #121 ceremony, #122 docs). Outcomes are annotated inline; unchecked boxes are the remaining post-deploy work.
- Production ceremony done — all five verifiers regenerated from a phase-2
ceremony (three ephemeral-machine contributions + drand quicknet round
30261477); transcript published at
ceremony/transcript.md; ceremony marker flipped tobuild: "ceremony". (#121) - Deploy code proven on Fuji wrapping REAL Circle USDC — the token-agnostic
converter wraps Circle testnet USDC (
0x5425…Bc65) on Fuji,isConverter()is true, deposit → transfer → withdraw round-trips (17/17 real-funds flows). (#120) - CCTP router + deposit-on-behalf proven on testnet — one-tap cross-chain
deposit lands into the converter on the user's behalf; re-proven on mainnet by a
0.1 USDC Base→Avalanche burn (settle tx
0xc479b7c8…f8a3). (#120, prior milestones) -
avalanche.jsonmanifest populated + config parity — real deployed addresses written tocontracts/deployments/avalanche.jsonandpackages/config/src/deployments/avalanche.json(tierproduction); noplaceholderflag. (#120) - Separate prod secrets + DB provisioned — distinct prod
APP_MASTER_KEY,OPS_PRIVATE_KEY, deployer (0x09b6…9846), auditor (0x5ba6…93ea, distinct from the deployer), and a non-localDATABASE_URL. (#120) - Deployer funded with mainnet AVAX — above the
deploy:mainnetbalance floor (the deploy broadcast successfully). (#120) - Auditor key custodied + sealed — the mainnet auditor BabyJubJub key is
operator-provided and set on-chain (account
0x5ba6…93ea); its private half is a local operator secret, never committed. (#120) - All
deploy:mainnetguardrails pass in a fork dry-run — on a C-Chain fork, all seven guardrails were green; the deploy then ran against mainnet. (#120) - Contracts source-verified on
snowtrace.iopost-deploy — complete. All ten mainnet contracts (the converter,Registrar, all five verifiers,BabyJubJub,PrivateGiftEscrow, andBenzoCCTPRouter) are source-verified on Snowtrace and carry averifiedAtin the manifest. (#120)
- Transfer
Ownableadmin off the hot deploy key. Admin is still the deployer (0x09b67991141146e2A43651C72CF6786eeb579846); move it to a multisig / cold wallet. Top priority. - Finish Routescan source-verification — all ten mainnet contracts are now
source-verified on Snowtrace and the manifest
verifiedflags are updated. - Harden the CCTP onramp beyond the single test burn — load, failure-injection, and adversarial testing before treating the onramp as production-grade.
- This is a fresh, unaudited deployment. The
Ownableadmin is still the hot deploy key; until it is moved to a multisig / cold wallet, a single compromised key controls the mainnet contracts. That transfer is the top post-deploy item. - The ceremony was single-coordinator, not open multi-party. Its trust rests on the published, re-verifiable transcript plus the unbiasable drand beacon — not on a large set of independent external participants.
- The CCTP onramp is proven by one 0.1 USDC test burn, not load-tested or adversarially hardened. The eERC deposit-on-behalf patch behind CCTP one-tap is a real code path, not a config value.
- W3 (selective disclosure / proof-of-payment) is reveal-and-verify, not a zero-knowledge disclosure circuit: it reveals the underlying values and verifies them on-chain. This is intentional and is stated plainly rather than overclaimed.