Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ If you're looking for MCP server support or React trust-badge components, those
| [Ecosystem Overview](docs/ecosystem.md) | Everyone — start here to understand the full picture |
| [Quickstart](docs/quickstart.md) | Developers — register your first agent in 10 minutes |
| [Robinhood Chain](docs/robinhood-chain.md) | Developers — deploy / test on Robinhood Chain (46630 / 4663) |
| [Mainnet Deploy & Role Rotation](docs/mainnet-deploy.md) | Operators — production deployment runbook for `4663` |
| [CounterAudit Integration](docs/counteraudit-integration.md) | Enterprise — embed agent identity in your audit trail |
| [AI Framework Integration](docs/ai-frameworks.md) | Developers — LangChain, AutoGen, CrewAI, Node.js |
| [Reputation Model](docs/reputation-model.md) | Everyone — how the 6-factor score works and grows |
Expand Down
267 changes: 267 additions & 0 deletions docs/mainnet-deploy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,267 @@
# Mainnet Deploy & Role Rotation

> **Status:** Draft runbook — not executed.
> **Target:** Robinhood Chain mainnet `4663`

This guide covers the end-to-end deployment and role-hardening sequence for
bringing Countersig protocol contracts to mainnet. It is intended for operators
responsible for deploying, verifying, and securing the production registry.

---

## Critical Script Split

Two deployment scripts exist with distinct purposes — do not confuse them:

| Script | What It Deploys | When to Use |
|---|---|---|
| `script/Deploy.s.sol` | Protocol: Identity + Reputation + Staking + **faucet** `CSIGToken` | Testnet today. Requires a mainnet variant (no faucet; bond = WETH/USDC or fixed `CSIG`) |
| `script/DeployMainnet.s.sol` | **TGE only:** Timelock + Treasury/Team vesting + fixed-supply `CSIG` | Token launch. Does **not** deploy Identity/Reputation/Staking |

The [oracle-first strategy](oracle-first.md) means you can ship **protocol livenet** without TGE. Bond token should be an established asset (WETH/USDC) or fixed-supply CSIG — never `CSIGToken.faucet()`.

---

## 1. Pre-flight Checklist

Complete these items before any broadcast on `4663`.

### People & Keys

- [ ] **Deployer EOA** funded with mainnet gas (Alchemy RPC preferred; public RPC is rate-limited)
- [ ] **Gov Safe** — 3-of-5 (proposers/cancellers for Timelock)
- [ ] **Slashing committee Safe** — separate 3-of-5 (or same Safe only if you explicitly accept concentration)
- [ ] **Oracle key** — HSM/MPC wallet that will hold `ORACLE_ROLE` (not the deployer long-term)
- [ ] Cold backup of all Safe recovery paths

### Decisions to Lock Before Deploy

- [ ] Bond token address on `4663` (WETH / USDC / CSIG)
- [ ] `MINIMUM_STAKE`, `CHALLENGE_PERIOD` (7d), `SCORE_CHALLENGE_WINDOW` (6h recommended for mainnet; testnet uses shorter), `UNBONDING_PERIOD` (21d)
- [ ] Whether Identity is still legacy `CountersigIdentity` or ERC-8004-only at cutover (see [ADR 0001](adr/0001-erc8004-as-identity-layer.md))
- [ ] Timelock delay **7 days** ([tokenomics §9](tokenomics.md) — locked at init)
- [ ] Audit complete / findings remediated

### Code Gaps to Close First

- [ ] Add `script/DeployProtocolMainnet.s.sol` (or extend Deploy) that:
1. Accepts `BOND_TOKEN` address (no faucet mint)
2. Deploys Identity / Reputation / Staking proxies
3. Optionally deploys or accepts an existing Timelock
4. Grants roles to oracle + committee
5. Transfers `DEFAULT_ADMIN_ROLE` + `UPGRADER_ROLE` to Timelock
6. Renounces deployer admin/upgrader
7. Writes `deployments/4663.json`
- [ ] Add `script/RotateRoles.s.sol` for post-deploy or testnet rehearsal
- [ ] Dry-run both on a fork of `4663` / rehearse role rotation on `46630`

---

## 2. Recommended Sequence

Protocol livenet (oracle-first):

```
A. Rehearse on testnet 46630 (role rotation only)
B. Deploy Timelock on 4663 (gov Safe = proposer/canceller)
C. Deploy protocol proxies with BOND_TOKEN + initial roles
D. Verify on Blockscout
E. Rotate admin/upgrader → Timelock; committee → Safe; oracle → HSM key
F. Renounce deployer privileges; assert with cast calls
G. Point hardened oracle + SDK at deployments/4663.json
H. Smoke: register → stake → epoch → meetsThreshold (small real stake)
```

TGE (`DeployMainnet.s.sol`) is **optional later** and independent of C–H if bonds are WETH/USDC.

---

## 3. Phase A — Testnet Rehearsal

