DeployUtils.sol 15.5 KB
Newer Older
1
// SPDX-License-Identifier: MIT
2
pragma solidity ^0.8.0;
3

4
// Scripts
5
import { Vm } from "forge-std/Vm.sol";
6 7 8 9 10
import { console2 as console } from "forge-std/console2.sol";
import { Artifacts } from "scripts/Artifacts.s.sol";

// Libraries
import { LibString } from "@solady/utils/LibString.sol";
11
import { Bytes } from "src/libraries/Bytes.sol";
12
import { Constants } from "src/libraries/Constants.sol";
13

14
// Interfaces
15 16 17 18
import { IProxy } from "interfaces/universal/IProxy.sol";
import { IAddressManager } from "interfaces/legacy/IAddressManager.sol";
import { IL1ChugSplashProxy, IStaticL1ChugSplashProxy } from "interfaces/legacy/IL1ChugSplashProxy.sol";
import { IResolvedDelegateProxy } from "interfaces/legacy/IResolvedDelegateProxy.sol";
19 20

library DeployUtils {
21 22
    Vm internal constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code")))));

23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
    /// @notice Deploys a contract with the given name and arguments via CREATE.
    /// @param _name Name of the contract to deploy.
    /// @param _args ABI-encoded constructor arguments.
    /// @return addr_ Address of the deployed contract.
    function create1(string memory _name, bytes memory _args) internal returns (address payable addr_) {
        bytes memory bytecode = abi.encodePacked(vm.getCode(_name), _args);
        assembly {
            addr_ := create(0, add(bytecode, 0x20), mload(bytecode))
        }
        assertValidContractAddress(addr_);
    }

    /// @notice Deploys a contract with the given name via CREATE.
    /// @param _name Name of the contract to deploy.
    /// @return Address of the deployed contract.
    function create1(string memory _name) internal returns (address payable) {
        return create1(_name, hex"");
    }

    /// @notice Deploys a contract with the given name and arguments via CREATE and saves the result.
    /// @param _save Artifacts contract.
    /// @param _name Name of the contract to deploy.
    /// @param _nick Nickname to save the address to.
    /// @param _args ABI-encoded constructor arguments.
    /// @return addr_ Address of the deployed contract.
    function create1AndSave(
        Artifacts _save,
        string memory _name,
        string memory _nick,
        bytes memory _args
    )
        internal
        returns (address payable addr_)
    {
        console.log("Deploying %s", _nick);
        addr_ = create1(_name, _args);
        _save.save(_nick, addr_);
        console.log("%s deployed at %s", _nick, addr_);
    }

    /// @notice Deploys a contract with the given name via CREATE and saves the result.
    /// @param _save Artifacts contract.
    /// @param _name Name of the contract to deploy.
    /// @param _nickname Nickname to save the address to.
    /// @return addr_ Address of the deployed contract.
    function create1AndSave(
        Artifacts _save,
        string memory _name,
        string memory _nickname
    )
        internal
        returns (address payable addr_)
    {
        return create1AndSave(_save, _name, _nickname, hex"");
    }

    /// @notice Deploys a contract with the given name and arguments via CREATE and saves the result.
    /// @param _save Artifacts contract.
    /// @param _name Name of the contract to deploy.
    /// @param _args ABI-encoded constructor arguments.
    /// @return addr_ Address of the deployed contract.
    function create1AndSave(
        Artifacts _save,
        string memory _name,
        bytes memory _args
    )
        internal
        returns (address payable addr_)
    {
        return create1AndSave(_save, _name, _name, _args);
    }

    /// @notice Deploys a contract with the given name and arguments via CREATE2.
    /// @param _name Name of the contract to deploy.
    /// @param _args ABI-encoded constructor arguments.
    /// @param _salt Salt for the CREATE2 operation.
    /// @return addr_ Address of the deployed contract.
    function create2(string memory _name, bytes memory _args, bytes32 _salt) internal returns (address payable addr_) {
        bytes memory initCode = abi.encodePacked(vm.getCode(_name), _args);
        address preComputedAddress = vm.computeCreate2Address(_salt, keccak256(initCode));
        require(preComputedAddress.code.length == 0, "DeployUtils: contract already deployed");
        assembly {
            addr_ := create2(0, add(initCode, 0x20), mload(initCode), _salt)
106 107 108 109 110
            if iszero(addr_) {
                let size := returndatasize()
                returndatacopy(0, 0, size)
                revert(0, size)
            }
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
        }
        assertValidContractAddress(addr_);
    }

    /// @notice Deploys a contract with the given name via CREATE2.
    /// @param _name Name of the contract to deploy.
    /// @param _salt Salt for the CREATE2 operation.
    /// @return Address of the deployed contract.
    function create2(string memory _name, bytes32 _salt) internal returns (address payable) {
        return create2(_name, hex"", _salt);
    }

    /// @notice Deploys a contract with the given name and arguments via CREATE2 and saves the result.
    /// @param _save Artifacts contract.
    /// @param _name Name of the contract to deploy.
    /// @param _nick Nickname to save the address to.
    /// @param _args ABI-encoded constructor arguments.
    /// @param _salt Salt for the CREATE2 operation.
    /// @return addr_ Address of the deployed contract.
    function create2AndSave(
        Artifacts _save,
        string memory _name,
        string memory _nick,
        bytes memory _args,
        bytes32 _salt
    )
        internal
        returns (address payable addr_)
    {
        console.log("Deploying %s", _nick);
        addr_ = create2(_name, _args, _salt);
        _save.save(_nick, addr_);
        console.log("%s deployed at %s", _nick, addr_);
    }

    /// @notice Deploys a contract with the given name via CREATE2 and saves the result.
    /// @param _save Artifacts contract.
    /// @param _name Name of the contract to deploy.
    /// @param _nick Nickname to save the address to.
    /// @param _salt Salt for the CREATE2 operation.
    /// @return addr_ Address of the deployed contract.
    function create2AndSave(
        Artifacts _save,
        string memory _name,
        string memory _nick,
        bytes32 _salt
    )
        internal
        returns (address payable addr_)
    {
        return create2AndSave(_save, _name, _nick, hex"", _salt);
    }

    /// @notice Deploys a contract with the given name and arguments via CREATE2 and saves the result.
    /// @param _save Artifacts contract.
    /// @param _name Name of the contract to deploy.
    /// @param _args ABI-encoded constructor arguments.
    /// @param _salt Salt for the CREATE2 operation.
    /// @return addr_ Address of the deployed contract.
    function create2AndSave(
        Artifacts _save,
        string memory _name,
        bytes memory _args,
        bytes32 _salt
    )
        internal
        returns (address payable addr_)
    {
        return create2AndSave(_save, _name, _name, _args, _salt);
    }

    /// @notice Deploys a contract with the given name via CREATE2 and saves the result.
    /// @param _save Artifacts contract.
    /// @param _name Name of the contract to deploy.
    /// @param _salt Salt for the CREATE2 operation.
    /// @return addr_ Address of the deployed contract.
    function create2AndSave(
        Artifacts _save,
        string memory _name,
        bytes32 _salt
    )
        internal
        returns (address payable addr_)
    {
        return create2AndSave(_save, _name, _name, hex"", _salt);
    }

    /// @notice Takes a sender and an identifier and returns a deterministic address based on the
    ///         two. The result is used to etch the input and output contracts to a deterministic
    ///         address based on those two values, where the identifier represents the input or
    ///         output contract, such as `optimism.DeploySuperchainInput` or
    ///         `optimism.DeployOPChainOutput`.
    ///         Example: `toIOAddress(msg.sender, "optimism.DeploySuperchainInput")`
    /// @param _sender Address of the sender.
    /// @param _identifier Additional identifier.
    /// @return Deterministic address.
207 208 209 210
    function toIOAddress(address _sender, string memory _identifier) internal pure returns (address) {
        return address(uint160(uint256(keccak256(abi.encode(_sender, _identifier)))));
    }

211 212 213 214 215
    /// @notice Strips the first 4 bytes of `_data` and returns the remaining bytes
    ///         If `_data` is not greater than 4 bytes, it returns empty bytes type.
    /// @param _data constructor arguments prefixed with a psuedo-constructor function signature
    /// @return encodedData_ constructor arguments without the psuedo-constructor function signature prefix
    function encodeConstructor(bytes memory _data) internal pure returns (bytes memory encodedData_) {
216
        require(_data.length >= 4, "DeployUtils: encodeConstructor takes in _data of length >= 4");
217 218 219
        encodedData_ = Bytes.slice(_data, 4);
    }

220 221
    /// @notice Asserts that the given address is a valid contract address.
    /// @param _who Address to check.
222
    function assertValidContractAddress(address _who) internal view {
223 224 225 226
        // Foundry will set returned address to address(1) whenever a contract creation fails
        // inside of a test. If this is the case then let Foundry handle the error itself and don't
        // trigger a revert (which would probably break a test).
        if (_who == address(1)) return;
227 228 229 230
        require(_who != address(0), "DeployUtils: zero address");
        require(_who.code.length > 0, string.concat("DeployUtils: no code at ", LibString.toHexStringChecksummed(_who)));
    }

231 232
    /// @notice Asserts that the given proxy has an implementation set.
    /// @param _proxy Proxy to check.
233
    function assertERC1967ImplementationSet(address _proxy) internal returns (address implementation_) {
234 235 236
        // We prank as the zero address due to the Proxy's `proxyCallIfNotAdmin` modifier.
        // Pranking inside this function also means it can no longer be considered `view`.
        vm.prank(address(0));
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
        implementation_ = IProxy(payable(_proxy)).implementation();
        assertValidContractAddress(implementation_);
    }

    /// @notice Asserts that the given L1ChugSplashProxy has an implementation set.
    /// @param _proxy L1ChugSplashProxy to check.
    function assertL1ChugSplashImplementationSet(address _proxy) internal returns (address implementation_) {
        vm.prank(address(0));
        implementation_ = IStaticL1ChugSplashProxy(_proxy).getImplementation();
        assertValidContractAddress(implementation_);
    }

    /// @notice Asserts that the given ResolvedDelegateProxy has an implementation set.
    /// @param _implementationName Name of the implementation contract.
    /// @param _addressManager AddressManager contract.
    function assertResolvedDelegateProxyImplementationSet(
        string memory _implementationName,
        IAddressManager _addressManager
    )
        internal
        view
        returns (address implementation_)
    {
        implementation_ = _addressManager.getAddress(_implementationName);
        assertValidContractAddress(implementation_);
    }

    /// @notice Builds an ERC1967 Proxy with a dummy implementation.
    /// @param _proxyImplName Name of the implementation contract.
266
    function buildERC1967ProxyWithImpl(string memory _proxyImplName) internal returns (IProxy genericProxy_) {
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
        genericProxy_ = IProxy(
            create1({
                _name: "Proxy",
                _args: DeployUtils.encodeConstructor(abi.encodeCall(IProxy.__constructor__, (address(0))))
            })
        );
        address implementation = address(vm.addr(uint256(keccak256(abi.encodePacked(_proxyImplName)))));
        vm.etch(address(implementation), hex"01");
        vm.prank(address(0));
        genericProxy_.upgradeTo(address(implementation));
        vm.etch(address(genericProxy_), address(genericProxy_).code);
    }

    /// @notice Builds an L1ChugSplashProxy with a dummy implementation.
    /// @param _proxyImplName Name of the implementation contract.
282 283 284 285
    function buildL1ChugSplashProxyWithImpl(string memory _proxyImplName)
        internal
        returns (IL1ChugSplashProxy proxy_)
    {
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304
        proxy_ = IL1ChugSplashProxy(
            create1({
                _name: "L1ChugSplashProxy",
                _args: DeployUtils.encodeConstructor(abi.encodeCall(IL1ChugSplashProxy.__constructor__, (address(0))))
            })
        );
        address implementation = address(vm.addr(uint256(keccak256(abi.encodePacked(_proxyImplName)))));
        vm.etch(address(implementation), hex"01");
        vm.prank(address(0));
        proxy_.setStorage(Constants.PROXY_IMPLEMENTATION_ADDRESS, bytes32(uint256(uint160(implementation))));
    }

    /// @notice Builds a ResolvedDelegateProxy with a dummy implementation.
    /// @param _addressManager AddressManager contract.
    /// @param _proxyImplName Name of the implementation contract.
    function buildResolvedDelegateProxyWithImpl(
        IAddressManager _addressManager,
        string memory _proxyImplName
    )
305
        internal
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
        returns (IResolvedDelegateProxy proxy_)
    {
        proxy_ = IResolvedDelegateProxy(
            create1({
                _name: "ResolvedDelegateProxy",
                _args: DeployUtils.encodeConstructor(
                    abi.encodeCall(IResolvedDelegateProxy.__constructor__, (_addressManager, _proxyImplName))
                )
            })
        );
        address implementation = address(vm.addr(uint256(keccak256(abi.encodePacked(_proxyImplName)))));
        vm.etch(address(implementation), hex"01");
        _addressManager.setAddress(_proxyImplName, implementation);
    }

    /// @notice Builds an AddressManager contract.
322
    function buildAddressManager() internal returns (IAddressManager addressManager_) {
323 324 325 326 327 328
        addressManager_ = IAddressManager(
            create1({
                _name: "AddressManager",
                _args: DeployUtils.encodeConstructor(abi.encodeCall(IAddressManager.__constructor__, ()))
            })
        );
329 330
    }

331 332
    /// @notice Asserts that the given addresses are valid contract addresses.
    /// @param _addrs Addresses to check.
333 334 335 336 337 338 339 340 341 342 343
    function assertValidContractAddresses(address[] memory _addrs) internal view {
        // Assert that all addresses are non-zero and have code.
        // We use LibString to avoid the need for adding cheatcodes to this contract.
        for (uint256 i = 0; i < _addrs.length; i++) {
            address who = _addrs[i];
            assertValidContractAddress(who);
        }

        // All addresses should be unique.
        for (uint256 i = 0; i < _addrs.length; i++) {
            for (uint256 j = i + 1; j < _addrs.length; j++) {
344 345 346 347 348 349
                require(
                    _addrs[i] != _addrs[j],
                    string.concat(
                        "DeployUtils: check failed, duplicates at ", LibString.toString(i), ",", LibString.toString(j)
                    )
                );
350 351 352
            }
        }
    }
353

354 355 356
    /// @notice Asserts that for a given contract the value of a storage slot at an offset is 1 or
    ///         `type(uint8).max`. The value is set to 1 when a contract is initialized, and set to
    ///         `type(uint8).max` when `_disableInitializers` is called.
357 358
    function assertInitialized(address _contractAddress, uint256 _slot, uint256 _offset) internal view {
        bytes32 slotVal = vm.load(_contractAddress, bytes32(_slot));
359
        uint8 value = uint8((uint256(slotVal) >> (_offset * 8)) & 0xFF);
360
        require(
361
            value == 1 || value == type(uint8).max,
362
            "DeployUtils: value at the given slot and offset does not indicate initialization"
363 364
        );
    }
365
}