Skip to content

Commit 67927b4

Browse files
committed
feat: Add emergency role transfer workflow and scripts
1 parent 7dcf930 commit 67927b4

File tree

5 files changed

+1118
-0
lines changed

5 files changed

+1118
-0
lines changed
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
name: Emergency Role Transfer
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
step:
7+
description: 'Transfer step to execute'
8+
required: true
9+
type: choice
10+
options:
11+
- grant-roles-begin-transfer
12+
- accept-admin-revoke-old
13+
default: grant-roles-begin-transfer
14+
network_type:
15+
description: 'Network type to transfer roles on'
16+
required: true
17+
type: choice
18+
options:
19+
- testnets
20+
- sepolia-only
21+
- arbitrum_sepolia-only
22+
default: testnets
23+
old_address:
24+
description: 'Old/compromised address (for both steps)'
25+
required: true
26+
type: string
27+
default: '0x9990cfb1Feb7f47297F54bef4d4EbeDf6c5463a3'
28+
new_address:
29+
description: 'New secure address (only needed for step 1)'
30+
required: false
31+
type: string
32+
33+
jobs:
34+
setup-matrix:
35+
runs-on: ubuntu-latest
36+
outputs:
37+
matrix: ${{ steps.set-matrix.outputs.matrix }}
38+
steps:
39+
- name: Set matrix based on network type
40+
id: set-matrix
41+
run: |
42+
case "${{ github.event.inputs.network_type }}" in
43+
testnets)
44+
MATRIX='["sepolia", "arbitrum_sepolia"]'
45+
;;
46+
sepolia-only)
47+
MATRIX='["sepolia"]'
48+
;;
49+
arbitrum_sepolia-only)
50+
MATRIX='["arbitrum_sepolia"]'
51+
;;
52+
esac
53+
echo "matrix=$MATRIX" >> $GITHUB_OUTPUT
54+
55+
transfer-roles:
56+
needs: setup-matrix
57+
runs-on: ubuntu-latest
58+
strategy:
59+
matrix:
60+
network: ${{ fromJson(needs.setup-matrix.outputs.matrix) }}
61+
fail-fast: false
62+
environment: ${{ matrix.network }}
63+
64+
steps:
65+
- uses: actions/checkout@v4
66+
with:
67+
submodules: recursive
68+
69+
- name: Install Foundry
70+
uses: foundry-rs/foundry-toolchain@v1
71+
with:
72+
version: v1.4.4
73+
cache: true
74+
75+
- name: Validate inputs
76+
run: |
77+
if [ "${{ inputs.step }}" = "grant-roles-begin-transfer" ] && [ -z "${{ inputs.new_address }}" ]; then
78+
echo "Error: new_address is required for grant-roles-begin-transfer step"
79+
exit 1
80+
fi
81+
82+
- name: Execute Step 1 - Grant Roles and Begin Transfer
83+
if: inputs.step == 'grant-roles-begin-transfer'
84+
env:
85+
ADMIN_PRIVATE_KEY: ${{ secrets.ADMIN_PRIVATE_KEY }}
86+
CHAIN: ${{ matrix.network }}
87+
RPC_URL: ${{ secrets.RPC_URL }}
88+
OLD_ADDRESS: ${{ inputs.old_address }}
89+
NEW_ADDRESS: ${{ inputs.new_address }}
90+
run: |
91+
echo "Executing Step 1: Grant roles and begin admin transfer"
92+
echo "Chain: $CHAIN"
93+
echo "Old address: $OLD_ADDRESS"
94+
echo "New address: $NEW_ADDRESS"
95+
make grant-roles-begin-transfer
96+
97+
- name: Execute Step 2 - Accept Admin and Revoke Old Roles
98+
if: inputs.step == 'accept-admin-revoke-old'
99+
env:
100+
NEW_ADMIN_PRIVATE_KEY: ${{ secrets.NEW_ADMIN_PRIVATE_KEY }}
101+
CHAIN: ${{ matrix.network }}
102+
RPC_URL: ${{ secrets.RPC_URL }}
103+
OLD_ADDRESS: ${{ inputs.old_address }}
104+
run: |
105+
echo "Executing Step 2: Accept admin role and revoke old roles"
106+
echo "Chain: $CHAIN"
107+
echo "Old address to revoke: $OLD_ADDRESS"
108+
make accept-admin-revoke-old
109+
110+
- name: Summary
111+
run: |
112+
if [ "${{ inputs.step }}" = "grant-roles-begin-transfer" ]; then
113+
echo "✅ Step 1 Complete on ${{ matrix.network }}"
114+
echo "⏰ Wait for the delay period to pass before running Step 2"
115+
echo "📝 Next: Run this workflow again with step=accept-admin-revoke-old"
116+
else
117+
echo "✅ Step 2 Complete on ${{ matrix.network }}"
118+
echo "🎉 All roles have been transferred!"
119+
echo "🔒 Old address has been completely removed from all contracts"
120+
fi

