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
{
"SafeProxyFactory": "<ADDRESS_1>",
"SafeSingleton": "<ADDRESS_2>",
"SystemOwnerSafe": "<ADDRESS_3>"
}
Without this artifact file, the next deployment scripts can not be run.
Note: This script is not completed yet. It is for one layer only. Check our final script for full deployment.
forge script script/000_DeployAllScript.s.sol --trezor --sender <DEPLOYER_ADDRESS> --rpc-url <RPC_URL> --broadcast
--mnemonic-derivation-paths "m/44'/60'/0'/0/0"
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {DeploySafeProxyScript} from "@scripts/101_DeploySafeProxyScript.s.sol";
import {Script} from "@redprint-forge-std/Script.sol";
import {SetupSuperchainScript} from "@scripts/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();
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {DeployAddressManagerScript} from "@scripts/201A_DeployAddressManagerScript.s.sol";
import {DeployAndInitializeProtocolVersionsScript} from "@scripts/203B_DeployAndInitializeProtocolVersionsScript.s.sol";
import {DeployAndInitializeSuperchainConfigScript} from "@scripts/202B_DeployAndInitializeSuperchainConfigScript.s.sol";
import {DeployAndSetupProxyAdminScript} from "@scripts/201B_DeployAndSetupProxyAdminScript.s.sol";
import {DeployProtocolVersionsProxyScript} from "@scripts/203A_DeployProtocolVersionsProxyScript.s.sol";
import {DeploySuperchainConfigProxyScript} from "@scripts/202A_DeploySuperchainConfigProxyScript.s.sol";
import {IDeployer, getDeployer} from "@redprint-deploy/deployer/DeployScript.sol";
import {Script} from "@redprint-forge-std/Script.sol";
import {console} from "@redprint-forge-std/console.sol";
contract SetupSuperchainScript is Script {
IDeployer deployerProcedue;
function run() public {
deployerProcedue = getDeployer();
deployerProcedue.setAutoSave(true);
console.log("Setup Superchain ... ");
DeployAddressManagerScript addressManagerDeployments = new DeployAddressManagerScript();
DeployAndSetupProxyAdminScript proxyAdminDeployments = new DeployAndSetupProxyAdminScript();
DeploySuperchainConfigProxyScript superchainConfigProxyDeployments = new DeploySuperchainConfigProxyScript();
DeployAndInitializeSuperchainConfigScript superchainConfigDeployments = new DeployAndInitializeSuperchainConfigScript();
DeployProtocolVersionsProxyScript protocolVersionsProxyDeployments = new DeployProtocolVersionsProxyScript();
DeployAndInitializeProtocolVersionsScript protocolVersionsDeployments = new DeployAndInitializeProtocolVersionsScript();
// Deploy a new ProxyAdmin and AddressManager
addressManagerDeployments.deploy();
proxyAdminDeployments.deploy();
proxyAdminDeployments.initialize();
// Deploy the SuperchainConfigProxy
superchainConfigProxyDeployments.deploy();
superchainConfigDeployments.deploy();
superchainConfigDeployments.initialize();
// Deploy the ProtocolVersionsProxy
protocolVersionsProxyDeployments.deploy();
protocolVersionsDeployments.deploy();
protocolVersionsDeployments.initialize();
console.log("AddressManager at: ", deployerProcedue.getAddress("AddressManager"));
console.log("ProxyAdmin at: ", deployerProcedue.getAddress("ProxyAdmin"));
console.log("SuperchainConfigProxy at: ", deployerProcedue.getAddress("SuperchainConfigProxy"));
console.log("SuperchainConfig at: ", deployerProcedue.getAddress("SuperchainConfig"));
console.log("ProtocolVersionsProxy at: ", deployerProcedue.getAddress("ProtocolVersionsProxy"));
}
}
After running the deploy script, the address deployed is saved at deployments/31337/.save.json. Otherwise, as specified in .env.<network>.local.
Your saved address will be different.
You can change DEPLOYMENT_OUTFILE=deployments/31337/.save.json to reflect yours!
{
"SafeProxyFactory": "<ADDRESS_1>",
"SafeSingleton": "<ADDRESS_2>",
"SystemOwnerSafe": "<ADDRESS_3>",
"AddressManager": "<ADDRESS_4>",
"ProxyAdmin": "<ADDRESS_5>",
"SuperchainConfigProxy": "<ADDRESS_6>",
"SuperchainConfig": "<ADDRESS_7>",
"ProtocolVersionsProxy": "<ADDRESS_8>",
"ProtocolVersions": "<ADDRESS_9>"
}
click on ✕ button to close
2.1A : Deploy AddressManager Contract
forge script script/201A_DeployAddressManagerScript.s.sol --trezor --sender <DEPLOYER_ADDRESS> --rpc-url <RPC_URL> --broadcast
--mnemonic-derivation-paths "m/44'/60'/0'/0/0"
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {Ownable} from "@redprint-openzeppelin/access/Ownable.sol";
/// @custom:security-contact Consult full code at https://github.com/ethereum-optimism/optimism/blob/v1.9.4/packages/contracts-bedrock/src/legacy/AddressManager.sol
contract AddressManager is Ownable {
mapping(bytes32 => address) private addresses;
event AddressSet(string indexed name, address newAddress, address oldAddress);
function setAddress(string memory _name, address _address) external onlyOwner {
bytes32 nameHash = _getNameHash(_name);
address oldAddress = addresses[nameHash];
addresses[nameHash] = _address;
emit AddressSet(_name, _address, oldAddress);
}
function getAddress(string memory _name) external view returns (address) {
return addresses[_getNameHash(_name)];
}
function _getNameHash(string memory _name) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(_name));
}
}
// 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";
/// @custom:security-contact Consult full internal deploy script at https://github.com/Ratimon/redprint-forge
contract DeployAddressManagerScript is DeployScript {
using DeployerFunctions for IDeployer ;
function deploy() external returns (AddressManager) {
return AddressManager(deployer.deploy_AddressManager("AddressManager"));
}
}
After running the deploy script, the address deployed is saved at deployments/31337/.save.json. Otherwise, as specified in .env.<network>.local.
Your saved address will be different.
You can change DEPLOYMENT_OUTFILE=deployments/31337/.save.json to reflect yours!
{
"SafeProxyFactory": "<ADDRESS_1>",
"SafeSingleton": "<ADDRESS_2>",
"SystemOwnerSafe": "<ADDRESS_3>",
"AddressManager": "<ADDRESS_4>",
}
click on ✕ button to close
2.1B : Deploy And Setup ProxyAdmin Contract
forge script script/201B_DeployAndSetupProxyAdminScript.s.sol --trezor --sender <DEPLOYER_ADDRESS> --rpc-url <RPC_URL> --broadcast
--mnemonic-derivation-paths "m/44'/60'/0'/0/0"
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {Constants} from "@redprint-core/libraries/Constants.sol";
import {IAddressManager} from "@redprint-core/legacy/interfaces/IAddressManager.sol";
import {IL1ChugSplashProxy, IStaticL1ChugSplashProxy} from "@redprint-core/legacy/interfaces/IL1ChugSplashProxy.sol";
import {IProxy} from "@redprint-core/universal/interfaces/IProxy.sol";
import {IStaticERC1967Proxy} from "@redprint-core/universal/interfaces/IStaticERC1967Proxy.sol";
import {Ownable} from "@redprint-openzeppelin/access/Ownable.sol";
/// @custom:security-contact Consult full code at https://github.com/ethereum-optimism/optimism/blob/v1.9.4/packages/contracts-bedrock/src/universal/ProxyAdmin.sol
contract ProxyAdmin is Ownable {
enum ProxyType {
ERC1967,
CHUGSPLASH,
RESOLVED
}
/// @notice A mapping of proxy types, used for backwards compatibility.
mapping(address => ProxyType) public proxyType;
/// @notice A reverse mapping of addresses to names held in the AddressManager. This must be
/// manually kept up to date with changes in the AddressManager for this contract
/// to be able to work as an admin for the ResolvedDelegateProxy type.
mapping(address => string) public implementationName;
/// @notice The address of the address manager, this is required to manage the
/// ResolvedDelegateProxy type.
IAddressManager public addressManager;
bool internal upgrading;
constructor(address _owner) Ownable() {
_transferOwnership(_owner);
}
function setProxyType(address _address, ProxyType _type) external onlyOwner {
proxyType[_address] = _type;
}
function setImplementationName(address _address, string memory _name)
external
onlyOwner
{
implementationName[_address] = _name;
}
function setAddressManager(IAddressManager _address) external onlyOwner {
addressManager = _address;
}
function setAddress(string memory _name, address _address) external onlyOwner {
addressManager.setAddress(_name, _address);
}
function setUpgrading(bool _upgrading) external onlyOwner {
upgrading = _upgrading;
}
function isUpgrading() external view returns (bool) {
return upgrading;
}
function getProxyImplementation(address _proxy)
external
view
returns (address)
{
ProxyType ptype = proxyType[_proxy];
if (ptype == ProxyType.ERC1967) {
return IStaticERC1967Proxy(_proxy).implementation();
} else if (ptype == ProxyType.CHUGSPLASH) {
return IStaticL1ChugSplashProxy(_proxy).getImplementation();
} else if (ptype == ProxyType.RESOLVED) {
return addressManager.getAddress(implementationName[_proxy]);
} else {
revert("ProxyAdmin: unknown proxy type");
}
}
function getProxyAdmin(address payable _proxy)
external
view
returns (address)
{
ProxyType ptype = proxyType[_proxy];
if (ptype == ProxyType.ERC1967) {
return IStaticERC1967Proxy(_proxy).admin();
} else if (ptype == ProxyType.CHUGSPLASH) {
return IStaticL1ChugSplashProxy(_proxy).getOwner();
} else if (ptype == ProxyType.RESOLVED) {
return addressManager.owner();
} else {
revert("ProxyAdmin: unknown proxy type");
}
}
function changeProxyAdmin(address payable _proxy, address _newAdmin)
external
onlyOwner
{
ProxyType ptype = proxyType[_proxy];
if (ptype == ProxyType.ERC1967) {
IProxy(_proxy).changeAdmin(_newAdmin);
} else if (ptype == ProxyType.CHUGSPLASH) {
IL1ChugSplashProxy(_proxy).setOwner(_newAdmin);
} else if (ptype == ProxyType.RESOLVED) {
addressManager.transferOwnership(_newAdmin);
} else {
revert("ProxyAdmin: unknown proxy type");
}
}
function upgrade(address payable _proxy, address _implementation)
public
onlyOwner
{
ProxyType ptype = proxyType[_proxy];
if (ptype == ProxyType.ERC1967) {
IProxy(_proxy).upgradeTo(_implementation);
} else if (ptype == ProxyType.CHUGSPLASH) {
IL1ChugSplashProxy(_proxy).setStorage(
Constants.PROXY_IMPLEMENTATION_ADDRESS, bytes32(uint256(uint160(_implementation)))
);
} else if (ptype == ProxyType.RESOLVED) {
string memory name = implementationName[_proxy];
addressManager.setAddress(name, _implementation);
} else {
// It should not be possible to retrieve a ProxyType value which is not matched by
// one of the previous conditions.
assert(false);
}
}
function upgradeAndCall(address payable _proxy, address _implementation, bytes memory _data)
external payable
onlyOwner
{
ProxyType ptype = proxyType[_proxy];
if (ptype == ProxyType.ERC1967) {
IProxy(_proxy).upgradeToAndCall{ value: msg.value }(_implementation, _data);
} else {
// reverts if proxy type is unknown
upgrade(_proxy, _implementation);
(bool success,) = _proxy.call{ value: msg.value }(_data);
require(success, "ProxyAdmin: call to proxy after upgrade failed");
}
}
}
// 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 {IAddressManager} from "@redprint-core/legacy/interfaces/IAddressManager.sol";
import {ProxyAdmin} from "@redprint-core/universal/ProxyAdmin.sol";
import {Vm, VmSafe} from "@redprint-forge-std/Vm.sol";
import {console} from "@redprint-forge-std/console.sol";
/// @custom:security-contact Consult full internal deploy script at https://github.com/Ratimon/redprint-forge
contract DeployAndSetupProxyAdminScript is DeployScript {
using DeployerFunctions for IDeployer ;
ProxyAdmin proxyAdmin;
string mnemonic = vm.envString("MNEMONIC");
uint256 ownerPrivateKey = vm.deriveKey(mnemonic, "m/44'/60'/0'/0/", 1);
address owner = vm.envOr("DEPLOYER_ADDRESS", vm.addr(ownerPrivateKey));
function deploy() external returns (ProxyAdmin) {
proxyAdmin = deployer.deploy_ProxyAdmin("ProxyAdmin", address(owner));
require(proxyAdmin.owner() == address(owner));
return proxyAdmin;
}
function initialize() external {
AddressManager addressManager = AddressManager(deployer.mustGetAddress("AddressManager"));
(VmSafe.CallerMode mode ,address msgSender, ) = vm.readCallers();
if ( address(proxyAdmin.addressManager()) != address(addressManager)) {
if(mode != VmSafe.CallerMode.Broadcast && msgSender != owner) {
console.log("Pranking owner ...");
vm.prank(owner);
} else {
console.log("Broadcasting ...");
vm.broadcast(owner);
}
proxyAdmin.setAddressManager( IAddressManager(address(addressManager)));
console.log("AddressManager setted to : %s", address(addressManager));
}
address safe = deployer.mustGetAddress("SystemOwnerSafe");
if (proxyAdmin.owner() != safe) {
if(mode != VmSafe.CallerMode.Broadcast && msgSender != owner) {
console.log("Pranking owner ...");
vm.prank(owner);
} else {
console.log("Broadcasting ...");
vm.broadcast(owner);
}
proxyAdmin.transferOwnership(safe);
console.log("ProxyAdmin ownership transferred to Safe at: %s", safe);
}
}
}
After running the deploy script, the address deployed is saved at deployments/31337/.save.json. Otherwise, as specified in .env.<network>.local.
Your saved address will be different.
You can change DEPLOYMENT_OUTFILE=deployments/31337/.save.json to reflect yours!
{
"SafeProxyFactory": "<ADDRESS_1>",
"SafeSingleton": "<ADDRESS_2>",
"SystemOwnerSafe": "<ADDRESS_3>",
"AddressManager": "<ADDRESS_4>",
"ProxyAdmin": "<ADDRESS_5>"
}
click on ✕ button to close
2.2A : Deploy SuperchainConfigProxy Contract
forge script script/202A_DeploySuperchainConfigProxyScript.s.sol --trezor --sender <DEPLOYER_ADDRESS> --rpc-url <RPC_URL> --broadcast
--mnemonic-derivation-paths "m/44'/60'/0'/0/0"
// 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;
}
}
// 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 DeploySuperchainConfigProxyScript is DeployScript {
using DeployerFunctions for IDeployer ;
function deploy() external returns (Proxy) {
address proxyOwner = deployer.mustGetAddress("ProxyAdmin");
return Proxy(deployer.deploy_ERC1967Proxy("SuperchainConfigProxy", proxyOwner));
}
}
After running the deploy script, the address deployed is saved at deployments/31337/.save.json. Otherwise, as specified in .env.<network>.local.
Your saved address will be different.
You can change DEPLOYMENT_OUTFILE=deployments/31337/.save.json to reflect yours!
{
"SafeProxyFactory": "<ADDRESS_1>",
"SafeSingleton": "<ADDRESS_2>",
"SystemOwnerSafe": "<ADDRESS_3>",
"AddressManager": "<ADDRESS_4>",
"ProxyAdmin": "<ADDRESS_5>",
"SuperchainConfigProxy": "<ADDRESS_6>"
}
click on ✕ button to close
2.2B : DeployAndInitializeSuperchainConfig Contract
forge script script/202B_DeployAndInitializeSuperchainConfigScript.s.sol --trezor --sender <DEPLOYER_ADDRESS> --rpc-url <RPC_URL> --broadcast
--mnemonic-derivation-paths "m/44'/60'/0'/0/0"
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {ISemver} from "@redprint-core/universal/interfaces/ISemver.sol";
import {Initializable} from "@redprint-openzeppelin/proxy/utils/Initializable.sol";
import {Storage} from "@redprint-core/libraries/Storage.sol";
/// @custom:security-contact Consult full code at https://github.com/ethereum-optimism/optimism/blob/v1.9.4/packages/contracts-bedrock/src/L1/SuperchainConfig.sol
contract SuperchainConfig is Initializable, ISemver {
/// @notice Enum representing different types of updates.
/// @custom:value GUARDIAN Represents an update to the guardian.
enum UpdateType {
GUARDIAN
}
/// @notice Whether or not the Superchain is paused.
bytes32 public constant PAUSED_SLOT = bytes32(uint256(keccak256("superchainConfig.paused")) - 1);
/// @notice The address of the guardian, which can pause withdrawals from the System.
/// It can only be modified by an upgrade.
bytes32 public constant GUARDIAN_SLOT = bytes32(uint256(keccak256("superchainConfig.guardian")) - 1);
/// @notice Emitted when the pause is triggered.
/// @param identifier A string helping to identify provenance of the pause transaction.
event Paused(string identifier);
/// @notice Emitted when the pause is lifted.
event Unpaused();
event ConfigUpdate(UpdateType indexed updateType, bytes data);
/// @notice Semantic version.
/// @custom:semver 1.1.1-beta.1
string public constant version = "1.1.1-beta.1";
constructor() {
initialize({ _guardian: address(0), _paused: false });
}
function initialize(address _guardian, bool _paused) public initializer {
_setGuardian(_guardian);
if (_paused) {
_pause("Initializer paused");
}
}
function guardian() public view returns (address guardian_) {
guardian_ = Storage.getAddress(GUARDIAN_SLOT);
}
function paused() public view returns (bool paused_) {
paused_ = Storage.getBool(PAUSED_SLOT);
}
function pause(string memory _identifier) external {
require(msg.sender == guardian(), "SuperchainConfig: only guardian can pause");
_pause(_identifier);
}
function _pause(string memory _identifier) internal returns (address) {
Storage.setBool(PAUSED_SLOT, true);
emit Paused(_identifier);
}
function unpause() external {
require(msg.sender == guardian(), "SuperchainConfig: only guardian can unpause");
Storage.setBool(PAUSED_SLOT, false);
emit Unpaused();
}
function _setGuardian(address _guardian) internal {
Storage.setAddress(GUARDIAN_SLOT, _guardian);
emit ConfigUpdate(UpdateType.GUARDIAN, abi.encode(_guardian));
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {ChainAssertions} from "@redprint-deploy/optimism/ChainAssertions.sol";
import {DeployScript, IDeployer} from "@redprint-deploy/deployer/DeployScript.sol";
import {DeployerFunctions, DeployOptions} from "@redprint-deploy/deployer/DeployerFunctions.sol";
import {Proxy} from "@redprint-core/universal/Proxy.sol";
import {SafeScript} from "@redprint-deploy/safe-management/SafeScript.sol";
import {SuperchainConfig} from "@redprint-core/L1/SuperchainConfig.sol";
import {Vm, VmSafe} from "@redprint-forge-std/Vm.sol";
import {console} from "@redprint-forge-std/console.sol";
/// @custom:security-contact Consult full internal deploy script at https://github.com/Ratimon/redprint-forge
contract DeployAndInitializeSuperchainConfigScript is DeployScript, SafeScript {
using DeployerFunctions for IDeployer ;
SuperchainConfig superchainConfig;
string mnemonic = vm.envString("MNEMONIC");
uint256 ownerPrivateKey = vm.deriveKey(mnemonic, "m/44'/60'/0'/0/", 1);
address owner = vm.envOr("DEPLOYER_ADDRESS", vm.addr(ownerPrivateKey));
function deploy() external returns (SuperchainConfig) {
bytes32 _salt = DeployScript.implSalt();
DeployOptions memory options = DeployOptions({salt:_salt});
superchainConfig = deployer.deploy_SuperchainConfig("SuperchainConfig", options);
return superchainConfig;
}
function initialize() external {
(VmSafe.CallerMode mode ,address msgSender, ) = vm.readCallers();
if(mode != VmSafe.CallerMode.Broadcast && msgSender != owner) {
console.log("Pranking owner ...");
vm.startPrank(owner);
initializeSuperchainConfig();
vm.stopPrank();
} else {
console.log("Broadcasting ...");
vm.startBroadcast(owner);
initializeSuperchainConfig();
console.log("SuperchainConfig setted to : %s", address(superchainConfig));
vm.stopBroadcast();
}
}
function initializeSuperchainConfig() public {
console.log("Upgrading and initializing SuperchainConfig");
address payable superchainConfigProxy = deployer.mustGetAddress("SuperchainConfigProxy");
address proxyAdmin = deployer.mustGetAddress("ProxyAdmin");
address safe = deployer.mustGetAddress("SystemOwnerSafe");
_upgradeAndCallViaSafe({
_proxyAdmin: proxyAdmin,
_safe: safe,
_owner: owner,
_proxy: superchainConfigProxy,
_implementation: address(superchainConfig),
_innerCallData: abi.encodeCall(SuperchainConfig.initialize, ( deployer.getConfig().superchainConfigGuardian(), false))
});
ChainAssertions.checkSuperchainConfig({ _contracts: deployer.getProxiesUnstrict(), _cfg: deployer.getConfig(), _isPaused: false, _isProxy: true });
}
}
After running the deploy script, the address deployed is saved at deployments/31337/.save.json. Otherwise, as specified in .env.<network>.local.
Your saved address will be different.
You can change DEPLOYMENT_OUTFILE=deployments/31337/.save.json to reflect yours!
{
"SafeProxyFactory": "<ADDRESS_1>",
"SafeSingleton": "<ADDRESS_2>",
"SystemOwnerSafe": "<ADDRESS_3>",
"AddressManager": "<ADDRESS_4>",
"ProxyAdmin": "<ADDRESS_5>",
"SuperchainConfigProxy": "<ADDRESS_6>",
"SuperchainConfig": "<ADDRESS_7>"
}
click on ✕ button to close
2.3A : Deploy ProtocolVersionsProxy Contract
forge script script/203A_DeployProtocolVersionsProxyScript.s.sol --trezor --sender <DEPLOYER_ADDRESS> --rpc-url <RPC_URL> --broadcast
--mnemonic-derivation-paths "m/44'/60'/0'/0/0"
// 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;
}
}
// 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 DeployProtocolVersionsProxyScript is DeployScript {
using DeployerFunctions for IDeployer ;
function deploy() external returns (Proxy) {
address proxyOwner = deployer.mustGetAddress("ProxyAdmin");
return Proxy(deployer.deploy_ERC1967Proxy("ProtocolVersionsProxy", proxyOwner));
}
}
After running the deploy script, the address deployed is saved at deployments/31337/.save.json. Otherwise, as specified in .env.<network>.local.
Your saved address will be different.
You can change DEPLOYMENT_OUTFILE=deployments/31337/.save.json to reflect yours!
{
"SafeProxyFactory": "<ADDRESS_1>",
"SafeSingleton": "<ADDRESS_2>",
"SystemOwnerSafe": "<ADDRESS_3>",
"AddressManager": "<ADDRESS_4>",
"ProxyAdmin": "<ADDRESS_5>",
"SuperchainConfigProxy": "<ADDRESS_6>",
"SuperchainConfig": "<ADDRESS_7>",
"ProtocolVersionsProxy": "<ADDRESS_8>"
}
click on ✕ button to close
2.3B : DeployAndInitializeProtocolVersions Contract
forge script script/203B_DeployAndInitializeProtocolVersionsScript.s.sol --trezor --sender <DEPLOYER_ADDRESS> --rpc-url <RPC_URL> --broadcast
--mnemonic-derivation-paths "m/44'/60'/0'/0/0"
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {ISemver} from "@redprint-core/universal/interfaces/ISemver.sol";
import {OwnableUpgradeable} from "@redprint-openzeppelin-upgradeable/access/OwnableUpgradeable.sol";
import {Storage} from "@redprint-core/libraries/Storage.sol";
type ProtocolVersion is uint256;
/// @custom:security-contact Consult full code at https://github.com/ethereum-optimism/optimism/blob/v1.9.4/packages/contracts-bedrock/src/L1/ProtocolVersions.sol
contract ProtocolVersions is OwnableUpgradeable, ISemver {
/// @notice Enum representing different types of updates.
/// @custom:value REQUIRED_PROTOCOL_VERSION Represents an update to the required protocol version.
/// @custom:value RECOMMENDED_PROTOCOL_VERSION Represents an update to the recommended protocol version.
enum UpdateType {
REQUIRED_PROTOCOL_VERSION,
RECOMMENDED_PROTOCOL_VERSION
}
/// @notice Version identifier, used for upgrades.
uint256 public constant VERSION = 0;
/// @notice Storage slot that the required protocol version is stored at.
bytes32 public constant REQUIRED_SLOT = bytes32(uint256(keccak256("protocolversion.required")) - 1);
/// @notice Storage slot that the recommended protocol version is stored at.
bytes32 public constant RECOMMENDED_SLOT = bytes32(uint256(keccak256("protocolversion.recommended")) - 1);
/// @notice Emitted when configuration is updated.
/// @param version ProtocolVersion version.
/// @param updateType Type of update.
/// @param data Encoded update data.
event ConfigUpdate(uint256 indexed version, UpdateType indexed updateType, bytes data);
/// @notice Semantic version.
/// @custom:semver 1.0.1-beta.3
string public constant version = "1.0.1-beta.3";
constructor() {
initialize({
_owner: address(0xdEaD),
_required: ProtocolVersion.wrap(uint256(0)),
_recommended: ProtocolVersion.wrap(uint256(0))
});
}
function initialize(address _owner, ProtocolVersion _required, ProtocolVersion _recommended)
public
initializer
{
__Ownable_init();
transferOwnership(_owner);
_setRequired(_required);
_setRecommended(_recommended);
}
function required() external view returns (ProtocolVersion out_) {
out_ = ProtocolVersion.wrap(Storage.getUint(REQUIRED_SLOT));
}
function setRequired(ProtocolVersion _required) external onlyOwner {
_setRequired(_required);
}
function _setRequired(ProtocolVersion _required) internal {
Storage.setUint(REQUIRED_SLOT, ProtocolVersion.unwrap(_required));
bytes memory data = abi.encode(_required);
emit ConfigUpdate(VERSION, UpdateType.REQUIRED_PROTOCOL_VERSION, data);
}
function recommended() external view returns (ProtocolVersion out_) {
out_ = ProtocolVersion.wrap(Storage.getUint(RECOMMENDED_SLOT));
}
function setRecommended(ProtocolVersion _recommended) external onlyOwner {
_setRecommended(_recommended);
}
function _setRecommended(ProtocolVersion _recommended) internal {
Storage.setUint(RECOMMENDED_SLOT, ProtocolVersion.unwrap(_recommended));
bytes memory data = abi.encode(_recommended);
emit ConfigUpdate(VERSION, UpdateType.RECOMMENDED_PROTOCOL_VERSION, data);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {ChainAssertions} from "@redprint-deploy/optimism/ChainAssertions.sol";
import {DeployScript, IDeployer} from "@redprint-deploy/deployer/DeployScript.sol";
import {DeployerFunctions, DeployOptions} from "@redprint-deploy/deployer/DeployerFunctions.sol";
import {ProtocolVersions, ProtocolVersion} from "@redprint-core/L1/ProtocolVersions.sol";
import {SafeScript} from "@redprint-deploy/safe-management/SafeScript.sol";
import {Types} from "@redprint-deploy/optimism/Types.sol";
import {Vm, VmSafe} from "@redprint-forge-std/Vm.sol";
import {console} from "@redprint-forge-std/console.sol";
/// @custom:security-contact Consult full internal deploy script at https://github.com/Ratimon/redprint-forge
contract DeployAndInitializeProtocolVersionsScript is DeployScript, SafeScript {
using DeployerFunctions for IDeployer ;
ProtocolVersions versions;
string mnemonic = vm.envString("MNEMONIC");
uint256 ownerPrivateKey = vm.deriveKey(mnemonic, "m/44'/60'/0'/0/", 1);
address owner = vm.envOr("DEPLOYER_ADDRESS", vm.addr(ownerPrivateKey));
function deploy() external returns (ProtocolVersions) {
bytes32 _salt = DeployScript.implSalt();
DeployOptions memory options = DeployOptions({salt:_salt});
versions = deployer.deploy_ProtocolVersions("ProtocolVersions", options);
Types.ContractSet memory contracts = deployer.getProxiesUnstrict();
contracts.ProtocolVersions = address(versions);
ChainAssertions.checkProtocolVersions({ _contracts: contracts, _cfg: deployer.getConfig(), _isProxy: false });
return versions;
}
function initialize() external {
(VmSafe.CallerMode mode ,address msgSender, ) = vm.readCallers();
if(mode != VmSafe.CallerMode.Broadcast && msgSender != owner) {
console.log("Pranking owner ...");
vm.startPrank(owner);
initializeProtocolVersions();
vm.stopPrank();
} else {
console.log("Broadcasting ...");
vm.startBroadcast(owner);
initializeProtocolVersions();
console.log("ProtocolVersions setted to : %s", address(versions));
vm.stopBroadcast();
}
}
function initializeProtocolVersions() public {
console.log("Upgrading and initializing ProtocolVersions proxy");
address proxyAdmin = deployer.mustGetAddress("ProxyAdmin");
address safe = deployer.mustGetAddress("SystemOwnerSafe");
address protocolVersionsProxy = deployer.mustGetAddress("ProtocolVersionsProxy");
address protocolVersions = deployer.mustGetAddress("ProtocolVersions");
address finalSystemOwner = deployer.getConfig().finalSystemOwner();
uint256 requiredProtocolVersion = deployer.getConfig().requiredProtocolVersion();
uint256 recommendedProtocolVersion = deployer.getConfig().recommendedProtocolVersion();
_upgradeAndCallViaSafe({
_proxyAdmin: proxyAdmin,
_safe: safe,
_owner: owner,
_proxy: payable(protocolVersionsProxy),
_implementation: protocolVersions,
_innerCallData: abi.encodeCall(
ProtocolVersions.initialize,
(
finalSystemOwner,
ProtocolVersion.wrap(requiredProtocolVersion),
ProtocolVersion.wrap(recommendedProtocolVersion)
)
)
});
ProtocolVersions _versions = ProtocolVersions(protocolVersionsProxy);
string memory version = _versions.version();
console.log("ProtocolVersions version: %s", version);
ChainAssertions.checkProtocolVersions({ _contracts: deployer.getProxiesUnstrict(), _cfg: deployer.getConfig(), _isProxy: true });
}
}
After running the deploy script, the address deployed is saved at deployments/31337/.save.json. Otherwise, as specified in .env.<network>.local.
Your saved address will be different.
You can change DEPLOYMENT_OUTFILE=deployments/31337/.save.json to reflect yours!
{
"SafeProxyFactory": "<ADDRESS_1>",
"SafeSingleton": "<ADDRESS_2>",
"SystemOwnerSafe": "<ADDRESS_3>",
"AddressManager": "<ADDRESS_4>",
"ProxyAdmin": "<ADDRESS_5>",
"SuperchainConfigProxy": "<ADDRESS_6>",
"SuperchainConfig": "<ADDRESS_7>",
"ProtocolVersionsProxy": "<ADDRESS_8>",
"ProtocolVersions": "<ADDRESS_9>"
}
click on ✕ button to close