Mainnet day — Phase 2 TGE + desks (4663)
The Phase 2 mainnet-day checklist for broadcasting on 4663.
One-page checklist for when you are ready to broadcast. Do not run this
until you mean it. .env stays gitignored.
46630 testnet vault 0x8ff6… is unchanged (no router, no $SEAT).
Full Phase 2 spec: phase-2.md. Cited 4663 facts:
phase-1-live.md.
0. Pre-flight
-
phase-2merged (or you are on the branch that hasDeployPhase2.s.sol) -
forge test,pnpm typecheck,make paper,pnpm --filter keeper testgreen - Deploy wallet (
OWNER= broadcaster) funded with ETH on 4663 - Leader wallet(s) opted in and ready (you choose addresses; script never invents them)
-
KEEPER_ADDRESShot wallet funded (optional but needed for live copies) -
PROTOCOL_FEE_RECIPIENTtreasury address decided -
SEAT_HOLDERdecided (defaultOWNER; receives full 1B mint)
Choose one path:
| Path | When |
|---|---|
make deploy-phase2 | Full stack: $SEAT, staking, factory, up to 3 desks (recommended if nothing live on 4663 yet) |
make deploy-mainnet | Phase 1 only: one capped desk, no $SEAT — skip if you want Phase 2 in one shot |
This runbook assumes deploy-phase2.
1. .env (local only)
Copy from .env.example. Minimum for broadcast:
# RPC
RH_RPC_URL=https://rpc.mainnet.chain.robinhood.com
NEXT_PUBLIC_RH_RPC_URL=https://rpc.mainnet.chain.robinhood.com
CHAIN_ID=4663
NEXT_PUBLIC_CHAIN_ID=4663
# Signer (must match OWNER)
PRIVATE_KEY=...
OWNER=0x... # same as broadcaster
# Dual confirm — both required
CONFIRM_MAINNET=I_UNDERSTAND
CONFIRM_SEAT_TGE=I_UNDERSTAND
# Official USDG (script rejects anything else)
USDG_ADDRESS=0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168
# Leaders — unset = skipped; listDesk covers later
LEADER_ADDRESS=0x...
# LEADER_2=0x...
# LEADER_3=0x...
# Ops
KEEPER_ADDRESS=0x...
PROTOCOL_FEE_RECIPIENT=0x...
SEAT_HOLDER=0x... # optional; defaults to OWNER
LISTING_BOND_SEAT=100000000000000000000000 # 100_000e18 default
# Live keeper (after deploy)
SWAP_ROUTER_CONFIGURED=1
EXECUTION_MODE=LIVE
SEAT_SUBMIT_TX=0 # set 1 only when ready to submit copy txs
DESK_FACTORY=0x... # from deploy logs; or DESK_ADDRESSES=0x...,0x...Optional risk caps (defaults in script): MAX_FILL_USDG, MAX_POSITION_USDG,
MAX_GROSS_USDG, MAX_DRAWDOWN_BPS, MAX_STALENESS_SEC.
2. Dry run (no broadcast)
cd seat
export PATH="$HOME/.foundry/bin:$PATH"
set -a && . ./.env && set +a
forge script contracts/script/DeployPhase2.s.sol --rpc-url robinhoodExpect OWNER must be the broadcaster if OWNER ≠ signer. Fix before
broadcasting.
3. Broadcast
make deploy-phase2Script deploys (single tx batch):
SeatToken→SEAT_HOLDER(1B mint)StakingPool,LpLockerRiskModule,SwapAdapter,ExactInputRouter02,FeeModule(70/20/10)ChainlinkOracle+ NVDA/AAPL/SPY feedsDeskFactory+setListingParams($SEAT, bond)createDeskfor each setLEADER_*(oracle, fees, $50k cap, MAG7, session bps, keeper)
Save console output — addresses for step 4.
LP: script logs “seed+lock later” unless SEAT_LP_USDG is set. Minting the
Uni v3 position is not automated.
4. Wire the app
pnpm exec tsx scripts/write-addresses.tsVerify app/src/lib/addresses.ts on 4663 has non-null:
seatToken,stakingPool,lpLocker,deskFactory,deskVault, …
Restart app with NEXT_PUBLIC_CHAIN_ID=4663:
make app-devBlotter should show Phase 2 · $SEAT, desk picker when deskCount > 1,
stake + list-desk forms.
5. Post-deploy owner txs (manual)
Order is flexible; typical sequence:
5a. $SEAT bucket transfers
From SEAT_HOLDER, transfer per phase-2.md buckets (40/20/15/12/8/5).
Do not invent team/airdrop addresses in the constructor — allocate now.
5b. SEAT/USDG liquidity + lock
Cited NPM (4663):
0x73991a25C818Bf1f1128dEAaB1492D45638DE0D3
- Create / use SEAT–USDG pool on Uni v3 (fee 3000) if needed
- Mint position via NPM
LpLocker.lock(npm, tokenId, 365 days, beneficiary)from owner- Confirm
unlockTime≥ 365 days; no early withdraw
5c. Optional staker bootstrap
Transfer $SEAT to stakers or seed messaging; optional USDG is not required
for the pool to work (rewards come from desk fees via notifyReward).
5d. Extra desks
- Owner:
factory.createDesk(leader)(no bond) - Anyone: approve bond →
factory.listDesk(leader)via blotter or wallet
6. Keeper
# .env: CHAIN_ID=4663, DESK_FACTORY=<from logs>, SWAP_ROUTER_CONFIGURED=1
make keeper-mainnetKeeper walks factory.allDesks when DESK_FACTORY is set (or use
DESK_ADDRESSES). Fill tape rows include desk.
Start with SEAT_SUBMIT_TX=0 (dry-run copies). Set SEAT_SUBMIT_TX=1 only
after vault has USDG and you accept live swap risk.
7. Smoke test checklist
- Blotter on 4663: NAV / cash / leader read from vault
- Deposit ≤ $50k USDG on desk 1; redeem instant when cash available
- Stake
$SEAT→ after fee accrual, claim USDG from pool -
listDeskposts bond; new vault appears in picker (?desk=) - Keeper writes tape with correct
deskfield - 46630 vault still works unchanged (optional regression)
8. Rollback / mistakes
- There is no admin upgrade path on deployed vaults.
- Wrong deploy: do not reuse addresses in git; deploy fresh only with
new CONFIRM intent and update
write-addresses.tsfrom new broadcast. - Never commit
.env,PRIVATE_KEY, or broadcast logs with secrets.
Quick reference
| Item | Value |
|---|---|
| Chain | Robinhood mainnet 4663 |
| USDG | 0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168 |
| SwapRouter02 | 0xCaf681a66D020601342297493863E78C959E5cb2 |
| NPM | 0x73991a25C818Bf1f1128dEAaB1492D45638DE0D3 |
| Deposit cap | $50k USDG per desk |
| Fee split | 70% leader / 20% protocol / 10% stakers |
| Listing bond default | 100_000 $SEAT |