On `46630`, deployer currently holds admin/oracle/committee (confirm against `deployments/46630.json`). Practice:

1. Deploy a **test** Timelock (48h delay OK for rehearsal; mainnet must be 7d).
2. Create/use a Safe as `COMMITTEE_ADDRESS`.
3. Run rotation script / cast sequence:
- `grantRole(DEFAULT_ADMIN_ROLE, timelock)` on Identity, Reputation, Staking
- `grantRole(UPGRADER_ROLE, timelock)` on all three
- `grantRole(ORACLE_ROLE, oracleHsm)` on Reputation
- `grantRole(SLASHING_COMMITTEE_ROLE, committeeSafe)` on Reputation + Staking
- `renounceRole` for deployer on admin/upgrader/oracle/committee as appropriate
4. Confirm deployer **cannot** `upgradeTo` or `setMinimumStake` anymore.
5. Confirm Timelock → schedule → wait → execute can still change a harmless param.
6. Confirm committee Safe can `initiateSlash` (on a throwaway agent).

Do **not** renounce testnet deployer until rehearsal is documented and reversible plan exists.

---

## 4. Phase B — Deploy Timelock on Mainnet

Either via a thin script or adapted from `DeployMainnet.s.sol` step 1:

```solidity
TimelockController(
minDelay = 7 days,
proposers = [GOV_MULTISIG],
executors = [address(0)], // permissionless execute after delay
admin = address(0) // timelock self-admins
)
```

Record: `timelock`, `govMultisig`.

---

## 5. Phase C — Deploy Protocol on `4663`

Intended env (for the future `DeployProtocolMainnet` script):

```bash
export DEPLOYER_PRIVATE_KEY=0x...
export BOND_TOKEN=0x... # WETH or USDC on 4663
export ORACLE_ADDRESS=0x... # HSM/MPC (or deployer temporarily)
export COMMITTEE_ADDRESS=0x... # Safe (or deployer temporarily)
export MINIMUM_STAKE=... # in bond-token wei
export CHALLENGE_PERIOD=604800 # 7 days
export SCORE_CHALLENGE_WINDOW=21600 # 6 hours
export UNBONDING_PERIOD=1814400 # 21 days

# dry-run
forge script script/DeployProtocolMainnet.s.sol --rpc-url robinhood_mainnet -vvvv

# broadcast + verify
forge script script/DeployProtocolMainnet.s.sol --rpc-url $ALCHEMY_ROBINHOOD_MAINNET \
--broadcast --verify --verifier blockscout \
--verifier-url https://robinhoodchain.blockscout.com/api/ -vvvv
```

