Quick Guide !!

We have prepared a set of scripts to deploy all contracts in one click. Otherwise, you can look into and customize each contract as you like.

we reference to OPStack 's version of v1.9.4

4.1 : Prerequisites

Make sure you have run the deploy script for superchain layer:

You should have following file with below fields saved at the deployments/31337/.save.json. Otherwise, as specified in .env.<network>.local.

{
"SafeProxyFactory": "<ADDRESS_1>",
"SafeSingleton": "<ADDRESS_2>",
"SystemOwnerSafe": "<ADDRESS_3>",
"OptimismPortalProxy": "<ADDRESS_4>",
"ProxyAdmin": "<ADDRESS_5>",
"SuperchainConfigProxy": "<ADDRESS_6>",
"SuperchainConfig": "<ADDRESS_7>",
"ProtocolVersionsProxy": "<ADDRESS_8>",
"ProtocolVersions": "<ADDRESS_9>"
}

Without this artifact file, the next deployment scripts can not be run.

One-Click L2 Opchain (Proxies) Deployment

Note: This script is not completed yet. It is for one layer only. Check our final script for full deployment.

In your terminal, copy below contracts' codes and run deployment scripts to your prefered network:

forge script script/000_DeployAllScript.s.sol --trezor --sender <DEPLOYER_ADDRESS> --rpc-url <RPC_URL> --broadcast

(Optional), you can specify your derivation path:

--mnemonic-derivation-paths "m/44'/60'/0'/0/0"

Contract Settings

Step 1

Owner

Step 2

Step 3

Step 4.1

Deploy Script:
000_DeployAllScript.s.sol
        
          // SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {DeploySafeProxyScript} from "@script/101_DeploySafeProxyScript.s.sol";
import {Script} from "@redprint-forge-std/Script.sol";
import {SetupOpAltDAScript} from "@script/300_SetupOpAltDAScript.s.sol";
import {SetupOpchainScript} from "@script/400_SetupOpchain.s.sol";
import {SetupSuperchainScript} from "@script/200_SetupSuperchain.s.sol";

contract DeployAllScript is Script {
    function run() public {
        DeploySafeProxyScript safeDeployments = new DeploySafeProxyScript();
        //1) set up Safe Multisig
        safeDeployments.deploy();
        SetupSuperchainScript superchainSetups = new SetupSuperchainScript();
        superchainSetups.run();
        SetupOpAltDAScript opAltDASetups = new SetupOpAltDAScript();
        opAltDASetups.run();
        SetupOpchainScript opchainSetups = new SetupOpchainScript();
        opchainSetups.run();
    }
}

        
      

Contract Settings

Step 4

OptimismPortalProxy SystemConfigProxy L1StandardBridgeProxy L1CrossDomainMessengerProxy OptimismMintableERC20FactoryProxy L1ERC721BridgeProxy DisputeGameFactoryProxy L2OutputOracleProxy DelayedWETHProxy PermissionedDelayedWETHProxy AnchorStateRegistryProxy
Deploy Script:
400_SetupOpchainScript.s.sol
        
          // SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {AddressManager} from "@redprint-core/legacy/AddressManager.sol";
import {DeployAnchorStateRegistryProxyScript} from "@script/401K_DeployAnchorStateRegistryProxyScript.s.sol";
import {DeployDelayedWETHProxyScript} from "@script/401I_DeployDelayedWETHProxyScript.s.sol";
import {DeployDisputeGameFactoryProxyScript} from "@script/401G_DeployDisputeGameFactoryProxyScript.s.sol";
import {DeployL1CrossDomainMessengerProxyScript} from "@script/401D_DeployL1CrossDomainMessengerProxyScript.s.sol";
import {DeployL1ERC721BridgeProxyScript} from "@script/401F_DeployL1ERC721BridgeProxyScript.s.sol";
import {DeployL1StandardBridgeProxyScript} from "@script/401C_DeployL1StandardBridgeProxyScript.s.sol";
import {DeployL2OutputOracleProxyScript} from "@script/401H_DeployL2OutputOracleProxyScript.s.sol";
import {DeployOptimismMintableERC20FactoryProxyScript} from "@script/401E_DeployOptimismMintableERC20FactoryProxyScript.s.sol";
import {DeployOptimismPortalProxyScript} from "@script/401A_DeployOptimismPortalProxyScript.s.sol";
import {DeployPermissionedDelayedWETHProxyScript} from "@script/401J_DeployPermissionedDelayedWETHProxyScript.s.sol";
import {DeploySystemConfigProxyScript} from "@script/401B_DeploySystemConfigProxyScript.s.sol";
import {IDeployer, getDeployer} from "@redprint-deploy/deployer/DeployScript.sol";
import {Script} from "@redprint-forge-std/Script.sol";
import {TransferAddressManagerOwnershipScript} from "@script/401L_TransferAddressManagerOwnershipScript.s.sol";
import {Vm, VmSafe} from "@redprint-forge-std/Vm.sol";
import {console} from "@redprint-forge-std/console.sol";

contract SetupOpchainScript is Script {
    IDeployer deployerProcedue;

    function run() public {
        deployerProcedue = getDeployer();
        deployerProcedue.setAutoSave(true);
        
        DeployOptimismPortalProxyScript optimismPortalProxyDeployments = new DeployOptimismPortalProxyScript();
        DeploySystemConfigProxyScript systemConfigProxyDeployments = new DeploySystemConfigProxyScript();
        DeployL1StandardBridgeProxyScript l1StandardBridgeProxyDeployments = new DeployL1StandardBridgeProxyScript();
        DeployL1CrossDomainMessengerProxyScript l1CrossDomainMessengerProxyDeployments = new DeployL1CrossDomainMessengerProxyScript();
        DeployOptimismMintableERC20FactoryProxyScript optimismMintableERC20FactoryProxyDeployments = new DeployOptimismMintableERC20FactoryProxyScript();
        DeployL1ERC721BridgeProxyScript l1ERC721BridgeProxyDeployments = new DeployL1ERC721BridgeProxyScript();
        DeployDisputeGameFactoryProxyScript disputeGameFactoryProxyDeployments = new DeployDisputeGameFactoryProxyScript();
        DeployL2OutputOracleProxyScript l2OutputOracleProxyDeployments = new DeployL2OutputOracleProxyScript();
        DeployDelayedWETHProxyScript delayedWETHProxyDeployments = new DeployDelayedWETHProxyScript();
        DeployPermissionedDelayedWETHProxyScript permissionedDelayedWETHProxyDeployments = new DeployPermissionedDelayedWETHProxyScript();
        DeployAnchorStateRegistryProxyScript anchorStateRegistryProxyDeployments = new DeployAnchorStateRegistryProxyScript();
        TransferAddressManagerOwnershipScript transferAddressManagerOwnership = new TransferAddressManagerOwnershipScript();

        optimismPortalProxyDeployments.deploy();
        systemConfigProxyDeployments.deploy();
        l1StandardBridgeProxyDeployments.deploy();
        l1CrossDomainMessengerProxyDeployments.deploy();
        optimismMintableERC20FactoryProxyDeployments.deploy();
        l1ERC721BridgeProxyDeployments.deploy();
        disputeGameFactoryProxyDeployments.deploy();
        l2OutputOracleProxyDeployments.deploy();
        delayedWETHProxyDeployments.deploy();
        permissionedDelayedWETHProxyDeployments.deploy();
        anchorStateRegistryProxyDeployments.deploy();
        transferAddressManagerOwnership.run();
        
        console.log("Setup Opchain ... ");
        
        console.log("OptimismPortalProxy at: ", deployerProcedue.getAddress("OptimismPortalProxy"));
        console.log("SystemConfigProxy at: ", deployerProcedue.getAddress("SystemConfigProxy"));
        console.log("L1CrossDomainMessengerProxy at: ", deployerProcedue.getAddress("L1CrossDomainMessengerProxy"));
        console.log("L1ERC721BridgeProxy at: ", deployerProcedue.getAddress("L1ERC721BridgeProxy"));

        console.log("DisputeGameFactoryProxy at: ", deployerProcedue.getAddress("DisputeGameFactoryProxy"));
        console.log("L2OutputOracleProxy at: ", deployerProcedue.getAddress("L2OutputOracleProxy"));
        console.log("DelayedWETHProxy at: ", deployerProcedue.getAddress("DelayedWETHProxy"));
        console.log("PermissionedDelayedWETHProxy at: ", deployerProcedue.getAddress("PermissionedDelayedWETHProxy"));
        console.log("AnchorStateRegistryProxy at: ", deployerProcedue.getAddress("AnchorStateRegistryProxy"));
    }
}

        
      

After running the deploy script, the address deployed is saved at deployments/31337/.save.json. Otherwise, as specified in .env.<network>.local.

4.1 : Deploy Proxies Contracts

4.1A : Deploy OptimismPortalProxy Contract

In your terminal, copy below contracts' codes and run deployment scripts to your prefered network:

