Part of the sequenced cleanup tracked in https://gitea.d-bis.org/d-bis/proxmox/issues/1. Phase 1b — Chain 138 admin transfer-then-revoke Forge scripts. Scaffolding only; no broadcast, no secret values committed.
The Chain 138 deployer EOA 0x4A666F96fC8764181194447A7dFdb7d471b301C8 was committed to d-bis/proxmox in plaintext and must be rotated off every contract it controls. Because the key is an EOA (not a multisig), rotation is transfer-then-revoke, not revoke-first — per docs/runbooks/MULTI_CHAIN_EXECUTION_KEY_ROTATION.md.
What this PR adds
File
Purpose
script/rotation/RotateChain138Admin.s.sol
Three Forge scripts: RotateStage1 (OLD signs, grants NEW), RotateStage2 (NEW signs, revokes OLD), VerifyChain138RotationComplete (read-only end-state check). Targets cUSDT, cUSDC, DODOPMMIntegration (plus POOL_MANAGER_ROLE + SWAP_OPERATOR_ROLE on DODOPMMIntegration).
script/rotation/README.md
Index + follow-up table for the other chains (mainnet, Cronos, Polygon, Base, OP, BSC, AVAX, Arbitrum, Wemix).
scripts/rotation/chain138-rotation-runbook.md
Operator runbook — simulate/broadcast forge script sequences, Stage-1/Stage-2 verification (cast call), rollback path (only possible between stages), per-chain template for follow-up PRs.
Design
Simulation by default.forge script ... -vvv simulates against the RPC and prints calldata; broadcasting requires explicit --broadcast --slow, and the runbook gates that behind per-tx operator approval.
Preflight asserts. Stage 1 aborts if OLD no longer holds all roles or if NEW already holds any role; Stage 2 aborts if Stage 1 didn't land. Cuts off the obvious "ran it twice by mistake" failure mode.
Signer identity checks. Each stage requires vm.addr(PRIVATE_KEY) to match the expected signer (Stage 1: OLD deployer; Stage 2: NEW admin). Prevents signing with the wrong key.
Canonical addresses as defaults. cUSDT / cUSDC / DODOPMMIntegration default to the values in docs/11-references/EXPLORER_TOKEN_LIST_CROSSCHECK.md §5; overridable via env for test environments. OLD_DEPLOYER_ADDRESS defaults to 0x4A66…01C8.
Verifier reverts loudly.VerifyChain138RotationComplete reverts with a specific message if any contract still names OLD as owner/admin. Exit code 0 = rotation complete.
What this PR does NOT do
Does NOT broadcast anything. forge script simulates by default; --broadcast is a separate operator decision per the runbook.
Does NOT cover the other chains (mainnet / Cronos / Polygon / Base / OP / BSC / AVAX / Arbitrum / Wemix). Each of those needs its own enumeration + script and lands in a follow-up PR. Template in script/rotation/README.md.
Does NOT touch the PMM pool contracts on Chain 138. Their owner() / _OWNER_() reverted during Phase 1b discovery, suggesting they may be immutable DODO-style pools without a rotatable admin. A follow-up probes their actual role model with the source.
Does NOT rotate the MINTER_ROLE on CompliantFiatToken derivatives (cXAUC / cXAUT etc.) — discovery shows the deployer holds DEFAULT_ADMIN_ROLE on those, and MINTER_ROLE is delegated elsewhere. Follow-up once the actual minter grants are enumerated on-chain.
Does NOT include any secret values. The runbook refers to PRIVATE_KEY as an env var the operator supplies locally.
Review & Testing Checklist for Human
Risk: yellow — scripts do nothing until --broadcast is added, but when they do run they change on-chain admin state permanently. Stage 2 is irreversible.
Read scripts/rotation/chain138-rotation-runbook.md end-to-end; confirm the simulate-before-broadcast + stage-then-revoke flow matches how you actually want this executed.
Read script/rotation/RotateChain138Admin.s.sol:
Canonical addresses in the DEFAULT_* constants match docs/11-references/EXPLORER_TOKEN_LIST_CROSSCHECK.md §5.
The preflight require set covers the failure modes you care about.
The SWAP_OPERATOR_ROLE / POOL_MANAGER_ROLE grant in Stage 1 is intentional (deployer holds both today per the Phase 1b discovery).
Once the NEW_ADMIN_ADDRESS decision is made, operator executes §2 and §3 of the runbook with per-tx approval. Follow-up PRs stage the same pattern for the other chains.
## Summary
Part of the sequenced cleanup tracked in <https://gitea.d-bis.org/d-bis/proxmox/issues/1>. **Phase 1b — Chain 138 admin transfer-then-revoke Forge scripts.** Scaffolding only; no broadcast, no secret values committed.
The Chain 138 deployer EOA `0x4A666F96fC8764181194447A7dFdb7d471b301C8` was committed to `d-bis/proxmox` in plaintext and must be rotated off every contract it controls. Because the key is an EOA (not a multisig), rotation is **transfer-then-revoke**, not revoke-first — per `docs/runbooks/MULTI_CHAIN_EXECUTION_KEY_ROTATION.md`.
### What this PR adds
| File | Purpose |
|---|---|
| `script/rotation/RotateChain138Admin.s.sol` | Three Forge scripts: `RotateStage1` (OLD signs, grants NEW), `RotateStage2` (NEW signs, revokes OLD), `VerifyChain138RotationComplete` (read-only end-state check). Targets cUSDT, cUSDC, DODOPMMIntegration (plus `POOL_MANAGER_ROLE` + `SWAP_OPERATOR_ROLE` on DODOPMMIntegration). |
| `script/rotation/README.md` | Index + follow-up table for the other chains (mainnet, Cronos, Polygon, Base, OP, BSC, AVAX, Arbitrum, Wemix). |
| `scripts/rotation/chain138-rotation-runbook.md` | Operator runbook — simulate/broadcast `forge script` sequences, Stage-1/Stage-2 verification (`cast call`), rollback path (only possible between stages), per-chain template for follow-up PRs. |
### Design
- **Simulation by default.** `forge script ... -vvv` simulates against the RPC and prints calldata; broadcasting requires explicit `--broadcast --slow`, and the runbook gates that behind per-tx operator approval.
- **Preflight asserts.** Stage 1 aborts if OLD no longer holds all roles or if NEW already holds any role; Stage 2 aborts if Stage 1 didn't land. Cuts off the obvious "ran it twice by mistake" failure mode.
- **Signer identity checks.** Each stage requires `vm.addr(PRIVATE_KEY)` to match the expected signer (Stage 1: OLD deployer; Stage 2: NEW admin). Prevents signing with the wrong key.
- **Canonical addresses as defaults.** cUSDT / cUSDC / DODOPMMIntegration default to the values in `docs/11-references/EXPLORER_TOKEN_LIST_CROSSCHECK.md` §5; overridable via env for test environments. `OLD_DEPLOYER_ADDRESS` defaults to `0x4A66…01C8`.
- **Verifier reverts loudly.** `VerifyChain138RotationComplete` reverts with a specific message if any contract still names OLD as owner/admin. Exit code 0 = rotation complete.
### What this PR does NOT do
- Does NOT broadcast anything. `forge script` simulates by default; `--broadcast` is a separate operator decision per the runbook.
- Does NOT cover the other chains (mainnet / Cronos / Polygon / Base / OP / BSC / AVAX / Arbitrum / Wemix). Each of those needs its own enumeration + script and lands in a follow-up PR. Template in `script/rotation/README.md`.
- Does NOT touch the PMM pool contracts on Chain 138. Their `owner()` / `_OWNER_()` reverted during Phase 1b discovery, suggesting they may be immutable DODO-style pools without a rotatable admin. A follow-up probes their actual role model with the source.
- Does NOT rotate the `MINTER_ROLE` on CompliantFiatToken derivatives (cXAUC / cXAUT etc.) — discovery shows the deployer holds `DEFAULT_ADMIN_ROLE` on those, and `MINTER_ROLE` is delegated elsewhere. Follow-up once the actual minter grants are enumerated on-chain.
- Does NOT include any secret values. The runbook refers to `PRIVATE_KEY` as an env var the operator supplies locally.
## Review & Testing Checklist for Human
Risk: **yellow** — scripts do nothing until `--broadcast` is added, but when they do run they change on-chain admin state permanently. Stage 2 is irreversible.
- [ ] Read `scripts/rotation/chain138-rotation-runbook.md` end-to-end; confirm the simulate-before-broadcast + stage-then-revoke flow matches how you actually want this executed.
- [ ] Read `script/rotation/RotateChain138Admin.s.sol`:
- [ ] Canonical addresses in the `DEFAULT_*` constants match `docs/11-references/EXPLORER_TOKEN_LIST_CROSSCHECK.md` §5.
- [ ] The preflight `require` set covers the failure modes you care about.
- [ ] The `SWAP_OPERATOR_ROLE` / `POOL_MANAGER_ROLE` grant in Stage 1 is intentional (deployer holds both today per the Phase 1b discovery).
- [ ] Confirm whether PMM pools (`0xff8d…0849`, `0x6fc6…9D71`, `0x9f74…0263`) need admin rotation — follow-up PR pending.
- [ ] Decide the `NEW_ADMIN_ADDRESS` target (Gnosis Safe preferred per runbook §0.2) and who holds the signer.
- [ ] Dry-run locally against Chain 138 (read-only) before the actual rotation window:
```bash
source smom-dbis-138/scripts/load-env.sh
export NEW_ADMIN_ADDRESS=0x<candidate>
forge script script/rotation/RotateChain138Admin.s.sol:RotateStage1 \
--rpc-url "$RPC_URL_138" -vvv
```
### Next
Once the `NEW_ADMIN_ADDRESS` decision is made, operator executes §2 and §3 of the runbook with per-tx approval. Follow-up PRs stage the same pattern for the other chains.
Tracking: <https://gitea.d-bis.org/d-bis/proxmox/issues/1>.
Part of the sequenced cleanup tracked in
d-bis/proxmox#1.
Scaffolding only. No broadcast. No secret values committed.
The Chain 138 deployer EOA 0x4A666F96...01C8 is in proxmox master in
plaintext and must be rotated off as owner/admin on all contracts it
controls. Because the key is an EOA (not a multisig), rotation is
transfer-then-revoke (not revoke-first) per
docs/runbooks/MULTI_CHAIN_EXECUTION_KEY_ROTATION.md.
- script/rotation/RotateChain138Admin.s.sol
- RotateStage1 (signed by OLD): transferOwnership + grantRole
DEFAULT_ADMIN_ROLE on cUSDT, cUSDC, DODOPMMIntegration; additionally
POOL_MANAGER_ROLE + SWAP_OPERATOR_ROLE on DODOPMMIntegration.
Preflight asserts OLD still holds each role and NEW does not.
- RotateStage2 (signed by NEW): revokeRole on OLD for each of the
above. Preflight asserts Stage 1 landed (NEW holds roles, OLD still
holds roles).
- VerifyChain138RotationComplete: read-only end-state check; reverts
with a specific message if any contract still names OLD as owner or
admin.
- Default addresses are the canonical Chain 138 cUSDT / cUSDC /
DODOPMMIntegration from docs/11-references/ADDRESS_MATRIX_AND_STATUS.md
and .cursor/rules/chain138-tokens-and-pmm.mdc. Overridable via env.
- script/rotation/README.md: index + follow-up PR table for the other
chains (mainnet, Cronos, Polygon, Base, OP, BSC, AVAX, Arbitrum,
Wemix).
- scripts/rotation/chain138-rotation-runbook.md: authoritative operator
runbook with simulate/broadcast cast sequences, Stage-1/Stage-2
verification steps, rollback path (only possible between stages), and
a per-chain template for the follow-up PRs.
Forge build passes (one mixed-case-variable lint note on field names
matching the contract names; not an error).
This PR does not cover other chains or execute the rotation — those
are explicit operator steps gated on per-tx approval.
Co-Authored-By: Nakamoto, S <defi@defi-oracle.io>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Part of the sequenced cleanup tracked in https://gitea.d-bis.org/d-bis/proxmox/issues/1. Phase 1b — Chain 138 admin transfer-then-revoke Forge scripts. Scaffolding only; no broadcast, no secret values committed.
The Chain 138 deployer EOA
0x4A666F96fC8764181194447A7dFdb7d471b301C8was committed tod-bis/proxmoxin plaintext and must be rotated off every contract it controls. Because the key is an EOA (not a multisig), rotation is transfer-then-revoke, not revoke-first — perdocs/runbooks/MULTI_CHAIN_EXECUTION_KEY_ROTATION.md.What this PR adds
script/rotation/RotateChain138Admin.s.solRotateStage1(OLD signs, grants NEW),RotateStage2(NEW signs, revokes OLD),VerifyChain138RotationComplete(read-only end-state check). Targets cUSDT, cUSDC, DODOPMMIntegration (plusPOOL_MANAGER_ROLE+SWAP_OPERATOR_ROLEon DODOPMMIntegration).script/rotation/README.mdscripts/rotation/chain138-rotation-runbook.mdforge scriptsequences, Stage-1/Stage-2 verification (cast call), rollback path (only possible between stages), per-chain template for follow-up PRs.Design
forge script ... -vvvsimulates against the RPC and prints calldata; broadcasting requires explicit--broadcast --slow, and the runbook gates that behind per-tx operator approval.vm.addr(PRIVATE_KEY)to match the expected signer (Stage 1: OLD deployer; Stage 2: NEW admin). Prevents signing with the wrong key.docs/11-references/EXPLORER_TOKEN_LIST_CROSSCHECK.md§5; overridable via env for test environments.OLD_DEPLOYER_ADDRESSdefaults to0x4A66…01C8.VerifyChain138RotationCompletereverts with a specific message if any contract still names OLD as owner/admin. Exit code 0 = rotation complete.What this PR does NOT do
forge scriptsimulates by default;--broadcastis a separate operator decision per the runbook.script/rotation/README.md.owner()/_OWNER_()reverted during Phase 1b discovery, suggesting they may be immutable DODO-style pools without a rotatable admin. A follow-up probes their actual role model with the source.MINTER_ROLEon CompliantFiatToken derivatives (cXAUC / cXAUT etc.) — discovery shows the deployer holdsDEFAULT_ADMIN_ROLEon those, andMINTER_ROLEis delegated elsewhere. Follow-up once the actual minter grants are enumerated on-chain.PRIVATE_KEYas an env var the operator supplies locally.Review & Testing Checklist for Human
Risk: yellow — scripts do nothing until
--broadcastis added, but when they do run they change on-chain admin state permanently. Stage 2 is irreversible.scripts/rotation/chain138-rotation-runbook.mdend-to-end; confirm the simulate-before-broadcast + stage-then-revoke flow matches how you actually want this executed.script/rotation/RotateChain138Admin.s.sol:DEFAULT_*constants matchdocs/11-references/EXPLORER_TOKEN_LIST_CROSSCHECK.md§5.requireset covers the failure modes you care about.SWAP_OPERATOR_ROLE/POOL_MANAGER_ROLEgrant in Stage 1 is intentional (deployer holds both today per the Phase 1b discovery).0xff8d…0849,0x6fc6…9D71,0x9f74…0263) need admin rotation — follow-up PR pending.NEW_ADMIN_ADDRESStarget (Gnosis Safe preferred per runbook §0.2) and who holds the signer.Next
Once the
NEW_ADMIN_ADDRESSdecision is made, operator executes §2 and §3 of the runbook with per-tx approval. Follow-up PRs stage the same pattern for the other chains.Tracking: https://gitea.d-bis.org/d-bis/proxmox/issues/1.
View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.