Skip to content

Commit da64ba9

Browse files
authored
Upgrade OptimismMintableERC20Factory implementation (#158)
* upgrade OptimismMintableERC20Factory implementation * Update README.md * implement review changes * fix readme title * rename script
1 parent f83133e commit da64ba9

File tree

5 files changed

+339
-0
lines changed

5 files changed

+339
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
OP_COMMIT=844cc20084a2e9716631b4092ce7eca4804a8e0a
2+
BASE_CONTRACTS_COMMIT=b2682e85953a48cf64659a24ae7121f39f327867
3+
4+
BRIDGE=0x4200000000000000000000000000000000000010
5+
PROXY_ADMIN=0x4200000000000000000000000000000000000018
6+
ERC20_FACTORY=0x4200000000000000000000000000000000000012
7+
ERC20_FACTORY_IMPL=0x6922ac4DbDfEdEa3a1E5535f12c3171f2b964C91
8+
NESTED_SAFE=0x2304CB33d95999dC29f4CeF1e35065e670a70050
9+
BASE_SAFE=0xd94E416cf2c7167608B2515B7e4102B41efff94f
10+
OP_SAFE=0x28EDB11394eb271212ED66c08f2b7893C04C5D65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
include ../../Makefile
2+
include ../.env
3+
include .env
4+
5+
ifndef LEDGER_ACCOUNT
6+
override LEDGER_ACCOUNT = 0
7+
endif
8+
9+
.PHONY: sign-cb
10+
sign-cb:
11+
$(GOPATH)/bin/eip712sign --ledger --hd-paths "m/44'/60'/$(LEDGER_ACCOUNT)'/0/0" -- \
12+
forge script --rpc-url $(L2_RPC_URL) UpgradeOptimismMintableERC20Factory \
13+
--sig "sign(address)" $(BASE_SAFE)
14+
15+
.PHONY: sign-op
16+
sign-op:
17+
$(GOPATH)/bin/eip712sign --ledger --hd-paths "m/44'/60'/$(LEDGER_ACCOUNT)'/0/0" -- \
18+
forge script --rpc-url $(L2_RPC_URL) UpgradeOptimismMintableERC20Factory \
19+
--sig "sign(address)" $(OP_SAFE)
20+
21+
22+
.PHONY: approve-cb
23+
approve-cb:
24+
forge script --rpc-url $(L2_RPC_URL) UpgradeOptimismMintableERC20Factory \
25+
--sig "approve(address,bytes)" $(BASE_SAFE) $(SIGNATURES) \
26+
--ledger --hd-paths "m/44'/60'/$(LEDGER_ACCOUNT)'/0/0"
27+
28+
.PHONY: approve-op
29+
approve-op:
30+
forge script --rpc-url $(L2_RPC_URL) UpgradeOptimismMintableERC20Factory \
31+
--sig "approve(address,bytes)" $(OP_SAFE) $(SIGNATURES) \
32+
--ledger --hd-paths "m/44'/60'/$(LEDGER_ACCOUNT)'/0/0"
33+
34+
.PHONY: execute
35+
execute:
36+
forge script --rpc-url $(L2_RPC_URL) UpgradeOptimismMintableERC20Factory \
37+
--sig "run()" --ledger --hd-paths "m/44'/60'/$(LEDGER_ACCOUNT)'/0/0"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
# Upgrade OptimismMintableERC20Factory implementation
2+
3+
Status: READY TO SIGN
4+
5+
## Objective
6+
7+
This task upgrades the `OptimismMintableERC20Factory` implementation, at address [0x4200000000000000000000000000000000000012](https://basescan.org/address/0x4200000000000000000000000000000000000012#code), to point to the newly deployed factory at address [0x6922ac4DbDfEdEa3a1E5535f12c3171f2b964C91](https://basescan.org/address/0x6922ac4dbdfedea3a1e5535f12c3171f2b964c91#code), effectively replacing the old implementation at address [0xc0D3c0d3C0d3c0d3c0D3c0d3c0D3c0D3c0D30012](https://basescan.org/address/0xc0d3c0d3c0d3c0d3c0d3c0d3c0d3c0d3c0d30012#code).
8+
9+
## Approving the transaction
10+
11+
### 1. Update repo and move to the appropriate folder:
12+
13+
```
14+
cd contract-deployments
15+
git pull
16+
cd mainnet/2024-04-17-upgrade-erc20-factory
17+
make deps
18+
```
19+
20+
### 2. Setup Ledger
21+
22+
Your Ledger needs to be connected and unlocked. The Ethereum
23+
application needs to be opened on Ledger with the message "Application
24+
is ready".
25+
26+
### 3. Simulate and validate the transaction
27+
28+
Make sure your ledger is still unlocked and run the following.
29+
30+
``` shell
31+
make sign-op # or make sign-cb for Coinbase signers
32+
```
33+
34+
Once you run the make sign command successfully, you will see a "Simulation link" from the output.
35+
36+
Paste this URL in your browser. A prompt may ask you to choose a
37+
project, any project will do. You can create one if necessary.
38+
39+
Click "Simulate Transaction".
40+
41+
We will be performing 3 validations and then we'll extract the domain hash and
42+
message hash to approve on your Ledger then verify completion:
43+
44+
1. Validate integrity of the simulation.
45+
2. Validate correctness of the state diff.
46+
3. Validate and extract domain hash and message hash to approve.
47+
4. Validate that the transaction completed successfully
48+
49+
50+
#### 3.1. Validate integrity of the simulation.
51+
52+
Make sure you are on the "Overview" tab of the tenderly simulation, to
53+
validate integrity of the simulation, we need to check the following:
54+
55+
1. "Network": Check the network is Base Mainnet.
56+
2. "Timestamp": Check the simulation is performed on a block with a
57+
recent timestamp (i.e. close to when you run the script).
58+
3. "Sender": Check the address shown is your signer account. If not,
59+
you will need to determine which “number” it is in the list of
60+
addresses on your ledger.
61+
4. "Success" with a green check mark
62+
63+
64+
#### 3.2. Validate correctness of the state diff.
65+
66+
Now click on the "State" tab. Verify that:
67+
68+
1. Verify that the nonce is incremented for the Nested Multisig under the "GnosisSafeProxy" at address `0x2304cb33d95999dc29f4cef1e35065e670a70050`:
69+
70+
```
71+
Key: 0x0000000000000000000000000000000000000000000000000000000000000005
72+
Before: 0x0000000000000000000000000000000000000000000000000000000000000003
73+
After: 0x0000000000000000000000000000000000000000000000000000000000000004
74+
```
75+
76+
2. And for the same contract, verify that this specific execution is approved:
77+
78+
```
79+
Key (if you are an OP signer): 0x9052fb3c2fd9cb5dd17446c311f63a63659a6f571458940b7f0aced851e51d55
80+
Key (if you are a CB signer): 0x967af9182a28f979962ecb388b54737ad2cbe53fbe8f354b8b1a0005bc9abcff
81+
Before: 0x0000000000000000000000000000000000000000000000000000000000000000
82+
After: 0x0000000000000000000000000000000000000000000000000000000000000001
83+
```
84+
85+
3. Verify that the nonce is incremented for your multisig:
86+
87+
If you are an OP signer - the OP Foundation Multisig should be under the "GnosisSafeProxy" at address `0x28edb11394eb271212ed66c08f2b7893c04c5d65`:
88+
89+
```
90+
Key: 0x0000000000000000000000000000000000000000000000000000000000000005
91+
Before: 0x0000000000000000000000000000000000000000000000000000000000000003
92+
After: 0x0000000000000000000000000000000000000000000000000000000000000004
93+
```
94+
95+
If you are a CB signer - the Coinbase Multisig should be under the address `0xd94e416cf2c7167608b2515b7e4102b41efff94f`:
96+
97+
```
98+
Key: 0x0000000000000000000000000000000000000000000000000000000000000005
99+
Before: 0x0000000000000000000000000000000000000000000000000000000000000006
100+
After: 0x0000000000000000000000000000000000000000000000000000000000000007
101+
```
102+
103+
4. Verify that the new ERC20 factory implementation has been registered in the ERC20 factory "Proxy" at address `0x4200000000000000000000000000000000000012`:
104+
105+
```
106+
Key: 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc
107+
Before: 0x000000000000000000000000c0d3c0d3c0d3c0d3c0d3c0d3c0d3c0d3c0d30012
108+
After: 0x0000000000000000000000006922ac4dbdfedea3a1e5535f12c3171f2b964c91
109+
```
110+
111+
5. For the same contract, verify its `initialized` slot has been set to 1:
112+
113+
```
114+
Key: 0x0000000000000000000000000000000000000000000000000000000000000000
115+
Before: 0x0000000000000000000000000000000000000000000000000000000000000000
116+
After: 0x0000000000000000000000000000000000000000000000000000000000000001
117+
```
118+
119+
6. For the same contract, verify the `bridge` address has been correctly registered:
120+
121+
```
122+
Key: 0x0000000000000000000000000000000000000000000000000000000000000001
123+
Before: 0x0000000000000000000000000000000000000000000000000000000000000000
124+
After: 0x0000000000000000000000004200000000000000000000000000000000000010
125+
```
126+
127+
7. Verify that the L1FeeVault at `0x420000000000000000000000000000000000001a` receives the gas associated with the call:
128+
129+
```
130+
Balance 1132449259793366198 -> 1132449525791013671
131+
```
132+
133+
8. Verify that the nonce for the sending address is appropriately incremented:
134+
135+
```
136+
Nonce 0 -> 1
137+
```
138+
139+
#### 3.3. Extract the domain hash and the message hash to approve.
140+
141+
Now that we have verified the transaction performs the right
142+
operation, we need to extract the domain hash and the message hash to
143+
approve.
144+
145+
Go back to the "Overview" tab, and find the
146+
`GnosisSafeL2.checkSignatures` call. This call's `data` parameter
147+
contains both the domain hash and the message hash that will show up
148+
in your Ledger.
149+
150+
Here is an example screenshot. Note that the hash value may be
151+
different:
152+
153+
![Screenshot 2024-04-18 at 18 53 26](https://github.com/base-org/contract-deployments/assets/33523487/85566275-feab-489d-87e9-6ebc4de7b823)
154+
155+
It will be a concatenation of `0x1901`, the domain hash, and the
156+
message hash: `0x1901[domain hash][message hash]`.
157+
158+
Note down this value. You will need to compare it with the ones
159+
displayed on the Ledger screen at signing.
160+
161+
### 4. Approve the signature on your ledger
162+
163+
Once the validations are done, it's time to actually sign the
164+
transaction. Make sure your ledger is still unlocked and run the
165+
following:
166+
167+
``` shell
168+
make sign-op # or make sign-cb for Coinbase signers
169+
```
170+
171+
> [!IMPORTANT] This is the most security critical part of the
172+
> playbook: make sure the domain hash and message hash in the
173+
> following two places match:
174+
175+
1. on your Ledger screen.
176+
2. in the Tenderly simulation. You should use the same Tenderly
177+
simulation as the one you used to verify the state diffs, instead
178+
of opening the new one printed in the console.
179+
180+
181+
There is no need to verify anything printed in the console. There is
182+
no need to open the new Tenderly simulation link either.
183+
184+
After verification, sign the transaction. You will see the `Data`,
185+
`Signer` and `Signature` printed in the console. Format should be
186+
something like this:
187+
188+
```
189+
Data: <DATA>
190+
Signer: <ADDRESS>
191+
Signature: <SIGNATURE>
192+
```
193+
194+
Double check the signer address is the right one.
195+
196+
### 5. Send the output to Facilitator(s)
197+
198+
Nothing has occurred onchain - these are offchain signatures which
199+
will be collected by Facilitators for execution. Execution can occur
200+
by anyone once a threshold of signatures are collected, so a
201+
Facilitator will do the final execution for convenience.
202+
203+
Share the `Data`, `Signer` and `Signature` with the Facilitator, and
204+
congrats, you are done!
205+
206+
## [For Facilitator ONLY] How to execute the rehearsal
207+
208+
### [After the rehearsal] Execute the output
209+
210+
1. Collect outputs from all participating signers.
211+
2. Concatenate all signatures and export it as the `SIGNATURES`
212+
environment variable, i.e. `export
213+
SIGNATURES="0x[SIGNATURE1][SIGNATURE2]..."`.
214+
3. Run `make approve-cb` with Coinbase signer signatures.
215+
4. Run `make approve-op` with Optimism signer signatures.
216+
4. Run `make run` to execute the transaction onchain.
217+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
[profile.default]
2+
src = 'src'
3+
out = 'out'
4+
libs = ['lib']
5+
broadcast = 'records'
6+
fs_permissions = [{ access = "read-write", path = "./" }]
7+
optimizer = true
8+
optimizer_runs = 999999
9+
solc_version = "0.8.19"
10+
via-ir = true
11+
remappings = [
12+
'@eth-optimism-bedrock/=lib/optimism/packages/contracts-bedrock/',
13+
'@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts',
14+
'@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts',
15+
'@rari-capital/solmate/=lib/solmate/',
16+
'@base-contracts/=lib/base-contracts',
17+
'solady/=lib/solady/src/',
18+
]
19+
20+
# See more config options https://github.com/foundry-rs/foundry/tree/master/config
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity 0.8.19;
3+
4+
import "@base-contracts/script/universal/NestedMultisigBuilder.sol";
5+
6+
interface IProxyAdmin {
7+
function upgradeAndCall(address payable proxy, address implementation, bytes memory data) external payable;
8+
}
9+
10+
interface IImpl {
11+
function initialize(address _bridge) external;
12+
function bridge() external returns (address);
13+
}
14+
15+
bytes32 constant IMPLEMENTATION_KEY = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
16+
17+
contract UpgradeOptimismMintableERC20Factory is NestedMultisigBuilder {
18+
address public BRIDGE = vm.envAddress("BRIDGE");
19+
address public PROXY_ADMIN = vm.envAddress("PROXY_ADMIN");
20+
address public ERC20_FACTORY = vm.envAddress("ERC20_FACTORY");
21+
address public ERC20_FACTORY_IMPL = vm.envAddress("ERC20_FACTORY_IMPL");
22+
23+
address internal NESTED_SAFE = vm.envAddress("NESTED_SAFE");
24+
25+
function _postCheck(Vm.AccountAccess[] memory, SimulationPayload memory) internal override {
26+
address impl = address(uint160(uint256(vm.load(ERC20_FACTORY, IMPLEMENTATION_KEY))));
27+
if (impl != ERC20_FACTORY_IMPL) {
28+
revert("Implementation not correctly set");
29+
}
30+
31+
address bridge_ = IImpl(ERC20_FACTORY).bridge();
32+
if (bridge_ != BRIDGE) {
33+
revert("Implementation not correctly initialized");
34+
}
35+
}
36+
37+
function _buildCalls() internal view override returns (IMulticall3.Call3[] memory) {
38+
IMulticall3.Call3[] memory calls = new IMulticall3.Call3[](1);
39+
40+
calls[0] = IMulticall3.Call3({
41+
target: address(PROXY_ADMIN),
42+
allowFailure: false,
43+
callData: abi.encodeCall(
44+
IProxyAdmin.upgradeAndCall,
45+
(payable(ERC20_FACTORY), ERC20_FACTORY_IMPL, abi.encodeCall(IImpl.initialize, (BRIDGE)))
46+
)
47+
});
48+
49+
return calls;
50+
}
51+
52+
function _ownerSafe() internal view override returns (address) {
53+
return NESTED_SAFE;
54+
}
55+
}

0 commit comments

Comments
 (0)