forge script script/401A_DeployOptimismPortalProxyScript.s.sol --trezor --sender <DEPLOYER_ADDRESS> --rpc-url <RPC_URL> --broadcast

(Optional), you can specify your derivation path:

--mnemonic-derivation-paths "m/44'/60'/0'/0/0"

Contract Settings

Smart Contract:
Proxy.sol
        
          // SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {Constants} from "@redprint-core/libraries/Constants.sol";

/// @custom:security-contact Consult full code at https://github.com/ethereum-optimism/optimism/blob/1.9.4/packages/contracts-bedrock/src/universal/Proxy.sol
contract Proxy {
    /**
     * @notice An event that is emitted each time the implementation is changed. This event is part
     *         of the EIP-1967 specification.
     *
     * @param implementation The address of the implementation contract
     */
    event Upgraded(address indexed implementation);
    /**
     * @notice An event that is emitted each time the owner is upgraded. This event is part of the
     *         EIP-1967 specification.
     *
     * @param previousAdmin The previous owner of the contract
     * @param newAdmin      The new owner of the contract
     */
    event AdminChanged(address previousAdmin, address newAdmin);

    modifier proxyCallIfNotAdmin() {
        if (msg.sender == _getAdmin() || msg.sender == address(0)) {
            _;
        } else {
            // This WILL halt the call frame on completion.
            _doProxyCall();
        }
    }

    constructor(address _admin) {
        _changeAdmin(_admin);
    }

    receive() external payable {
        // Proxy call by default.
        _doProxyCall();
    }

    fallback() external payable {
        // Proxy call by default.
        _doProxyCall();
    }

    function upgradeTo(address _implementation)
        public
        virtual
        returns (proxyCallIfNotAdmin)
    {
        _setImplementation(_implementation);
    }

    function upgradeToAndCall(address _implementation, bytes calldata _data)
        public
        payable virtual proxyCallIfNotAdmin
        returns (bytes memory)
    {
        _setImplementation(_implementation);
        (bool success, bytes memory returndata) = _implementation.delegatecall(_data);
        require(success, "Proxy: delegatecall to new implementation contract failed");
        return returndata;
    }

    function changeAdmin(address _admin) public virtual proxyCallIfNotAdmin {
        _changeAdmin(_admin);
    }

    function admin() public virtual proxyCallIfNotAdmin returns (address) {
        return _getAdmin();
    }

    function implementation()
        public
        virtual proxyCallIfNotAdmin
        returns (address)
    {
        return _getImplementation();
    }

    function _setImplementation(address _implementation) internal {
        bytes32 proxyImplementation = Constants.PROXY_IMPLEMENTATION_ADDRESS;
        assembly {
            sstore(proxyImplementation, _implementation)
        }
        emit Upgraded(_implementation);
    }

    function _changeAdmin(address _admin) internal {
        address previous = _getAdmin();
        bytes32 proxyOwner = Constants.PROXY_OWNER_ADDRESS;
        assembly {
            sstore(proxyOwner, _admin)
        }
        emit AdminChanged(previous, _admin);
    }

    function _doProxyCall() internal {
        address impl = _getImplementation();
        require(impl != address(0), "Proxy: implementation not initialized");
  
        assembly {
            // Copy calldata into memory at 0x0....calldatasize.
            calldatacopy(0x0, 0x0, calldatasize())
  
            // Perform the delegatecall, make sure to pass all available gas.
            let success := delegatecall(gas(), impl, 0x0, calldatasize(), 0x0, 0x0)
  
            // Copy returndata into memory at 0x0....returndatasize. Note that this *will*
            // overwrite the calldata that we just copied into memory but that doesn't really
            // matter because we'll be returning in a second anyway.
            returndatacopy(0x0, 0x0, returndatasize())
  
            // Success == 0 means a revert. We'll revert too and pass the data up.
            if iszero(success) { revert(0x0, returndatasize()) }
  
            // Otherwise we'll just return and pass the data up.
            return(0x0, returndatasize())
        }
    }

    function _getImplementation() internal view returns (address) {
        address impl;
        bytes32 proxyImplementation = Constants.PROXY_IMPLEMENTATION_ADDRESS;
        assembly {
            impl := sload(proxyImplementation)
        }
        return impl;
    }

    function _getAdmin() internal view returns (address) {
        address owner;
        bytes32 proxyOwner = Constants.PROXY_OWNER_ADDRESS;
        assembly {
            owner := sload(proxyOwner)
        }
        return owner;
    }
}

        
      
Deploy Script:
401A_DeployOptimismPortalProxyScript.s.sol
        
          // SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {DeployScript} from "@redprint-deploy/deployer/DeployScript.sol";
import {DeployerFunctions, IDeployer} from "@redprint-deploy/deployer/DeployerFunctions.sol";
import {Proxy} from "@redprint-core/universal/Proxy.sol";

/// @custom:security-contact Consult full internal deploy script at https://github.com/Ratimon/redprint-forge
contract DeployOptimismPortalProxyScript is DeployScript {
    using DeployerFunctions for IDeployer ;
    function deploy() external returns (Proxy) {
        address proxyOwner = deployer.mustGetAddress("ProxyAdmin");

        return Proxy(deployer.deploy_ERC1967Proxy("OptimismPortalProxy", proxyOwner));
    }
}

        
      

After running the deploy script, the address deployed is saved at deployments/31337/.save.json. Otherwise, as specified in .env.<network>.local.

4.1B : Deploy SystemConfigProxy Contract

In your terminal, copy below contracts' codes and run deployment scripts to your prefered network:

forge script script/401B_DeploySystemConfigProxyScript.s.sol --trezor --sender <DEPLOYER_ADDRESS> --rpc-url <RPC_URL> --broadcast

(Optional), you can specify your derivation path:

--mnemonic-derivation-paths "m/44'/60'/0'/0/0"

Contract Settings

Smart Contract:
Proxy.sol
        
          // SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {Constants} from "@redprint-core/libraries/Constants.sol";

/// @custom:security-contact Consult full code at https://github.com/ethereum-optimism/optimism/blob/v1.9.4/packages/contracts-bedrock/src/universal/Proxy.sol
contract Proxy {
    /**
     * @notice An event that is emitted each time the implementation is changed. This event is part
     *         of the EIP-1967 specification.
     *
     * @param implementation The address of the implementation contract
     */
    event Upgraded(address indexed implementation);
    /**
     * @notice An event that is emitted each time the owner is upgraded. This event is part of the
     *         EIP-1967 specification.
     *
     * @param previousAdmin The previous owner of the contract
     * @param newAdmin      The new owner of the contract
     */
    event AdminChanged(address previousAdmin, address newAdmin);

    modifier proxyCallIfNotAdmin() {
        if (msg.sender == _getAdmin() || msg.sender == address(0)) {
            _;
        } else {
            // This WILL halt the call frame on completion.
            _doProxyCall();
        }
    }

    constructor(address _admin) {
        _changeAdmin(_admin);
    }

    receive() external payable {
        // Proxy call by default.
        _doProxyCall();
    }

    fallback() external payable {
        // Proxy call by default.
        _doProxyCall();
    }

    function upgradeTo(address _implementation)
        public
        virtual
        returns (proxyCallIfNotAdmin)
    {
        _setImplementation(_implementation);
    }

    function upgradeToAndCall(address _implementation, bytes calldata _data)
        public
        payable virtual proxyCallIfNotAdmin
        returns (bytes memory)
    {
        _setImplementation(_implementation);
        (bool success, bytes memory returndata) = _implementation.delegatecall(_data);
        require(success, "Proxy: delegatecall to new implementation contract failed");
        return returndata;
    }

    function changeAdmin(address _admin) public virtual proxyCallIfNotAdmin {
        _changeAdmin(_admin);
    }

    function admin() public virtual proxyCallIfNotAdmin returns (address) {
        return _getAdmin();
    }

    function implementation()
        public
        virtual proxyCallIfNotAdmin
        returns (address)
    {
        return _getImplementation();
    }

    function _setImplementation(address _implementation) internal {
        bytes32 proxyImplementation = Constants.PROXY_IMPLEMENTATION_ADDRESS;
        assembly {
            sstore(proxyImplementation, _implementation)
        }
        emit Upgraded(_implementation);
    }

    function _changeAdmin(address _admin) internal {
        address previous = _getAdmin();
        bytes32 proxyOwner = Constants.PROXY_OWNER_ADDRESS;
        assembly {
            sstore(proxyOwner, _admin)
        }
        emit AdminChanged(previous, _admin);
    }

    function _doProxyCall() internal {
        address impl = _getImplementation();
        require(impl != address(0), "Proxy: implementation not initialized");
  
        assembly {
            // Copy calldata into memory at 0x0....calldatasize.
            calldatacopy(0x0, 0x0, calldatasize())
  
            // Perform the delegatecall, make sure to pass all available gas.
            let success := delegatecall(gas(), impl, 0x0, calldatasize(), 0x0, 0x0)
  
            // Copy returndata into memory at 0x0....returndatasize. Note that this *will*
            // overwrite the calldata that we just copied into memory but that doesn't really
            // matter because we'll be returning in a second anyway.
            returndatacopy(0x0, 0x0, returndatasize())
  
            // Success == 0 means a revert. We'll revert too and pass the data up.
            if iszero(success) { revert(0x0, returndatasize()) }
  
            // Otherwise we'll just return and pass the data up.
            return(0x0, returndatasize())
        }
    }

    function _getImplementation() internal view returns (address) {
        address impl;
        bytes32 proxyImplementation = Constants.PROXY_IMPLEMENTATION_ADDRESS;
        assembly {
            impl := sload(proxyImplementation)
        }
        return impl;
    }

    function _getAdmin() internal view returns (address) {
        address owner;
        bytes32 proxyOwner = Constants.PROXY_OWNER_ADDRESS;
        assembly {
            owner := sload(proxyOwner)
        }
        return owner;
    }
}

        
      