Makefile

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,3 +201,43 @@ accept-default-admin-transfer: # CHAIN, RPC_URL
201201
$$(if [ "$(CI)" = "true" ]; then echo "--private-key $(NEW_DEFAULT_ADMIN_PRIVATE_KEY)"; else echo "--account $(ACCOUNT)"; fi) \
202202
--broadcast \
203203
-vvv
204+
205+
#
206+
# Emergency role transfer operations (for compromised addresses)
207+
#
208+
209+
# Step 1: Grant all roles to new address and begin admin transfer (run with compromised/old address)
210+
grant-roles-begin-transfer: # CHAIN, RPC_URL, OLD_ADDRESS, NEW_ADDRESS
211+
@echo "Step 1: Granting all roles to new address and beginning admin transfer on $(CHAIN)"
212+
@echo "Old address: $(OLD_ADDRESS)"
213+
@echo "New address: $(NEW_ADDRESS)"
214+
CHAIN=$(CHAIN) OLD_ADDRESS=$(OLD_ADDRESS) NEW_ADDRESS=$(NEW_ADDRESS) \
215+
forge script script/TransferAllRoles.s.sol:GrantRolesAndBeginAdminTransfer \
216+
--rpc-url $(RPC_URL) \
217+
$$(if [ "$(CI)" = "true" ]; then echo "--private-key $(ADMIN_PRIVATE_KEY)"; else echo "--account $(ACCOUNT)"; fi) \
218+
--broadcast \
219+
-vvv
220+
221+
# Step 2: Accept admin role and revoke all roles from old address (run with NEW address)
222+
accept-admin-revoke-old: # CHAIN, RPC_URL, OLD_ADDRESS
223+
@echo "Step 2: Accepting admin role and revoking all roles from old address on $(CHAIN)"
224+
@echo "Old address to revoke: $(OLD_ADDRESS)"
225+
CHAIN=$(CHAIN) OLD_ADDRESS=$(OLD_ADDRESS) \
226+
forge script script/TransferAllRoles.s.sol:AcceptAdminRoleAndRevokeOldRoles \
227+
--rpc-url $(RPC_URL) \
228+
$$(if [ "$(CI)" = "true" ]; then echo "--private-key $(NEW_ADMIN_PRIVATE_KEY)"; else echo "--account $(ACCOUNT)"; fi) \
229+
--broadcast \
230+
-vvv
231+
232+
#
233+
# Testing emergency role transfer on fork
234+
#
235+
236+
# Test the complete emergency role transfer process on a fork
237+
test-emergency-transfer-on-fork:
238+
@echo "Testing emergency role transfer on Arbitrum Sepolia fork..."
239+
@echo "Make sure you have a fork running: anvil --fork-url \$$ARBITRUM_SEPOLIA_RPC_URL --port 8546"
240+
forge script script/TestEmergencyRoleTransferOnFork.s.sol:TestEmergencyRoleTransferOnFork \
241+
--rpc-url http://localhost:8546 \
242+
--broadcast \
243+
-vvvv

0 commit comments

Comments
 (0)