github.com/ethereum-optimism/optimism@v1.7.2/packages/contracts-bedrock/test/universal/Proxy.t.sol (about) 1 // SPDX-License-Identifier: MIT 2 pragma solidity 0.8.15; 3 4 import { Test } from "forge-std/Test.sol"; 5 import { Proxy } from "src/universal/Proxy.sol"; 6 import { Bytes32AddressLib } from "@rari-capital/solmate/src/utils/Bytes32AddressLib.sol"; 7 8 contract SimpleStorage { 9 mapping(uint256 => uint256) internal store; 10 11 function get(uint256 key) external payable returns (uint256) { 12 return store[key]; 13 } 14 15 function set(uint256 key, uint256 value) external payable { 16 store[key] = value; 17 } 18 } 19 20 contract Clasher { 21 function upgradeTo(address) external pure { 22 revert("upgradeTo"); 23 } 24 } 25 26 contract Proxy_Test is Test { 27 event Upgraded(address indexed implementation); 28 event AdminChanged(address previousAdmin, address newAdmin); 29 30 address alice = address(64); 31 32 bytes32 internal constant IMPLEMENTATION_KEY = bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1); 33 34 bytes32 internal constant OWNER_KEY = bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1); 35 36 Proxy proxy; 37 SimpleStorage simpleStorage; 38 39 function setUp() external { 40 // Deploy a proxy and simple storage contract as 41 // the implementation 42 proxy = new Proxy(alice); 43 simpleStorage = new SimpleStorage(); 44 45 vm.prank(alice); 46 proxy.upgradeTo(address(simpleStorage)); 47 } 48 49 function test_implementationKey_succeeds() external { 50 // The hardcoded implementation key should be correct 51 vm.prank(alice); 52 proxy.upgradeTo(address(6)); 53 54 bytes32 key = vm.load(address(proxy), IMPLEMENTATION_KEY); 55 assertEq(address(6), Bytes32AddressLib.fromLast20Bytes(key)); 56 57 vm.prank(alice); 58 address impl = proxy.implementation(); 59 assertEq(impl, address(6)); 60 } 61 62 function test_ownerKey_succeeds() external { 63 // The hardcoded owner key should be correct 64 vm.prank(alice); 65 proxy.changeAdmin(address(6)); 66 67 bytes32 key = vm.load(address(proxy), OWNER_KEY); 68 assertEq(address(6), Bytes32AddressLib.fromLast20Bytes(key)); 69 70 vm.prank(address(6)); 71 address owner = proxy.admin(); 72 assertEq(owner, address(6)); 73 } 74 75 function test_proxyCallToImp_notAdmin_succeeds() external { 76 // The implementation does not have a `upgradeTo` 77 // method, calling `upgradeTo` not as the owner 78 // should revert. 79 vm.expectRevert(bytes("")); 80 proxy.upgradeTo(address(64)); 81 82 // Call `upgradeTo` as the owner, it should succeed 83 // and emit the `Upgraded` event. 84 vm.expectEmit(true, true, true, true); 85 emit Upgraded(address(64)); 86 vm.prank(alice); 87 proxy.upgradeTo(address(64)); 88 89 // Get the implementation as the owner 90 vm.prank(alice); 91 address impl = proxy.implementation(); 92 assertEq(impl, address(64)); 93 } 94 95 function test_ownerProxyCall_notAdmin_succeeds() external { 96 // Calling `changeAdmin` not as the owner should revert 97 // as the implementation does not have a `changeAdmin` method. 98 vm.expectRevert(bytes("")); 99 proxy.changeAdmin(address(1)); 100 101 // Call `changeAdmin` as the owner, it should succeed 102 // and emit the `AdminChanged` event. 103 vm.expectEmit(true, true, true, true); 104 emit AdminChanged(alice, address(1)); 105 vm.prank(alice); 106 proxy.changeAdmin(address(1)); 107 108 // Calling `admin` not as the owner should 109 // revert as the implementation does not have 110 // a `admin` method. 111 vm.expectRevert(bytes("")); 112 proxy.admin(); 113 114 // Calling `admin` as the owner should work. 115 vm.prank(address(1)); 116 address owner = proxy.admin(); 117 assertEq(owner, address(1)); 118 } 119 120 function test_delegatesToImpl_succeeds() external { 121 // Call the storage setter on the proxy 122 SimpleStorage(address(proxy)).set(1, 1); 123 124 // The key should not be set in the implementation 125 uint256 result = simpleStorage.get(1); 126 assertEq(result, 0); 127 { 128 // The key should be set in the proxy 129 uint256 expect = SimpleStorage(address(proxy)).get(1); 130 assertEq(expect, 1); 131 } 132 133 { 134 // The owner should be able to call through the proxy 135 // when there is not a function selector crash 136 vm.prank(alice); 137 uint256 expect = SimpleStorage(address(proxy)).get(1); 138 assertEq(expect, 1); 139 } 140 } 141 142 function test_upgradeToAndCall_succeeds() external { 143 { 144 // There should be nothing in the current proxy storage 145 uint256 expect = SimpleStorage(address(proxy)).get(1); 146 assertEq(expect, 0); 147 } 148 149 // Deploy a new SimpleStorage 150 simpleStorage = new SimpleStorage(); 151 152 // Set the new SimpleStorage as the implementation 153 // and call. 154 vm.expectEmit(true, true, true, true); 155 emit Upgraded(address(simpleStorage)); 156 vm.prank(alice); 157 proxy.upgradeToAndCall(address(simpleStorage), abi.encodeWithSelector(simpleStorage.set.selector, 1, 1)); 158 159 // The call should have impacted the state 160 uint256 result = SimpleStorage(address(proxy)).get(1); 161 assertEq(result, 1); 162 } 163 164 function test_upgradeToAndCall_functionDoesNotExist_reverts() external { 165 // Get the current implementation address 166 vm.prank(alice); 167 address impl = proxy.implementation(); 168 assertEq(impl, address(simpleStorage)); 169 170 // Deploy a new SimpleStorage 171 simpleStorage = new SimpleStorage(); 172 173 // Set the new SimpleStorage as the implementation 174 // and call. This reverts because the calldata doesn't 175 // match a function on the implementation. 176 vm.expectRevert("Proxy: delegatecall to new implementation contract failed"); 177 vm.prank(alice); 178 proxy.upgradeToAndCall(address(simpleStorage), hex""); 179 180 // The implementation address should have not 181 // updated because the call to `upgradeToAndCall` 182 // reverted. 183 vm.prank(alice); 184 address postImpl = proxy.implementation(); 185 assertEq(impl, postImpl); 186 187 // The attempt to `upgradeToAndCall` 188 // should revert when it is not called by the owner. 189 vm.expectRevert(bytes("")); 190 proxy.upgradeToAndCall(address(simpleStorage), abi.encodeWithSelector(simpleStorage.set.selector, 1, 1)); 191 } 192 193 function test_upgradeToAndCall_isPayable_succeeds() external { 194 // Give alice some funds 195 vm.deal(alice, 1 ether); 196 // Set the implementation and call and send 197 // value. 198 vm.prank(alice); 199 proxy.upgradeToAndCall{ value: 1 ether }( 200 address(simpleStorage), abi.encodeWithSelector(simpleStorage.set.selector, 1, 1) 201 ); 202 203 // The implementation address should be correct 204 vm.prank(alice); 205 address impl = proxy.implementation(); 206 assertEq(impl, address(simpleStorage)); 207 208 // The proxy should have a balance 209 assertEq(address(proxy).balance, 1 ether); 210 } 211 212 function test_upgradeTo_clashingFunctionSignatures_succeeds() external { 213 // Clasher has a clashing function with the proxy. 214 Clasher clasher = new Clasher(); 215 216 // Set the clasher as the implementation. 217 vm.prank(alice); 218 proxy.upgradeTo(address(clasher)); 219 220 { 221 // Assert that the implementation was set properly. 222 vm.prank(alice); 223 address impl = proxy.implementation(); 224 assertEq(impl, address(clasher)); 225 } 226 227 // Call the clashing function on the proxy 228 // not as the owner so that the call passes through. 229 // The implementation will revert so we can be 230 // sure that the call passed through. 231 vm.expectRevert(bytes("upgradeTo")); 232 proxy.upgradeTo(address(0)); 233 234 { 235 // Now call the clashing function as the owner 236 // and be sure that it doesn't pass through to 237 // the implementation. 238 vm.prank(alice); 239 proxy.upgradeTo(address(0)); 240 vm.prank(alice); 241 address impl = proxy.implementation(); 242 assertEq(impl, address(0)); 243 } 244 } 245 246 // Allow for `eth_call` to call proxy methods 247 // by setting "from" to `address(0)`. 248 function test_implementation_zeroAddressCaller_succeeds() external { 249 vm.prank(address(0)); 250 address impl = proxy.implementation(); 251 assertEq(impl, address(simpleStorage)); 252 } 253 254 function test_implementation_isZeroAddress_reverts() external { 255 // Set `address(0)` as the implementation. 256 vm.prank(alice); 257 proxy.upgradeTo(address(0)); 258 259 (bool success, bytes memory returndata) = address(proxy).call(hex""); 260 assertEq(success, false); 261 262 bytes memory err = abi.encodeWithSignature("Error(string)", "Proxy: implementation not initialized"); 263 264 assertEq(returndata, err); 265 } 266 }