Wiring (same as today's `Deploy.s.sol`):

1. Identity proxy (`admin = deployer` initially, `stakingCore = 0` then grant)
2. Reputation proxy
3. Staking proxy with `BOND_TOKEN`
4. `identity.grantRole(STAKING_CORE_ROLE, staking)`
5. `reputation.grantRole(STAKING_CORE_ROLE, staking)`
6. `reputation.grantRole(ORACLE_ROLE, oracle)`
7. `reputation` + `staking` `grantRole(SLASHING_COMMITTEE_ROLE, committee)`
8. Write `deployments/4663.json`

**Do not** deploy `CSIGToken` (faucet) on mainnet.

---

## 6. Phase D — Role Rotation

Assume temporary deploy left admin on deployer. Final state:

| Role | Identity | Reputation | Staking |
|---|---|---|---|
| `DEFAULT_ADMIN_ROLE` | Timelock only | Timelock only | Timelock only |
| `UPGRADER_ROLE` | Timelock only | Timelock only | Timelock only |
| `STAKING_CORE_ROLE` | Staking proxy | Staking proxy | n/a |
| `ORACLE_ROLE` | n/a | Oracle HSM (+ future operators) | n/a |
| `SLASHING_COMMITTEE_ROLE` | n/a | Committee Safe | Committee Safe |

### Cast-Style Sequence (per contract)

```bash
# Grant first, then renounce — never renounce admin before Timelock has it
cast send $REPUTATION "grantRole(bytes32,address)" $DEFAULT_ADMIN_ROLE $TIMELOCK \
--rpc-url ... --private-key $DEPLOYER
cast send $REPUTATION "grantRole(bytes32,address)" $UPGRADER_ROLE $TIMELOCK \
--rpc-url ... --private-key $DEPLOYER
# ... Identity + Staking same for admin/upgrader

cast send $REPUTATION "grantRole(bytes32,address)" $ORACLE_ROLE $ORACLE_HSM ...
cast send $REPUTATION "grantRole(bytes32,address)" $SLASHING_COMMITTEE_ROLE $COMMITTEE_SAFE ...
cast send $STAKING "grantRole(bytes32,address)" $SLASHING_COMMITTEE_ROLE $COMMITTEE_SAFE ...

# If deployer was temporary oracle/committee:
cast send $REPUTATION "renounceRole(bytes32,address)" $ORACLE_ROLE $DEPLOYER ...
cast send $REPUTATION "renounceRole(bytes32,address)" $SLASHING_COMMITTEE_ROLE $DEPLOYER ...
cast send $STAKING "renounceRole(bytes32,address)" $SLASHING_COMMITTEE_ROLE $DEPLOYER ...

# Last: renounce deployer admin + upgrader on all three
cast send $REPUTATION "renounceRole(bytes32,address)" $DEFAULT_ADMIN_ROLE $DEPLOYER ...
cast send $REPUTATION "renounceRole(bytes32,address)" $UPGRADER_ROLE $DEPLOYER ...
# Identity + Staking likewise
```

Prefer a single Foundry `RotateRoles.s.sol` that asserts `hasRole` after each step and reverts if Timelock/Safe/oracle missing roles.

### Post-Rotation Assertions

- [ ] `hasRole(DEFAULT_ADMIN_ROLE, deployer) == false` on all three
- [ ] `hasRole(UPGRADER_ROLE, deployer) == false` on all three
- [ ] `hasRole(DEFAULT_ADMIN_ROLE, timelock) == true` on all three
- [ ] Oracle HSM can `proposeReputation` (or is granted)
- [ ] Committee Safe can call `initiateSlash` / `rejectReputation`
- [ ] Deployer upgrade attempt reverts
- [ ] Timelock schedule → 7d → execute succeeds for a no-op/param change on a staging rehearsal

---

## 7. Phase E — Oracle & Ops Cutover

- [ ] `oracle/.env` → `4663` addresses, Alchemy RPC, `ORACLE_PRIVATE_KEY` = HSM key
- [ ] `ATTEST_COOLDOWN_MS`, `ORACLE_STATE_PATH` volume mounted
- [ ] Scrape `/metrics`; alert on `/health` 503 (stale epoch / unwritable store)
- [ ] `ORACLE_ADMIN_TOKEN` set (never expose unauthenticated `/attest`/`/epoch`)
- [ ] SDK / CounterAudit configs point at `deployments/4663.json`
- [ ] Run fork tests:

```bash
FOUNDRY_FORK=1 forge test --match-contract ForkTest --fork-url $MAINNET_RPC
```

Extend `Fork.t.sol` for `4663` once addresses committed.

---

## 8. Optional Later — TGE (`DeployMainnet.s.sol`)

Only when token launch is intentional (align with [oracle-first](oracle-first.md) / no public sale messaging until legal/product greenlight):

```bash
export GOV_MULTISIG=0x...
export TEAM_BENEFICIARY=0x...
export PUBLIC_SALE_RECIPIENT=0x... # must != LIQUIDITY_RECIPIENT
export LIQUIDITY_RECIPIENT=0x...
export TGE_START=...

forge script script/DeployMainnet.s.sol --rpc-url $RPC --broadcast -vvvv
```

If protocol already live on WETH/USDC, TGE does not automatically retarget Staking's bond token — that would be a separate governance upgrade / new deployment decision.

---

## 9. Risk Register

| Risk | Mitigation |
|---|---|
| Deployer retains admin after "mainnet" | Automated assertions + public role dump in `deployments/4663.json` |
| `challengeWindow = 0` after upgrade | Always `upgradeToAndCall` with V2 init (see `script/Upgrade.s.sol` lesson) |
| Faucet token on mainnet | Script must not deploy `CSIGToken` |
| Single oracle key | HSM + documented acceptance; roadmap multi-operator |
| Secrets in CI job-level conditions | Keep secret checks at step/env level, not job-level `if: secrets.*` |
| Public RPC flakiness | Alchemy for oracle + deploy |

---

## 10. Suggested Engineering Work

1. **Rehearse role rotation on `46630`** (practice path with a new Timelock + Safe).
2. **Implement `DeployProtocolMainnet.s.sol` + `RotateRoles.s.sol`** with fork tests.
3. **Extend `Fork.t.sol`** template for `4663` once deployed.
4. Keep TGE script parked until product/legal says go.

---

## Related

- [Robinhood Chain Migration](robinhood-chain.md)
- [Direction: Oracle-First](oracle-first.md)
- [Tokenomics §9 — Governance Timelock](tokenomics.md)
- [ADR 0001 — ERC-8004 as Identity Layer](adr/0001-erc8004-as-identity-layer.md)
4 changes: 4 additions & 0 deletions docs/robinhood-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,10 @@ Optional later: add a fork test suite against `robinhood_testnet` for post-deplo

## 6. Production (Robinhood mainnet)

For the full mainnet deployment sequence — including role rotation, Timelock
setup, and post-deploy hardening — see the [Mainnet Deploy & Role Rotation
runbook](mainnet-deploy.md).

Use `script/DeployMainnet.s.sol` + fixed-supply `CSIG` (not the testnet `CSIGToken`
faucet) when you are ready for TGE on chain `4663`. Uniswap is available on
Robinhood Chain for liquidity; revisit any Ethereum-only assumptions in
Expand Down
Loading