Deploy Script:
401B_DeploySystemConfigProxyScript.s.sol
        
          // SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {DeployScript} from "@redprint-deploy/deployer/DeployScript.sol";
import {DeployerFunctions, IDeployer} from "@redprint-deploy/deployer/DeployerFunctions.sol";
import {Proxy} from "@redprint-core/universal/Proxy.sol";

/// @custom:security-contact Consult full internal deploy script at https://github.com/Ratimon/redprint-forge
contract DeploySystemConfigProxyScript is DeployScript {
    using DeployerFunctions for IDeployer ;
    function deploy() external returns (Proxy) {
        address proxyOwner = deployer.mustGetAddress("ProxyAdmin");

        return Proxy(deployer.deploy_ERC1967Proxy("SystemConfigProxy", proxyOwner));
    }
}

        
      

After running the deploy script, the address deployed is saved at deployments/31337/.save.json. Otherwise, as specified in .env.<network>.local.

4.1C : Deploy L1StandardBridgeProxy Contract

In your terminal, copy below contracts' codes and run deployment scripts to your prefered network:

forge script script/401C_DeployL1StandardBridgeProxyScript.s.sol --trezor --sender <DEPLOYER_ADDRESS> --rpc-url <RPC_URL> --broadcast

(Optional), you can specify your derivation path:

--mnemonic-derivation-paths "m/44'/60'/0'/0/0"

Contract Settings

Smart Contract:
L1ChugSplashProxy.sol
        
          // SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {Constants} from "@redprint-core/libraries/Constants.sol";
import {IL1ChugSplashDeployer} from "@redprint-core/legacy/L1ChugSplashProxy.sol";

/// @custom:security-contact Consult full code at https://github.com/ethereum-optimism/optimism/blob/v1.9.4/packages/contracts-bedrock/src/legacy/L1ChugSplashProxy.sol
contract L1ChugSplashProxy {
    /// @notice "Magic" prefix. When prepended to some arbitrary bytecode and used to create a
    ///         contract, the appended bytecode will be deployed as given.
    bytes13 internal constant DEPLOY_CODE_PREFIX = 0x600D380380600D6000396000f3;
    

    modifier onlyWhenNotPaused() {
        address owner = _getOwner();

        // We do a low-level call because there's no guarantee that the owner actually *is* an
        // L1ChugSplashDeployer contract and Solidity will throw errors if we do a normal call and
        // it turns out that it isn't the right type of contract.
        (bool success, bytes memory returndata) =
            owner.staticcall(abi.encodeWithSelector(IL1ChugSplashDeployer.isUpgrading.selector));

        // If the call was unsuccessful then we assume that there's no "isUpgrading" method and we
        // can just continue as normal. We also expect that the return value is exactly 32 bytes
        // long. If this isn't the case then we can safely ignore the result.
        if (success && returndata.length == 32) {
            // Although the expected value is a *boolean*, it's safer to decode as a uint256 in the
            // case that the isUpgrading function returned something other than 0 or 1. But we only
            // really care about the case where this value is 0 (= false).
            uint256 ret = abi.decode(returndata, (uint256));
            require(ret == 0, "L1ChugSplashProxy: system is currently being upgraded");
        }

        _;
    }

    modifier proxyCallIfNotOwner() {
        if (msg.sender == _getOwner() || msg.sender == address(0)) {
            _;
        } else {
            // This WILL halt the call frame on completion.
            _doProxyCall();
        }
    }

    constructor(address _owner) {
        _setOwner(_owner);
    }

    receive() external payable {
        // Proxy call by default.
        _doProxyCall();
    }

    fallback() external payable {
        // Proxy call by default.
        _doProxyCall();
    }

    function setCode(bytes memory _code) external proxyCallIfNotOwner {
        // Get the code hash of the current implementation.
        address implementation = _getImplementation();

        // If the code hash matches the new implementation then we return early.
        if (keccak256(_code) == _getAccountCodeHash(implementation)) {
            return;
        }

        // Create the deploycode by appending the magic prefix.
        bytes memory deploycode = abi.encodePacked(DEPLOY_CODE_PREFIX, _code);

        // Deploy the code and set the new implementation address.
        address newImplementation;
        assembly {
            newImplementation := create(0x0, add(deploycode, 0x20), mload(deploycode))
        }

        // Check that the code was actually deployed correctly. I'm not sure if you can ever
        // actually fail this check. Should only happen if the contract creation from above runs
        // out of gas but this parent execution thread does NOT run out of gas. Seems like we
        // should be doing this check anyway though.
        require(
            _getAccountCodeHash(newImplementation) == keccak256(_code),
            "L1ChugSplashProxy: code was not correctly deployed"
        );

        _setImplementation(newImplementation);
    }

    function setStorage(bytes32 _key, bytes32 _value) external proxyCallIfNotOwner {
        assembly {
            sstore(_key, _value)
        }
    }

    function getOwner() public proxyCallIfNotOwner returns (address) {
        return _getOwner();
    }

    function getImplementation() external proxyCallIfNotOwner returns (address) {
        return _getImplementation();
    }

    function _setImplementation(address _implementation) internal {
        bytes32 proxyImplementation = Constants.PROXY_IMPLEMENTATION_ADDRESS;
        assembly {
            sstore(proxyImplementation, _implementation)
        }
    }

    function _setOwner(address _owner) internal {
        bytes32 proxyOwner = Constants.PROXY_OWNER_ADDRESS;
        assembly {
            sstore(proxyOwner, _owner)
        }
    }

    function _doProxyCall() internal onlyWhenNotPaused {
        address implementation = _getImplementation();

        require(implementation != address(0), "L1ChugSplashProxy: implementation is not set yet");

        assembly {
            // Copy calldata into memory at 0x0....calldatasize.
            calldatacopy(0x0, 0x0, calldatasize())

            // Perform the delegatecall, make sure to pass all available gas.
            let success := delegatecall(gas(), implementation, 0x0, calldatasize(), 0x0, 0x0)

            // Copy returndata into memory at 0x0....returndatasize. Note that this *will*
            // overwrite the calldata that we just copied into memory but that doesn't really
            // matter because we'll be returning in a second anyway.
            returndatacopy(0x0, 0x0, returndatasize())

            // Success == 0 means a revert. We'll revert too and pass the data up.
            if iszero(success) { revert(0x0, returndatasize()) }

            // Otherwise we'll just return and pass the data up.
            return(0x0, returndatasize())
        }
    }

    function _getImplementation() internal view returns (address) {
        address implementation;
        bytes32 proxyImplementation = Constants.PROXY_IMPLEMENTATION_ADDRESS;
        assembly {
            implementation := sload(proxyImplementation)
        }
        return implementation;
    }

    function _getOwner() internal view returns (address) {
        address owner;
        bytes32 proxyOwner = Constants.PROXY_OWNER_ADDRESS;
        assembly {
            owner := sload(proxyOwner)
        }
        return owner;
    }

    function _getAccountCodeHash(address _account)
        internal
        view
        returns (bytes32)
    {
        bytes32 codeHash;
        assembly {
            codeHash := extcodehash(_account)
        }
        return codeHash;
    }
}

        
      
Deploy Script:
401C_DeployL1StandardBridgeProxyScript.s.sol
        
          // SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {DeployScript} from "@redprint-deploy/deployer/DeployScript.sol";
import {DeployerFunctions, IDeployer} from "@redprint-deploy/deployer/DeployerFunctions.sol";
import {L1ChugSplashProxy} from "@redprint-core/legacy/L1ChugSplashProxy.sol";

/// @custom:security-contact Consult full internal deploy script at https://github.com/Ratimon/redprint-forge
contract DeployL1StandardBridgeProxyScript is DeployScript {
    using DeployerFunctions for IDeployer ;
    function deploy() external returns (L1ChugSplashProxy) {
        address proxyOwner = deployer.mustGetAddress("ProxyAdmin");

        return L1ChugSplashProxy(deployer.deploy_L1ChugSplashProxy("L1StandardBridgeProxy", proxyOwner ));
    }
}

        
      

