|
103 | 103 | ``` |
104 | 104 | make deploy-example MERKLE_ROOT=<claims-merkle-root> TIMESTAMP=2733427549 |
105 | 105 | ``` |
| 106 | + |
| 107 | +# Contract upgrade instructions |
| 108 | + |
| 109 | +To upgrade a contract, first make sure you pause the contract if it's not paused already (NOTE: the erc20 cannot be paused, the claim contract can though). Once that's done, clone the `aligned_layer` repo and go into the `claim_contracts` directory: |
| 110 | + |
| 111 | +> [!NOTE] |
| 112 | +> The ERC20 cannot be paused. Only the claimable airdrop proxy can be paused. |
| 113 | +
|
| 114 | +``` |
| 115 | +git clone [email protected]:yetanotherco/aligned_layer.git && cd aligned_layer/claim_contracts |
| 116 | +``` |
| 117 | + |
| 118 | +## Write the new contract implementation |
| 119 | + |
| 120 | +This implementation will most likely be a copy paste of the old implementation, only with one or few changes. In addition to that, there is one thing that MUST be done on this new contract: |
| 121 | + |
| 122 | +- Add a public `reinitalize function` with a `reinitializer()` modifier that takes in the next version number of the contract (the first version is `1`). As an example, if this is the first upgrade being done, you should add this function to the contract: |
| 123 | + |
| 124 | +> [!WARNING] |
| 125 | +> DO NOT UPDATE STORAGE VARIABLES IN THIS AND FOLLOWING UPGRADES, ONLY ADD NEW ONES. |
| 126 | +
|
| 127 | +```solidity |
| 128 | +function reinitialize() public reinitializer(2) { |
| 129 | + if (!paused()) { |
| 130 | + _pause(); |
| 131 | + } |
| 132 | + } |
| 133 | +``` |
| 134 | + |
| 135 | +Put the new implementation in a file inside the `src` directory with an appropriate name. |
| 136 | + |
| 137 | +## Write the deployment script |
| 138 | + |
| 139 | +Under the `script` directory, create a new forge script (with the `.s.sol` extension) with a name like `UpgradeContract.s.sol`, with this code in it: |
| 140 | + |
| 141 | +```solidity |
| 142 | +// SPDX-License-Identifier: MIT |
| 143 | +pragma solidity ^0.8.19; |
| 144 | +
|
| 145 | +import <path_to_upgrade_contract>; |
| 146 | +import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; |
| 147 | +import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; |
| 148 | +import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol"; |
| 149 | +import "forge-std/Script.sol"; |
| 150 | +import {Vm} from "forge-std/Vm.sol"; |
| 151 | +import {Utils} from "./Utils.sol"; |
| 152 | +
|
| 153 | +/// @notice Upgrade contract template |
| 154 | +contract UpgradeContract is Script { |
| 155 | + function run(string memory config) public { |
| 156 | + string memory root = vm.projectRoot(); |
| 157 | + string memory path = string.concat( |
| 158 | + root, |
| 159 | + "/script-config/config.", |
| 160 | + config, |
| 161 | + ".json" |
| 162 | + ); |
| 163 | + string memory config_json = vm.readFile(path); |
| 164 | +
|
| 165 | + address _currentContractProxy = stdJson.readAddress( |
| 166 | + config_json, |
| 167 | + ".contractProxy" |
| 168 | + ); |
| 169 | +
|
| 170 | + vm.broadcast(); |
| 171 | + <NameOfUpgradeContract> _newContract = new <NameOfUpgradeContract>(); |
| 172 | +
|
| 173 | + bytes memory _upgradeCalldata = abi.encodeCall( |
| 174 | + ProxyAdmin.upgradeAndCall, |
| 175 | + ( |
| 176 | + ITransparentUpgradeableProxy(_currentContractProxy), |
| 177 | + address(_newContract), |
| 178 | + abi.encodeCall(<NameOfUpgradeContract>.reinitialize, ()) |
| 179 | + ) |
| 180 | + ); |
| 181 | +
|
| 182 | + console.log( |
| 183 | + "Proxy Admin to call:", |
| 184 | + getAdminAddress(_currentContractProxy) |
| 185 | + ); |
| 186 | + console.log("Calldata of the transaction: "); |
| 187 | + console.logBytes(_upgradeCalldata); |
| 188 | + } |
| 189 | +
|
| 190 | + function getAdminAddress(address proxy) internal view returns (address) { |
| 191 | + address CHEATCODE_ADDRESS = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D; |
| 192 | + Vm vm = Vm(CHEATCODE_ADDRESS); |
| 193 | +
|
| 194 | + bytes32 adminSlot = vm.load(proxy, ERC1967Utils.ADMIN_SLOT); |
| 195 | + return address(uint160(uint256(adminSlot))); |
| 196 | + } |
| 197 | +} |
| 198 | +
|
| 199 | +``` |
| 200 | + |
| 201 | +then fill in the missing parts (between `<>` brackets), putting the path to the new contract code and the name of it. |
| 202 | + |
| 203 | +> [!IMPORTANT] |
| 204 | +> Remember to fill the missing parts (between `<>` brackets) in the script, putting the path to the new contract code and the name of it where needed. |
| 205 | +
|
| 206 | +Go into the `config.mainnet.json` file inside the `script-config` directory and fill in the following values: |
| 207 | + |
| 208 | +``` |
| 209 | +{ |
| 210 | + "foundation": "", |
| 211 | + "contractProxy": "" |
| 212 | + } |
| 213 | +
|
| 214 | +``` |
| 215 | + |
| 216 | +- `foundation` is the address of the foundation safe. |
| 217 | +- `contractProxy` is the address of the contract proxy to upgrade. |
| 218 | + |
| 219 | +## Run the deployment script |
| 220 | + |
| 221 | +Run the script with |
| 222 | + |
| 223 | +``` |
| 224 | +cd script && \ |
| 225 | + forge script <name_of_the_script.s.sol> \ |
| 226 | + --sig "run(string)" \ |
| 227 | + mainnet \ |
| 228 | + --private-key <private_key> \ |
| 229 | + --rpc-url <mainnet_rpc_url> \ |
| 230 | + --broadcast \ |
| 231 | + --verify \ |
| 232 | + --etherscan-api-key <etherscan_api_key> |
| 233 | +``` |
| 234 | + |
| 235 | +After running this script, it will show a message like this: |
| 236 | + |
| 237 | +``` |
| 238 | +Proxy Admin to call: 0xf447FD34D97317759777E242fF64cEAe9C58Bf9A |
| 239 | +Calldata of the transaction: |
| 240 | +0x9623609d0000000000000000000000000234947ce63d1a5e731e5700b911fb32ec54c3c3000000000000000000000000f7ac74dbc77e1afda093598c912a6b082dabc31a000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000046c2eb35000000000000000000000000000000000000000000000000000000000 |
| 241 | +``` |
| 242 | + |
| 243 | +Go into the foundation safe, create a new transaction calling the proxy admin address shown in the message with the message's calldata. Done. |
0 commit comments