Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
ce385b4
v3
gjermundgaraba May 29, 2026
a9b9d5d
Merge remote-tracking branch 'origin/main' into gjermund/v3-upgrade
gjermundgaraba Jun 3, 2026
6f3b436
pin deps
gjermundgaraba Jun 3, 2026
8f1e9e2
cr fixes pt1
gjermundgaraba Jun 9, 2026
203ad13
cleanly
gjermundgaraba Jun 9, 2026
ca23b65
Add v3 upgrade deployment flow
gjermundgaraba Jun 10, 2026
433e6a9
cr fixes
gjermundgaraba Jun 11, 2026
bac7b82
Fix shadow rehearsal bugs found during fork testing
gjermundgaraba Jun 11, 2026
ef66d71
Address review findings: admin checks, stricter shadow asserts, cleanups
gjermundgaraba Jun 11, 2026
e025aa9
Make atomic Safe MultiSend the single v3 upgrade execute path
gjermundgaraba Jun 14, 2026
cc0692c
Address review (T36/T37/pause): symmetric deploy helpers, SafeCast, w…
gjermundgaraba Jun 14, 2026
e9da905
fixes from rehersal
gjermundgaraba Jun 15, 2026
ac11cd4
chore: let new-operation cut from a base branch other than main
gjermundgaraba Jun 15, 2026
cf15c6c
chore: start operation 2026-06-15-upgrade-v2-to-v3
gjermundgaraba Jun 15, 2026
cf5b369
Add Safe-propose + relayer-vkey tooling and a general atomic timelock…
gjermundgaraba Jun 15, 2026
94e4041
Record testnet v3 deploys: AccessManager + ICS27, implementations, SP…
gjermundgaraba Jun 15, 2026
816fa3f
Rehearse the EXTRA_TIMELOCK_OPS folding path in the timelock shadow r…
gjermundgaraba Jun 15, 2026
e3fa245
Record ICS27GMP registration on gmpport (testnet v2->v3 upgrade compl…
gjermundgaraba Jun 15, 2026
e962c9b
docs: clarify step-8 ID-customizer signing in the v2->v3 runbook
gjermundgaraba Jun 15, 2026
59c7454
Add TMP operation record (remove before merge)
gjermundgaraba Jun 15, 2026
015584c
Add v3 role validator + post-upgrade role-testing runbook (mainnet-re…
gjermundgaraba Jun 16, 2026
55fdff4
Add v2 role-grant discovery tool + document the mainnet grant set
gjermundgaraba Jun 16, 2026
f24d1c9
Clarify TOKEN_OPERATOR: metadata-customizer admin, replaced by setCus…
gjermundgaraba Jun 16, 2026
136d84d
Document IBCERC20 metadata audit: used in v2, survives the upgrade
gjermundgaraba Jun 16, 2026
659052e
fix(scripts): fail loud on Etherscan throttle; validate reads rateLim…
gjermundgaraba Jun 16, 2026
01559c8
docs: durable operation RECORD, fix broken signer recipe, mainnet notes
gjermundgaraba Jun 16, 2026
188912e
chore: remove stale bun.lockb (pinned v2.0.0)
gjermundgaraba Jun 16, 2026
568ed99
feat: ledger proposer signing + timelock-pending & admin-revoke guards
gjermundgaraba Jun 17, 2026
ed4d9ae
docs: lock mainnet decisions; document ledger + execution guards
gjermundgaraba Jun 17, 2026
912eb0e
docs: start mainnet-path working note (TMP_MAINNET_WHAT_HAS_BEEN_DONE…
gjermundgaraba Jun 17, 2026
2ed8ed9
fix(safe-propose): sign the Safe EIP-712 typed data on Ledger, not a …
gjermundgaraba Jun 17, 2026
7aa4880
docs: add mainnet cutover runsheet; record staged-v6.1 rehearsal
gjermundgaraba Jun 17, 2026
8414c36
docs: signer verification tool + checklist (hardened via adversarial …
gjermundgaraba Jun 17, 2026
22ef1f0
fix: readiness-review hardening batch (trust-root assertions + verifi…
gjermundgaraba Jun 17, 2026
823af00
docs: address review feedback on READINESS-REVIEW; record trust-root …
gjermundgaraba Jun 17, 2026
1e5ca10
feat: 10-op fold rehearsal + cutover-runbook sequencing (F1/F5/F8/F9 …
gjermundgaraba Jun 17, 2026
629ca1c
docs/verify-roots: corrected relayer model + close F4/F5/escrow with …
gjermundgaraba Jun 17, 2026
3a86cc5
docs: review-response & re-validation guide; add 3 remaining footgun …
gjermundgaraba Jun 17, 2026
28d2996
fix: F3 keys survive deploy (top-level); verify-roots C1/C2; doc reco…
gjermundgaraba Jun 17, 2026
2508168
docs: final readiness report (standalone go/no-go entry point)
gjermundgaraba Jun 17, 2026
baaa03f
docs: apply subagent-review fixes (correctness, signer-facing, clarit…
gjermundgaraba Jun 17, 2026
3d0eda7
docs: verbosity trim pass (safe cuts)
gjermundgaraba Jun 17, 2026
60c22c5
docs: C4 passed; proof-api /dev/shm failure mode + C4 tooling
gjermundgaraba Jun 18, 2026
89b2097
docs+scripts: address readiness review (runbook contradictions, footg…
gjermundgaraba Jun 18, 2026
1cd97cf
test(rehearsal): drive ICS27GMP addIBCApp via the real customizer Safe
gjermundgaraba Jun 18, 2026
5c7492b
docs: list the customizer-Safe addIBCApp rehearsal in readiness evidence
gjermundgaraba Jun 18, 2026
7a5b783
ops(v2->v3): record Phase-A on-chain state + 8-op fold (drop 0x64259f72)
gjermundgaraba Jun 18, 2026
e36afa7
fix(safe-propose): allow proposing as a registered tx-service delegate
gjermundgaraba Jun 18, 2026
26e7bfa
docs(phase-b): coordinator hash-table for the 8 proposed schedules (n…
gjermundgaraba Jun 18, 2026
4df4ac8
docs(signer-checklist): fill expected-hash table with live nonces 18-25
gjermundgaraba Jun 18, 2026
fbe0ab5
docs(signer-checklist): close review blockers, then compress to one-s…
gjermundgaraba Jun 18, 2026
94b1882
docs(signer-checklist): scope to the schedule round; park later rounds
gjermundgaraba Jun 18, 2026
a0ed2bf
chore(operation): re-date 2026-06-15 -> 2026-06-18 (folder rename + r…
gjermundgaraba Jun 18, 2026
2242e96
chore(2026-06-18 branch): drop operation narrative docs (keep all cod…
gjermundgaraba Jun 18, 2026
b9bd3d8
feat(signer-verify): offline generator — drop the Safe API, read payl…
gjermundgaraba Jun 18, 2026
bddfff6
feat(signer-verify): checkout model — find the table from the repo, d…
gjermundgaraba Jun 18, 2026
b3aafde
docs(signer-checklist): scope to validation-only
gjermundgaraba Jun 18, 2026
37fd552
docs(signer-checklist): unwrap prose to one line per paragraph
gjermundgaraba Jun 18, 2026
dda6274
docs(signer-checklist): fill clone command, drop reporting/channel la…
gjermundgaraba Jun 18, 2026
6f90d18
fix(signer-verify): decode escrow/ibcerc20 upgrade selectors; fix sta…
gjermundgaraba Jun 18, 2026
c6be974
docs(signer): add execute-round (nonce 26) checklist + hash-table row
gjermundgaraba Jun 20, 2026
58d1650
docs(arming-branch): restore CUTOVER-RUNSHEET.md + RECORD.md; fix sta…
gjermundgaraba Jun 20, 2026
acb1fe4
docs(runsheet): execute via Safe UI (GS026), pre-execute pending recheck
gjermundgaraba Jun 22, 2026
fbe7ea1
feat(safe-propose): add --safe to target a non-governance Safe
gjermundgaraba Jun 26, 2026
5e5594d
chore(broadcast): record GrantRateLimiterRole mainnet dry-run artifact
gjermundgaraba Jun 26, 2026
0c068d3
docs(record): fill in mainnet Phase C + Phase D execution record
gjermundgaraba Jun 26, 2026
c05556e
docs(runbooks): relocate proof-api failure-mode write-up to runbooks/
gjermundgaraba Jun 26, 2026
e76d9eb
chore(broadcast): drop GrantRateLimiterRole dry-run sims from ops branch
gjermundgaraba Jun 26, 2026
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
4 changes: 2 additions & 2 deletions .eureka-env.example
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@ ETHERSCAN_API_KEY=0000000000000000000000000000000000
# zk algorithm for the generated client state; MUST match the existing client (groth16 or plonk)
# PROOF_TYPE=groth16

# Gnosis Safe that holds PROPOSER on the timelock (used by the safe.just hash recipes)
# SAFE_ADDRESS=0x0000000000000000000000000000000000000000
# The Gnosis Safe that holds PROPOSER/EXECUTOR on the timelock is configured per-deployment in
# deployments/<env>/<chain>.json under the ".safe" key (no env var needed).
4 changes: 4 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Keep shell scripts LF so a Windows `git checkout` (e.g. signers in WSL) can run them, and keep the
# signer hash-table LF so signer-verify.sh parses its payload rows byte-for-byte.
*.sh text eol=lf
runbooks/operations/2026-06-18-upgrade-v2-to-v3/COORDINATOR-HASH-TABLE.md text eol=lf
17 changes: 14 additions & 3 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@ on:
- "main"

jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v4
- name: "Set up environment"
uses: ./.github/setup
- name: Run tests
run: forge test -vvv

test-deploy:
runs-on: ubuntu-latest
env:
Expand All @@ -24,8 +34,7 @@ jobs:
anvil &
- name: Deploy contracts
run: |
forge script script/DeployProxiedICS26Router.sol -vvv --broadcast --private-key ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
forge script script/DeployProxiedICS20Transfer.sol -vvv --broadcast --private-key ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
forge script script/DeployV3Core.sol:DeployV3Core -vvv --broadcast --private-key ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
forge script script/DeploySP1ICS07Tendermint.sol -vvv --broadcast --private-key ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
forge script script/PauseTransfers.sol -vvv --broadcast --private-key ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
forge script script/UnpauseTransfers.sol -vvv --broadcast --private-key 59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d
Expand Down Expand Up @@ -62,6 +71,8 @@ jobs:
MAINNET_RPC_URL: ${{ secrets.MAINNET_RPC_URL }}
SEPOLIA_RPC_URL: ${{ secrets.SEPOLIA_RPC_URL }}
run: |
set -euo pipefail

# The chain id is the deployment file name: deployments/<env>/<chain_id>.json
chain_id="$(basename "${{ matrix.file }}" .json)"
case "$chain_id" in
Expand All @@ -76,4 +87,4 @@ jobs:
echo "FOUNDRY_ETH_RPC_URL=${rpc_url}" >> "$GITHUB_ENV"
- name: Verify deployed contracts
run: |
forge script script/VerifyDeployment.sol -vvv
forge script script/VerifyDeployment.sol:VerifyDeployment -vvv
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
cache/
out/
soljson-latest.js
__pycache__/

# Ignores development broadcast logs
!/broadcast
/broadcast/*/31337/
/broadcast/ShadowFork*.sol/

# Docs
docs/
Expand All @@ -15,3 +17,5 @@ docs/
.env
node_modules/
.idea/
.terminalgraph/
deployments/shadow-*/
64 changes: 51 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,21 @@ To see available recipes, run:
just --list
```

## Runbooks

Operator procedures live in [`runbooks/`](./runbooks). Key ones:

- [`upgrade-v2-to-v3.md`](./runbooks/upgrade-v2-to-v3.md) — the canonical v2→v3 core upgrade + SP1 v6.1 migration (steps 1–13). **Single source of truth for that operation.**
- [`post-upgrade-role-testing.md`](./runbooks/post-upgrade-role-testing.md) — validate & test the v3 `AccessManager` roles after a v2→v3 upgrade (read its *Mainnet adaptation* section before running anything on mainnet).
- [`upgrade-light-client.md`](./runbooks/upgrade-light-client.md), [`upgrade-ics-20.md`](./runbooks/upgrade-ics-20.md), [`upgrade-ics-26.md`](./runbooks/upgrade-ics-26.md), [`upgrade-escrow.md`](./runbooks/upgrade-escrow.md), [`upgrade-ibcerc20.md`](./runbooks/upgrade-ibcerc20.md) — routine single-contract upgrades.
- [`pause.md`](./runbooks/pause.md), [`recover-expired-light-client.md`](./runbooks/recover-expired-light-client.md), [`env-setup.md`](./runbooks/env-setup.md).
- **Operations log:** [`runbooks/operations/`](./runbooks/operations) — one folder per executed operation; each typically has a `RECORD.md` (addresses, tx hashes, findings). See [`2026-06-18-upgrade-v2-to-v3/SIGNER-CHECKLIST.md`](./runbooks/operations/2026-06-18-upgrade-v2-to-v3/SIGNER-CHECKLIST.md) for the v2→v3 signer checklist.

### Role discovery & validation scripts

- [`scripts/discover-v2-roles.py`](./scripts/discover-v2-roles.py) — **pre-cutover**: enumerate the live v2 role grants (via Etherscan logs) and reconcile against the deployment JSON, so you build the exact grant set (incl. the `RATE_LIMITER` re-grant set) the upgrade must carry. Needs `ETH_RPC` + `ETHERSCAN_API_KEY`.
- [`scripts/validate-v3-roles.py`](./scripts/validate-v3-roles.py) — **post-cutover**: independently validate every v3 `AccessManager` (target,selector)→role, exact role membership, and `authority()` wiring on-chain. Needs `ETH_RPC`.

## Manual verification instructions

Any on-chain verification that is not implemented as recipes yet should be documented below:
Expand All @@ -47,6 +62,35 @@ To verify that the Ethereum Light on the hub is running a specific version of th

## Recipes

### Shadow fork v2-to-v3 rehearsal

To rehearse the v2-to-v3 upgrade (including the SP1 light-client migrations) against uncommitted local changes, start an Anvil fork in one terminal:

```bash
export SEPOLIA_RPC=<SEPOLIA_RPC_URL>
just shadow-start-sepolia
```

Then run the rehearsal in another terminal:

```bash
just shadow-v2-to-v3-sepolia-with-sp1
```

The SP1 clients to migrate are read from the deployment JSON (`.light_clients[].clientId`), so there is nothing to type, and the rehearsal writes only to ignored `deployments/shadow-*` copies. Use `MAINNET_RPC` with `just shadow-start-mainnet` and `just shadow-v2-to-v3-mainnet-with-sp1` for Ethereum mainnet. To additionally exercise the real `TimelockController` + atomic Safe MultiSend path, use `just shadow-v2-to-v3-sepolia-timelock`. See [`runbooks/upgrade-v2-to-v3.md`](./runbooks/upgrade-v2-to-v3.md) for the full flow.

### Fresh v3 core deployment

For a new v3 deployment where `accessManager`, `ics26Router`, and `ics20Transfer` addresses are still zero in the deployment JSON, deploy the core contracts with:

```bash
just deploy-v3-core
```

This deploys the `AccessManager`, `ICS26Router`, `ICS20Transfer`, `ICS27GMP`, `ICS27Account`, `Escrow`, and `IBCERC20` implementations, registers the transfer and GMP apps on the router, configures the v3 target function roles, grants the configured relayer/pauser/unpauser/delegate-sender/customizer roles, and writes the deployed addresses back to `deployments/<environment>/<chain_id>.json`.

The script temporarily uses the broadcast account as the `AccessManager` admin during deployment, then hands admin control to the configured `.accessManagerRoles.admin`. The configured admin can be an EOA, Safe, or timelock.

### Deploy light client implementation for migration/upgrade

Migrating/upgrading a light client is done in two steps:
Expand Down Expand Up @@ -82,26 +126,20 @@ The implementation address will be updated in the deployment JSON entry for the

After the timelock delay has passed, do the above steps again but replace `schedule` with `execute`

### Updating IBCERC20 Metadata
### IBCERC20 Metadata

> ⚠️ Only a wallet with the Token Operator role is able to update the IBCERC20 Metadata

To update the Metadata for an IBCERC20 contract, you need to do the following:
1. Grant the metadata role for the IBCERC20 contract with:
```bash
just ops-grant-metadata-role # You will be prompted for the IBCERC20 Address and the address of the grantee
```
2. Set the metadata:
```bash
just ops-set-metadata # You will be prompted for the IBCERC20 Address to update and the values to set
```
IBCERC20 metadata customization was removed in solidity-ibc-eureka v3. Prefer custom ERC20s through the custom ERC20 flow instead of post-deployment IBCERC20 metadata changes.

### Upgrade a contract that is behind a proxy

Modify one (and only one at the time) of the `implementation` values in the deployment json for one of the ERC1967Proxy contracts (ICS26Router for instance).
> [!IMPORTANT]
> This is for **routine** UUPS upgrades **after** the v2-to-v3 migration. It performs an `upgradeToAndCall` with **empty** calldata, so it does **not** call `initializeV2`. Do **not** use it to perform the v2-to-v3 core upgrades — use `schedule-v3-*`/`execute-v3-*-upgrade-params` (see [`runbooks/upgrade-v2-to-v3.md`](./runbooks/upgrade-v2-to-v3.md)), which call `initializeV2(accessManager)`. As a guard, `timelock-upgrade-proxy` refuses to upgrade `ICS26Router`/`ICS20Transfer` when the deployment records an `accessManager` but the proxy is not yet AccessManaged by it.

Modify one (and only one at the time) of the `implementation` values in the deployment json for one of the ERC1967Proxy contracts (`ICS26Router`, `ICS20Transfer`, or `ICS27GMP`).

Run the script to generate the information needed to submit a proposal to the Safe Wallet:
```bash
just timelock-upgrade-proxy
```

The beacon implementations have their own param recipes: `schedule-escrow-upgrade-params`, `schedule-ibcerc20-upgrade-params`, and `schedule-ics27account-upgrade-params` (plus the matching `execute-*`).
67 changes: 67 additions & 0 deletions broadcast/DeployImplementation.sol/1/run-1781772262467.json

Large diffs are not rendered by default.

67 changes: 67 additions & 0 deletions broadcast/DeployImplementation.sol/1/run-1781772837447.json

Large diffs are not rendered by default.

Loading
Loading