After running the deploy script, the address deployed is saved at deployments/31337/.save.json. Otherwise, as specified in .env.<network>.local.

4.1D : Deploy L1CrossDomainMessengerProxy Contract

In your terminal, copy below contracts' codes and run deployment scripts to your prefered network:

forge script script/401D_DeployL1CrossDomainMessengerProxyScript.s.sol --trezor --sender <DEPLOYER_ADDRESS> --rpc-url <RPC_URL> --broadcast

(Optional), you can specify your derivation path:

--mnemonic-derivation-paths "m/44'/60'/0'/0/0"

Contract Settings

Smart Contract:
ResolvedDelegateProxy.sol
        
          // SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {AddressManager} from "@redprint-core/legacy/AddressManager.sol";

/// @custom:security-contact Consult full code at https://github.com/ethereum-optimism/optimism/blob/v1.9.4/packages/contracts-bedrock/src/legacy/ResolvedDelegateProxy.sol
contract ResolvedDelegateProxy {
    /// @notice Mapping used to store the implementation name that corresponds to this contract. A
    ///         mapping was originally used as a way to bypass the same issue normally solved by
    ///         storing the implementation address in a specific storage slot that does not conflict
    ///         with any other storage slot. Generally NOT a safe solution but works as long as the
    ///         implementation does not also keep a mapping in the first storage slot.
    mapping(address => string) private implementationName;
    /// @notice Mapping used to store the address of the AddressManager contract where the
    ///         implementation address will be resolved from. Same concept here as with the above
    ///         mapping. Also generally unsafe but fine if the implementation doesn't keep a mapping
    ///         in the second storage slot.
    mapping(address => AddressManager) private addressManager;

    constructor(AddressManager _addressManager, string memory _implementationName)
    {
        addressManager[address(this)] = _addressManager;
        implementationName[address(this)] = _implementationName;
    }

    fallback() external payable {
        address target = addressManager[address(this)].getAddress((implementationName[address(this)]));

        require(target != address(0), "ResolvedDelegateProxy: target address must be initialized");

        // slither-disable-next-line controlled-delegatecall
        (bool success, bytes memory returndata) = target.delegatecall(msg.data);

        if (success == true) {
            assembly {
                return(add(returndata, 0x20), mload(returndata))
            }
        } else {
            assembly {
                revert(add(returndata, 0x20), mload(returndata))
            }
        }
    }
}

        
      
Deploy Script:
401D_DeployL1CrossDomainMessengerProxyScript.s.sol
        
          // SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {AddressManager} from "@redprint-core/legacy/AddressManager.sol";
import {DeployScript} from "@redprint-deploy/deployer/DeployScript.sol";
import {DeployerFunctions, IDeployer} from "@redprint-deploy/deployer/DeployerFunctions.sol";
import {ResolvedDelegateProxy} from "@redprint-core/legacy/ResolvedDelegateProxy.sol";

/// @custom:security-contact Consult full internal deploy script at https://github.com/Ratimon/redprint-forge
contract DeployL1CrossDomainMessengerProxyScript is DeployScript {
    using DeployerFunctions for IDeployer ;
    function deploy() external returns (ResolvedDelegateProxy) {
        address addressManager = deployer.mustGetAddress("AddressManager");

        return ResolvedDelegateProxy(deployer.deploy_ResolvedDelegateProxy("L1CrossDomainMessengerProxy", addressManager, "OVM_L1CrossDomainMessenger" ));
    }
}

        
      

After running the deploy script, the address deployed is saved at deployments/31337/.save.json. Otherwise, as specified in .env.<network>.local.

4.1E : Deploy OptimismMintableERC20FactoryProxy Contract

In your terminal, copy below contracts' codes and run deployment scripts to your prefered network:

forge script script/401E_DeployOptimismMintableERC20FactoryProxyScript.s.sol --trezor --sender <DEPLOYER_ADDRESS> --rpc-url <RPC_URL> --broadcast

(Optional), you can specify your derivation path:

--mnemonic-derivation-paths "m/44'/60'/0'/0/0"

Contract Settings

Smart Contract:
Proxy.sol
        
          // SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {Constants} from "@redprint-core/libraries/Constants.sol";

/// @custom:security-contact Consult full code at https://github.com/ethereum-optimism/optimism/blob/v1.9.4/packages/contracts-bedrock/src/universal/Proxy.sol
contract Proxy {
    /**
     * @notice An event that is emitted each time the implementation is changed. This event is part
     *         of the EIP-1967 specification.
     *
     * @param implementation The address of the implementation contract
     */
    event Upgraded(address indexed implementation);
    /**
     * @notice An event that is emitted each time the owner is upgraded. This event is part of the
     *         EIP-1967 specification.
     *
     * @param previousAdmin The previous owner of the contract
     * @param newAdmin      The new owner of the contract
     */
    event AdminChanged(address previousAdmin, address newAdmin);

    modifier proxyCallIfNotAdmin() {
        if (msg.sender == _getAdmin() || msg.sender == address(0)) {
            _;
        } else {
            // This WILL halt the call frame on completion.
            _doProxyCall();
        }
    }

    constructor(address _admin) {
        _changeAdmin(_admin);
    }

    receive() external payable {
        // Proxy call by default.
        _doProxyCall();
    }

    fallback() external payable {
        // Proxy call by default.
        _doProxyCall();
    }

    function upgradeTo(address _implementation)
        public
        virtual
        returns (proxyCallIfNotAdmin)
    {
        _setImplementation(_implementation);
    }

    function upgradeToAndCall(address _implementation, bytes calldata _data)
        public
        payable virtual proxyCallIfNotAdmin
        returns (bytes memory)
    {
        _setImplementation(_implementation);
        (bool success, bytes memory returndata) = _implementation.delegatecall(_data);
        require(success, "Proxy: delegatecall to new implementation contract failed");
        return returndata;
    }

    function changeAdmin(address _admin) public virtual proxyCallIfNotAdmin {
        _changeAdmin(_admin);
    }

    function admin() public virtual proxyCallIfNotAdmin returns (address) {
        return _getAdmin();
    }

    function implementation()
        public
        virtual proxyCallIfNotAdmin
        returns (address)
    {
        return _getImplementation();
    }

    function _setImplementation(address _implementation) internal {
        bytes32 proxyImplementation = Constants.PROXY_IMPLEMENTATION_ADDRESS;
        assembly {
            sstore(proxyImplementation, _implementation)
        }
        emit Upgraded(_implementation);
    }

    function _changeAdmin(address _admin) internal {
        address previous = _getAdmin();
        bytes32 proxyOwner = Constants.PROXY_OWNER_ADDRESS;
        assembly {
            sstore(proxyOwner, _admin)
        }
        emit AdminChanged(previous, _admin);
    }

    function _doProxyCall() internal {
        address impl = _getImplementation();
        require(impl != address(0), "Proxy: implementation not initialized");
  
        assembly {
            // Copy calldata into memory at 0x0....calldatasize.
            calldatacopy(0x0, 0x0, calldatasize())
  
            // Perform the delegatecall, make sure to pass all available gas.
            let success := delegatecall(gas(), impl, 0x0, calldatasize(), 0x0, 0x0)
  
            // Copy returndata into memory at 0x0....returndatasize. Note that this *will*
            // overwrite the calldata that we just copied into memory but that doesn't really
            // matter because we'll be returning in a second anyway.
            returndatacopy(0x0, 0x0, returndatasize())
  
            // Success == 0 means a revert. We'll revert too and pass the data up.
            if iszero(success) { revert(0x0, returndatasize()) }
  
            // Otherwise we'll just return and pass the data up.
            return(0x0, returndatasize())
        }
    }

    function _getImplementation() internal view returns (address) {
        address impl;
        bytes32 proxyImplementation = Constants.PROXY_IMPLEMENTATION_ADDRESS;
        assembly {
            impl := sload(proxyImplementation)
        }
        return impl;
    }

    function _getAdmin() internal view returns (address) {
        address owner;
        bytes32 proxyOwner = Constants.PROXY_OWNER_ADDRESS;
        assembly {
            owner := sload(proxyOwner)
        }
        return owner;
    }
}

        
      
Deploy Script:
401E_DeployOptimismMintableERC20FactoryProxyScript.s.sol
        
          // SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {DeployScript} from "@redprint-deploy/deployer/DeployScript.sol";
import {DeployerFunctions, IDeployer} from "@redprint-deploy/deployer/DeployerFunctions.sol";
import {Proxy} from "@redprint-core/universal/Proxy.sol";

/// @custom:security-contact Consult full internal deploy script at https://github.com/Ratimon/redprint-forge
contract DeployOptimismMintableERC20FactoryProxyScript is DeployScript {
    using DeployerFunctions for IDeployer ;
    function deploy() external returns (Proxy) {
        address proxyOwner = deployer.mustGetAddress("ProxyAdmin");

        return Proxy(deployer.deploy_ERC1967Proxy("OptimismMintableERC20FactoryProxy", proxyOwner));
    }
}

        
      

After running the deploy script, the address deployed is saved at deployments/31337/.save.json. Otherwise, as specified in .env.<network>.local.

4.1F : Deploy L1ERC721BridgeProxy Contract

In your terminal, copy below contracts' codes and run deployment scripts to your prefered network:

forge script script/401F_DeployL1ERC721BridgeProxyScript.s.sol --trezor --sender <DEPLOYER_ADDRESS> --rpc-url <RPC_URL> --broadcast

(Optional), you can specify your derivation path:

--mnemonic-derivation-paths "m/44'/60'/0'/0/0"

Contract Settings

Smart Contract:
Proxy.sol
        
          // SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {Constants} from "@redprint-core/libraries/Constants.sol";

/// @custom:security-contact Consult full code at https://github.com/ethereum-optimism/optimism/blob/v1.9.4/packages/contracts-bedrock/src/universal/Proxy.sol
contract Proxy {
    /**
     * @notice An event that is emitted each time the implementation is changed. This event is part
     *         of the EIP-1967 specification.
     *
     * @param implementation The address of the implementation contract
     */
    event Upgraded(address indexed implementation);
    /**
     * @notice An event that is emitted each time the owner is upgraded. This event is part of the
     *         EIP-1967 specification.
     *
     * @param previousAdmin The previous owner of the contract
     * @param newAdmin      The new owner of the contract
     */
    event AdminChanged(address previousAdmin, address newAdmin);

    modifier proxyCallIfNotAdmin() {
        if (msg.sender == _getAdmin() || msg.sender == address(0)) {
            _;
        } else {
            // This WILL halt the call frame on completion.
            _doProxyCall();
        }
    }

    constructor(address _admin) {
        _changeAdmin(_admin);
    }

    receive() external payable {
        // Proxy call by default.
        _doProxyCall();
    }

    fallback() external payable {
        // Proxy call by default.
        _doProxyCall();
    }

    function upgradeTo(address _implementation)
        public
        virtual
        returns (proxyCallIfNotAdmin)
    {
        _setImplementation(_implementation);
    }

    function upgradeToAndCall(address _implementation, bytes calldata _data)
        public
        payable virtual proxyCallIfNotAdmin
        returns (bytes memory)
    {
        _setImplementation(_implementation);
        (bool success, bytes memory returndata) = _implementation.delegatecall(_data);
        require(success, "Proxy: delegatecall to new implementation contract failed");
        return returndata;
    }

    function changeAdmin(address _admin) public virtual proxyCallIfNotAdmin {
        _changeAdmin(_admin);
    }

    function admin() public virtual proxyCallIfNotAdmin returns (address) {
        return _getAdmin();
    }

    function implementation()
        public
        virtual proxyCallIfNotAdmin
        returns (address)
    {
        return _getImplementation();
    }

    function _setImplementation(address _implementation) internal {
        bytes32 proxyImplementation = Constants.PROXY_IMPLEMENTATION_ADDRESS;
        assembly {
            sstore(proxyImplementation, _implementation)
        }
        emit Upgraded(_implementation);
    }

    function _changeAdmin(address _admin) internal {
        address previous = _getAdmin();
        bytes32 proxyOwner = Constants.PROXY_OWNER_ADDRESS;
        assembly {
            sstore(proxyOwner, _admin)
        }
        emit AdminChanged(previous, _admin);
    }

    function _doProxyCall() internal {
        address impl = _getImplementation();
        require(impl != address(0), "Proxy: implementation not initialized");
  
        assembly {
            // Copy calldata into memory at 0x0....calldatasize.
            calldatacopy(0x0, 0x0, calldatasize())
  
            // Perform the delegatecall, make sure to pass all available gas.
            let success := delegatecall(gas(), impl, 0x0, calldatasize(), 0x0, 0x0)
  
            // Copy returndata into memory at 0x0....returndatasize. Note that this *will*
            // overwrite the calldata that we just copied into memory but that doesn't really
            // matter because we'll be returning in a second anyway.
            returndatacopy(0x0, 0x0, returndatasize())
  
            // Success == 0 means a revert. We'll revert too and pass the data up.
            if iszero(success) { revert(0x0, returndatasize()) }
  
            // Otherwise we'll just return and pass the data up.
            return(0x0, returndatasize())
        }
    }

    function _getImplementation() internal view returns (address) {
        address impl;
        bytes32 proxyImplementation = Constants.PROXY_IMPLEMENTATION_ADDRESS;
        assembly {
            impl := sload(proxyImplementation)
        }
        return impl;
    }

    function _getAdmin() internal view returns (address) {
        address owner;
        bytes32 proxyOwner = Constants.PROXY_OWNER_ADDRESS;
        assembly {
            owner := sload(proxyOwner)
        }
        return owner;
    }
}

        
      
Deploy Script:
401F_DeployL1ERC721BridgeProxyScript.s.sol
        
          // SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {DeployScript} from "@redprint-deploy/deployer/DeployScript.sol";
import {DeployerFunctions, IDeployer} from "@redprint-deploy/deployer/DeployerFunctions.sol";
import {Proxy} from "@redprint-core/universal/Proxy.sol";

/// @custom:security-contact Consult full internal deploy script at https://github.com/Ratimon/redprint-forge
contract DeployL1ERC721BridgeProxyScript is DeployScript {
    using DeployerFunctions for IDeployer ;
    function deploy() external returns (Proxy) {
        address proxyOwner = deployer.mustGetAddress("ProxyAdmin");

        return Proxy(deployer.deploy_ERC1967Proxy("L1ERC721BridgeProxy", proxyOwner));
    }
}

        
      

After running the deploy script, the address deployed is saved at deployments/31337/.save.json. Otherwise, as specified in .env.<network>.local.

4.1G : Deploy DisputeGameFactoryProxy Contract

In your terminal, copy below contracts' codes and run deployment scripts to your prefered network:

forge script script/401G_DeployDisputeGameFactoryProxyScript.s.sol --trezor --sender <DEPLOYER_ADDRESS> --rpc-url <RPC_URL> --broadcast

(Optional), you can specify your derivation path:

--mnemonic-derivation-paths "m/44'/60'/0'/0/0"

Contract Settings

Smart Contract:
Proxy.sol
        
          // SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {Constants} from "@redprint-core/libraries/Constants.sol";

/// @custom:security-contact Consult full code at https://github.com/ethereum-optimism/optimism/blob/4/packages/contracts-bedrock/src/universal/Proxy.sol
contract Proxy {
    /**
     * @notice An event that is emitted each time the implementation is changed. This event is part
     *         of the EIP-1967 specification.
     *
     * @param implementation The address of the implementation contract
     */
    event Upgraded(address indexed implementation);
    /**
     * @notice An event that is emitted each time the owner is upgraded. This event is part of the
     *         EIP-1967 specification.
     *
     * @param previousAdmin The previous owner of the contract
     * @param newAdmin      The new owner of the contract
     */
    event AdminChanged(address previousAdmin, address newAdmin);

    modifier proxyCallIfNotAdmin() {
        if (msg.sender == _getAdmin() || msg.sender == address(0)) {
            _;
        } else {
            // This WILL halt the call frame on completion.
            _doProxyCall();
        }
    }

    constructor(address _admin) {
        _changeAdmin(_admin);
    }

    receive() external payable {
        // Proxy call by default.
        _doProxyCall();
    }

    fallback() external payable {
        // Proxy call by default.
        _doProxyCall();
    }

    function upgradeTo(address _implementation)
        public
        virtual
        returns (proxyCallIfNotAdmin)
    {
        _setImplementation(_implementation);
    }

    function upgradeToAndCall(address _implementation, bytes calldata _data)
        public
        payable virtual proxyCallIfNotAdmin
        returns (bytes memory)
    {
        _setImplementation(_implementation);
        (bool success, bytes memory returndata) = _implementation.delegatecall(_data);
        require(success, "Proxy: delegatecall to new implementation contract failed");
        return returndata;
    }

    function changeAdmin(address _admin) public virtual proxyCallIfNotAdmin {
        _changeAdmin(_admin);
    }

    function admin() public virtual proxyCallIfNotAdmin returns (address) {
        return _getAdmin();
    }

    function implementation()
        public
        virtual proxyCallIfNotAdmin
        returns (address)
    {
        return _getImplementation();
    }

    function _setImplementation(address _implementation) internal {
        bytes32 proxyImplementation = Constants.PROXY_IMPLEMENTATION_ADDRESS;
        assembly {
            sstore(proxyImplementation, _implementation)
        }
        emit Upgraded(_implementation);
    }

    function _changeAdmin(address _admin) internal {
        address previous = _getAdmin();
        bytes32 proxyOwner = Constants.PROXY_OWNER_ADDRESS;
        assembly {
            sstore(proxyOwner, _admin)
        }
        emit AdminChanged(previous, _admin);
    }

    function _doProxyCall() internal {
        address impl = _getImplementation();
        require(impl != address(0), "Proxy: implementation not initialized");
  
        assembly {
            // Copy calldata into memory at 0x0....calldatasize.
            calldatacopy(0x0, 0x0, calldatasize())
  
            // Perform the delegatecall, make sure to pass all available gas.
            let success := delegatecall(gas(), impl, 0x0, calldatasize(), 0x0, 0x0)
  
            // Copy returndata into memory at 0x0....returndatasize. Note that this *will*
            // overwrite the calldata that we just copied into memory but that doesn't really
            // matter because we'll be returning in a second anyway.
            returndatacopy(0x0, 0x0, returndatasize())
  
            // Success == 0 means a revert. We'll revert too and pass the data up.
            if iszero(success) { revert(0x0, returndatasize()) }
  
            // Otherwise we'll just return and pass the data up.
            return(0x0, returndatasize())
        }
    }

    function _getImplementation() internal view returns (address) {
        address impl;
        bytes32 proxyImplementation = Constants.PROXY_IMPLEMENTATION_ADDRESS;
        assembly {
            impl := sload(proxyImplementation)
        }
        return impl;
    }

    function _getAdmin() internal view returns (address) {
        address owner;
        bytes32 proxyOwner = Constants.PROXY_OWNER_ADDRESS;
        assembly {
            owner := sload(proxyOwner)
        }
        return owner;
    }
}

        
      
Deploy Script:
401G_DeployDisputeGameFactoryProxyScript.s.sol
        
          // SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {DeployScript} from "@redprint-deploy/deployer/DeployScript.sol";
import {DeployerFunctions, IDeployer} from "@redprint-deploy/deployer/DeployerFunctions.sol";
import {Proxy} from "@redprint-core/universal/Proxy.sol";

/// @custom:security-contact Consult full internal deploy script at https://github.com/Ratimon/redprint-forge
contract DeployDisputeGameFactoryProxyScript is DeployScript {
    using DeployerFunctions for IDeployer ;
    function deploy() external returns (Proxy) {
        address proxyOwner = deployer.mustGetAddress("ProxyAdmin");

        return Proxy(deployer.deploy_ERC1967Proxy("DisputeGameFactoryProxy", proxyOwner));
    }
}

        
      

After running the deploy script, the address deployed is saved at deployments/31337/.save.json. Otherwise, as specified in .env.<network>.local.

4.1H : Deploy L2OutputOracleProxy Contract

In your terminal, copy below contracts' codes and run deployment scripts to your prefered network:

forge script script/401H_DeployL2OutputOracleProxyScript.s.sol --trezor --sender <DEPLOYER_ADDRESS> --rpc-url <RPC_URL> --broadcast

(Optional), you can specify your derivation path:

--mnemonic-derivation-paths "m/44'/60'/0'/0/0"

Contract Settings

Smart Contract:
Proxy.sol
        
          // SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {Constants} from "@redprint-core/libraries/Constants.sol";

/// @custom:security-contact Consult full code at https://github.com/ethereum-optimism/optimism/blob/v1.9.4/packages/contracts-bedrock/src/universal/Proxy.sol
contract Proxy {
    /**
     * @notice An event that is emitted each time the implementation is changed. This event is part
     *         of the EIP-1967 specification.
     *
     * @param implementation The address of the implementation contract
     */
    event Upgraded(address indexed implementation);
    /**
     * @notice An event that is emitted each time the owner is upgraded. This event is part of the
     *         EIP-1967 specification.
     *
     * @param previousAdmin The previous owner of the contract
     * @param newAdmin      The new owner of the contract
     */
    event AdminChanged(address previousAdmin, address newAdmin);

    modifier proxyCallIfNotAdmin() {
        if (msg.sender == _getAdmin() || msg.sender == address(0)) {
            _;
        } else {
            // This WILL halt the call frame on completion.
            _doProxyCall();
        }
    }

    constructor(address _admin) {
        _changeAdmin(_admin);
    }

    receive() external payable {
        // Proxy call by default.
        _doProxyCall();
    }

    fallback() external payable {
        // Proxy call by default.
        _doProxyCall();
    }

    function upgradeTo(address _implementation)
        public
        virtual
        returns (proxyCallIfNotAdmin)
    {
        _setImplementation(_implementation);
    }

    function upgradeToAndCall(address _implementation, bytes calldata _data)
        public
        payable virtual proxyCallIfNotAdmin
        returns (bytes memory)
    {
        _setImplementation(_implementation);
        (bool success, bytes memory returndata) = _implementation.delegatecall(_data);
        require(success, "Proxy: delegatecall to new implementation contract failed");
        return returndata;
    }

    function changeAdmin(address _admin) public virtual proxyCallIfNotAdmin {
        _changeAdmin(_admin);
    }

    function admin() public virtual proxyCallIfNotAdmin returns (address) {
        return _getAdmin();
    }

    function implementation()
        public
        virtual proxyCallIfNotAdmin
        returns (address)
    {
        return _getImplementation();
    }

    function _setImplementation(address _implementation) internal {
        bytes32 proxyImplementation = Constants.PROXY_IMPLEMENTATION_ADDRESS;
        assembly {
            sstore(proxyImplementation, _implementation)
        }
        emit Upgraded(_implementation);
    }

    function _changeAdmin(address _admin) internal {
        address previous = _getAdmin();
        bytes32 proxyOwner = Constants.PROXY_OWNER_ADDRESS;
        assembly {
            sstore(proxyOwner, _admin)
        }
        emit AdminChanged(previous, _admin);
    }

    function _doProxyCall() internal {
        address impl = _getImplementation();
        require(impl != address(0), "Proxy: implementation not initialized");
  
        assembly {
            // Copy calldata into memory at 0x0....calldatasize.
            calldatacopy(0x0, 0x0, calldatasize())
  
            // Perform the delegatecall, make sure to pass all available gas.
            let success := delegatecall(gas(), impl, 0x0, calldatasize(), 0x0, 0x0)
  
            // Copy returndata into memory at 0x0....returndatasize. Note that this *will*
            // overwrite the calldata that we just copied into memory but that doesn't really
            // matter because we'll be returning in a second anyway.
            returndatacopy(0x0, 0x0, returndatasize())
  
            // Success == 0 means a revert. We'll revert too and pass the data up.
            if iszero(success) { revert(0x0, returndatasize()) }
  
            // Otherwise we'll just return and pass the data up.
            return(0x0, returndatasize())
        }
    }

    function _getImplementation() internal view returns (address) {
        address impl;
        bytes32 proxyImplementation = Constants.PROXY_IMPLEMENTATION_ADDRESS;
        assembly {
            impl := sload(proxyImplementation)
        }
        return impl;
    }

    function _getAdmin() internal view returns (address) {
        address owner;
        bytes32 proxyOwner = Constants.PROXY_OWNER_ADDRESS;
        assembly {
            owner := sload(proxyOwner)
        }
        return owner;
    }
}

        
      
Deploy Script:
401H_DeployL2OutputOracleProxyScript.s.sol
        
          // SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {DeployScript} from "@redprint-deploy/deployer/DeployScript.sol";
import {DeployerFunctions, IDeployer} from "@redprint-deploy/deployer/DeployerFunctions.sol";
import {Proxy} from "@redprint-core/universal/Proxy.sol";

/// @custom:security-contact Consult full internal deploy script at https://github.com/Ratimon/redprint-forge
contract DeployL2OutputOracleProxyScript is DeployScript {
    using DeployerFunctions for IDeployer ;
    function deploy() external returns (Proxy) {
        address proxyOwner = deployer.mustGetAddress("ProxyAdmin");

        return Proxy(deployer.deploy_ERC1967Proxy("L2OutputOracleProxy", proxyOwner));
    }
}

        
      

After running the deploy script, the address deployed is saved at deployments/31337/.save.json. Otherwise, as specified in .env.<network>.local.

4.1I : Deploy DelayedWETHProxy Contract

In your terminal, copy below contracts' codes and run deployment scripts to your prefered network:

forge script script/401I_DeployDelayedWETHProxyScript.s.sol --trezor --sender <DEPLOYER_ADDRESS> --rpc-url <RPC_URL> --broadcast

(Optional), you can specify your derivation path:

--mnemonic-derivation-paths "m/44'/60'/0'/0/0"

Contract Settings

Smart Contract:
Proxy.sol
        
          // SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {Constants} from "@redprint-core/libraries/Constants.sol";

/// @custom:security-contact Consult full code at https://github.com/ethereum-optimism/optimism/blob/4/packages/contracts-bedrock/src/universal/Proxy.sol
contract Proxy {
    /**
     * @notice An event that is emitted each time the implementation is changed. This event is part
     *         of the EIP-1967 specification.
     *
     * @param implementation The address of the implementation contract
     */
    event Upgraded(address indexed implementation);
    /**
     * @notice An event that is emitted each time the owner is upgraded. This event is part of the
     *         EIP-1967 specification.
     *
     * @param previousAdmin The previous owner of the contract
     * @param newAdmin      The new owner of the contract
     */
    event AdminChanged(address previousAdmin, address newAdmin);

    modifier proxyCallIfNotAdmin() {
        if (msg.sender == _getAdmin() || msg.sender == address(0)) {
            _;
        } else {
            // This WILL halt the call frame on completion.
            _doProxyCall();
        }
    }

    constructor(address _admin) {
        _changeAdmin(_admin);
    }

    receive() external payable {
        // Proxy call by default.
        _doProxyCall();
    }

    fallback() external payable {
        // Proxy call by default.
        _doProxyCall();
    }

    function upgradeTo(address _implementation)
        public
        virtual
        returns (proxyCallIfNotAdmin)
    {
        _setImplementation(_implementation);
    }

    function upgradeToAndCall(address _implementation, bytes calldata _data)
        public
        payable virtual proxyCallIfNotAdmin
        returns (bytes memory)
    {
        _setImplementation(_implementation);
        (bool success, bytes memory returndata) = _implementation.delegatecall(_data);
        require(success, "Proxy: delegatecall to new implementation contract failed");
        return returndata;
    }

    function changeAdmin(address _admin) public virtual proxyCallIfNotAdmin {
        _changeAdmin(_admin);
    }

    function admin() public virtual proxyCallIfNotAdmin returns (address) {
        return _getAdmin();
    }

    function implementation()
        public
        virtual proxyCallIfNotAdmin
        returns (address)
    {
        return _getImplementation();
    }

    function _setImplementation(address _implementation) internal {
        bytes32 proxyImplementation = Constants.PROXY_IMPLEMENTATION_ADDRESS;
        assembly {
            sstore(proxyImplementation, _implementation)
        }
        emit Upgraded(_implementation);
    }

    function _changeAdmin(address _admin) internal {
        address previous = _getAdmin();
        bytes32 proxyOwner = Constants.PROXY_OWNER_ADDRESS;
        assembly {
            sstore(proxyOwner, _admin)
        }
        emit AdminChanged(previous, _admin);
    }

    function _doProxyCall() internal {
        address impl = _getImplementation();
        require(impl != address(0), "Proxy: implementation not initialized");
  
        assembly {
            // Copy calldata into memory at 0x0....calldatasize.
            calldatacopy(0x0, 0x0, calldatasize())
  
            // Perform the delegatecall, make sure to pass all available gas.
            let success := delegatecall(gas(), impl, 0x0, calldatasize(), 0x0, 0x0)
  
            // Copy returndata into memory at 0x0....returndatasize. Note that this *will*
            // overwrite the calldata that we just copied into memory but that doesn't really
            // matter because we'll be returning in a second anyway.
            returndatacopy(0x0, 0x0, returndatasize())
  
            // Success == 0 means a revert. We'll revert too and pass the data up.
            if iszero(success) { revert(0x0, returndatasize()) }
  
            // Otherwise we'll just return and pass the data up.
            return(0x0, returndatasize())
        }
    }

    function _getImplementation() internal view returns (address) {
        address impl;
        bytes32 proxyImplementation = Constants.PROXY_IMPLEMENTATION_ADDRESS;
        assembly {
            impl := sload(proxyImplementation)
        }
        return impl;
    }

    function _getAdmin() internal view returns (address) {
        address owner;
        bytes32 proxyOwner = Constants.PROXY_OWNER_ADDRESS;
        assembly {
            owner := sload(proxyOwner)
        }
        return owner;
    }
}

        
      
Deploy Script:
401I_DeployDelayedWETHProxyScript.s.sol
        
          // SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {DeployScript} from "@redprint-deploy/deployer/DeployScript.sol";
import {DeployerFunctions, IDeployer} from "@redprint-deploy/deployer/DeployerFunctions.sol";
import {Proxy} from "@redprint-core/universal/Proxy.sol";

/// @custom:security-contact Consult full internal deploy script at https://github.com/Ratimon/redprint-forge
contract DeployDelayedWETHProxyScript is DeployScript {
    using DeployerFunctions for IDeployer ;
    function deploy() external returns (Proxy) {
        address proxyOwner = deployer.mustGetAddress("ProxyAdmin");

        return Proxy(deployer.deploy_ERC1967Proxy("DelayedWETHProxy", proxyOwner));
    }
}

        
      

After running the deploy script, the address deployed is saved at deployments/31337/.save.json. Otherwise, as specified in .env.<network>.local.

4.1J : Deploy PermissionedDelayedWETHProxy Contract

In your terminal, copy below contracts' codes and run deployment scripts to your prefered network:

forge script script/401J_DeployPermissionedDelayedWETHProxyScript.s.sol --trezor --sender <DEPLOYER_ADDRESS> --rpc-url <RPC_URL> --broadcast

(Optional), you can specify your derivation path:

--mnemonic-derivation-paths "m/44'/60'/0'/0/0"

Contract Settings

Smart Contract:
Proxy.sol
        
          // SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {Constants} from "@redprint-core/libraries/Constants.sol";

/// @custom:security-contact Consult full code at https://github.com/ethereum-optimism/optimism/blob/v1.9.4/packages/contracts-bedrock/src/universal/Proxy.sol
contract Proxy {
    /**
     * @notice An event that is emitted each time the implementation is changed. This event is part
     *         of the EIP-1967 specification.
     *
     * @param implementation The address of the implementation contract
     */
    event Upgraded(address indexed implementation);
    /**
     * @notice An event that is emitted each time the owner is upgraded. This event is part of the
     *         EIP-1967 specification.
     *
     * @param previousAdmin The previous owner of the contract
     * @param newAdmin      The new owner of the contract
     */
    event AdminChanged(address previousAdmin, address newAdmin);

    modifier proxyCallIfNotAdmin() {
        if (msg.sender == _getAdmin() || msg.sender == address(0)) {
            _;
        } else {
            // This WILL halt the call frame on completion.
            _doProxyCall();
        }
    }

    constructor(address _admin) {
        _changeAdmin(_admin);
    }

    receive() external payable {
        // Proxy call by default.
        _doProxyCall();
    }

    fallback() external payable {
        // Proxy call by default.
        _doProxyCall();
    }

    function upgradeTo(address _implementation)
        public
        virtual
        returns (proxyCallIfNotAdmin)
    {
        _setImplementation(_implementation);
    }

    function upgradeToAndCall(address _implementation, bytes calldata _data)
        public
        payable virtual proxyCallIfNotAdmin
        returns (bytes memory)
    {
        _setImplementation(_implementation);
        (bool success, bytes memory returndata) = _implementation.delegatecall(_data);
        require(success, "Proxy: delegatecall to new implementation contract failed");
        return returndata;
    }

    function changeAdmin(address _admin) public virtual proxyCallIfNotAdmin {
        _changeAdmin(_admin);
    }

    function admin() public virtual proxyCallIfNotAdmin returns (address) {
        return _getAdmin();
    }

    function implementation()
        public
        virtual proxyCallIfNotAdmin
        returns (address)
    {
        return _getImplementation();
    }

    function _setImplementation(address _implementation) internal {
        bytes32 proxyImplementation = Constants.PROXY_IMPLEMENTATION_ADDRESS;
        assembly {
            sstore(proxyImplementation, _implementation)
        }
        emit Upgraded(_implementation);
    }

    function _changeAdmin(address _admin) internal {
        address previous = _getAdmin();
        bytes32 proxyOwner = Constants.PROXY_OWNER_ADDRESS;
        assembly {
            sstore(proxyOwner, _admin)
        }
        emit AdminChanged(previous, _admin);
    }

    function _doProxyCall() internal {
        address impl = _getImplementation();
        require(impl != address(0), "Proxy: implementation not initialized");
  
        assembly {
            // Copy calldata into memory at 0x0....calldatasize.
            calldatacopy(0x0, 0x0, calldatasize())
  
            // Perform the delegatecall, make sure to pass all available gas.
            let success := delegatecall(gas(), impl, 0x0, calldatasize(), 0x0, 0x0)
  
            // Copy returndata into memory at 0x0....returndatasize. Note that this *will*
            // overwrite the calldata that we just copied into memory but that doesn't really
            // matter because we'll be returning in a second anyway.
            returndatacopy(0x0, 0x0, returndatasize())
  
            // Success == 0 means a revert. We'll revert too and pass the data up.
            if iszero(success) { revert(0x0, returndatasize()) }
  
            // Otherwise we'll just return and pass the data up.
            return(0x0, returndatasize())
        }
    }

    function _getImplementation() internal view returns (address) {
        address impl;
        bytes32 proxyImplementation = Constants.PROXY_IMPLEMENTATION_ADDRESS;
        assembly {
            impl := sload(proxyImplementation)
        }
        return impl;
    }

    function _getAdmin() internal view returns (address) {
        address owner;
        bytes32 proxyOwner = Constants.PROXY_OWNER_ADDRESS;
        assembly {
            owner := sload(proxyOwner)
        }
        return owner;
    }
}

        
      
Deploy Script:
401J_DeployPermissionedDelayedWETHProxyScript.s.sol
        
          // SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {DeployScript} from "@redprint-deploy/deployer/DeployScript.sol";
import {DeployerFunctions, IDeployer} from "@redprint-deploy/deployer/DeployerFunctions.sol";
import {Proxy} from "@redprint-core/universal/Proxy.sol";

/// @custom:security-contact Consult full internal deploy script at https://github.com/Ratimon/redprint-forge
contract DeployPermissionedDelayedWETHProxyScript is DeployScript {
    using DeployerFunctions for IDeployer ;
    function deploy() external returns (Proxy) {
        address proxyOwner = deployer.mustGetAddress("ProxyAdmin");

        return Proxy(deployer.deploy_ERC1967Proxy("PermissionedDelayedWETHProxy", proxyOwner));
    }
}

        
      

After running the deploy script, the address deployed is saved at deployments/31337/.save.json. Otherwise, as specified in .env.<network>.local.

4.1K : Deploy AnchorStateRegistryProxy Contract

In your terminal, copy below contracts' codes and run deployment scripts to your prefered network:

forge script script/401K_DeployAnchorStateRegistryProxyScript.s.sol --trezor --sender <DEPLOYER_ADDRESS> --rpc-url <RPC_URL> --broadcast

(Optional), you can specify your derivation path:

--mnemonic-derivation-paths "m/44'/60'/0'/0/0"

Contract Settings

Smart Contract:
Proxy.sol
        
          // SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {Constants} from "@redprint-core/libraries/Constants.sol";

/// @custom:security-contact Consult full code at https://github.com/ethereum-optimism/optimism/blob/v1.9.4/packages/contracts-bedrock/src/universal/Proxy.sol
contract Proxy {
    /**
     * @notice An event that is emitted each time the implementation is changed. This event is part
     *         of the EIP-1967 specification.
     *
     * @param implementation The address of the implementation contract
     */
    event Upgraded(address indexed implementation);
    /**
     * @notice An event that is emitted each time the owner is upgraded. This event is part of the
     *         EIP-1967 specification.
     *
     * @param previousAdmin The previous owner of the contract
     * @param newAdmin      The new owner of the contract
     */
    event AdminChanged(address previousAdmin, address newAdmin);

    modifier proxyCallIfNotAdmin() {
        if (msg.sender == _getAdmin() || msg.sender == address(0)) {
            _;
        } else {
            // This WILL halt the call frame on completion.
            _doProxyCall();
        }
    }

    constructor(address _admin) {
        _changeAdmin(_admin);
    }

    receive() external payable {
        // Proxy call by default.
        _doProxyCall();
    }

    fallback() external payable {
        // Proxy call by default.
        _doProxyCall();
    }

    function upgradeTo(address _implementation)
        public
        virtual
        returns (proxyCallIfNotAdmin)
    {
        _setImplementation(_implementation);
    }

    function upgradeToAndCall(address _implementation, bytes calldata _data)
        public
        payable virtual proxyCallIfNotAdmin
        returns (bytes memory)
    {
        _setImplementation(_implementation);
        (bool success, bytes memory returndata) = _implementation.delegatecall(_data);
        require(success, "Proxy: delegatecall to new implementation contract failed");
        return returndata;
    }

    function changeAdmin(address _admin) public virtual proxyCallIfNotAdmin {
        _changeAdmin(_admin);
    }

    function admin() public virtual proxyCallIfNotAdmin returns (address) {
        return _getAdmin();
    }

    function implementation()
        public
        virtual proxyCallIfNotAdmin
        returns (address)
    {
        return _getImplementation();
    }

    function _setImplementation(address _implementation) internal {
        bytes32 proxyImplementation = Constants.PROXY_IMPLEMENTATION_ADDRESS;
        assembly {
            sstore(proxyImplementation, _implementation)
        }
        emit Upgraded(_implementation);
    }

    function _changeAdmin(address _admin) internal {
        address previous = _getAdmin();
        bytes32 proxyOwner = Constants.PROXY_OWNER_ADDRESS;
        assembly {
            sstore(proxyOwner, _admin)
        }
        emit AdminChanged(previous, _admin);
    }

    function _doProxyCall() internal {
        address impl = _getImplementation();
        require(impl != address(0), "Proxy: implementation not initialized");
  
        assembly {
            // Copy calldata into memory at 0x0....calldatasize.
            calldatacopy(0x0, 0x0, calldatasize())
  
            // Perform the delegatecall, make sure to pass all available gas.
            let success := delegatecall(gas(), impl, 0x0, calldatasize(), 0x0, 0x0)
  
            // Copy returndata into memory at 0x0....returndatasize. Note that this *will*
            // overwrite the calldata that we just copied into memory but that doesn't really
            // matter because we'll be returning in a second anyway.
            returndatacopy(0x0, 0x0, returndatasize())
  
            // Success == 0 means a revert. We'll revert too and pass the data up.
            if iszero(success) { revert(0x0, returndatasize()) }
  
            // Otherwise we'll just return and pass the data up.
            return(0x0, returndatasize())
        }
    }

    function _getImplementation() internal view returns (address) {
        address impl;
        bytes32 proxyImplementation = Constants.PROXY_IMPLEMENTATION_ADDRESS;
        assembly {
            impl := sload(proxyImplementation)
        }
        return impl;
    }

    function _getAdmin() internal view returns (address) {
        address owner;
        bytes32 proxyOwner = Constants.PROXY_OWNER_ADDRESS;
        assembly {
            owner := sload(proxyOwner)
        }
        return owner;
    }
}

        
      
Deploy Script:
401K_DeployAnchorStateRegistryProxyScript.s.sol
        
          // SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {DeployScript} from "@redprint-deploy/deployer/DeployScript.sol";
import {DeployerFunctions, IDeployer} from "@redprint-deploy/deployer/DeployerFunctions.sol";
import {Proxy} from "@redprint-core/universal/Proxy.sol";

/// @custom:security-contact Consult full internal deploy script at https://github.com/Ratimon/redprint-forge
contract DeployAnchorStateRegistryProxyScript is DeployScript {
    using DeployerFunctions for IDeployer ;
    function deploy() external returns (Proxy) {
        address proxyOwner = deployer.mustGetAddress("ProxyAdmin");

        return Proxy(deployer.deploy_ERC1967Proxy("AnchorStateRegistryProxy", proxyOwner));
    }
}

        
      

After running the deploy script, the address deployed is saved at deployments/31337/.save.json. Otherwise, as specified in .env.<network>.local.

4.1L : Transfer AddressManager Ownership

In your terminal, copy below contracts' codes and run deployment scripts to your prefered network:

forge script script/401L_TransferAddressManagerOwnershipScript.s.sol --trezor --sender <DEPLOYER_ADDRESS> --rpc-url <RPC_URL> --broadcast

(Optional), you can specify your derivation path:

--mnemonic-derivation-paths "m/44'/60'/0'/0/0"

Contract Settings

Deploy Script:
401L_TransferAddressManagerOwnershipScript.s.sol
        
          // SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {AddressManager} from "@redprint-core/legacy/AddressManager.sol";
import {IDeployer, getDeployer} from "@redprint-deploy/deployer/DeployScript.sol";
import {Script} from "@redprint-forge-std/Script.sol";
import {Vm, VmSafe} from "@redprint-forge-std/Vm.sol";
import {console} from "@redprint-forge-std/console.sol";

contract TransferAddressManagerOwnershipScript is Script {
    IDeployer deployerProcedue;

    function run() public {
        deployerProcedue = getDeployer();
      deployerProcedue.setAutoSave(true);

      console.log("Transferring AddressManager ownership to ProxyAdmin");
      AddressManager addressManager = AddressManager(deployerProcedue.mustGetAddress("AddressManager"));
      address owner = addressManager.owner();
      address proxyAdmin = deployerProcedue.mustGetAddress("ProxyAdmin");
      (VmSafe.CallerMode mode ,address msgSender, ) = vm.readCallers();

      if (owner != proxyAdmin) {

          if(mode != VmSafe.CallerMode.Broadcast && msgSender != owner) {
              console.log("Pranking owner ...");
              vm.prank(owner);
            } else {
              console.log("Broadcasting ...");
              vm.broadcast(owner);
            }

          addressManager.transferOwnership(proxyAdmin);
          console.log("AddressManager ownership transferred to %s", proxyAdmin);
      }

      require(addressManager.owner() == proxyAdmin);
    }
}