Superfuse Wizard: an interactive code generator for superchain interoperability.
Build your own cross-chain contracts!! use our toolkit
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
2.0 : Prerequisites
Make sure you have run the deploy script for governance 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>"
}
Without this artifact file, the next deployment scripts can not be run.
One-Click L1 Superchain 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
Step 2
// 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();
}
}
Contract Settings
Step 2
AddressManager ProxyAdmin SuperchainConfigProxy SuperchainConfig ProtocolVersionsProxy ProtocolVersions
// 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.
Example!
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
In your terminal, copy below contracts' codes and run deployment scripts to your prefered network:
forge script script/201A_DeployAddressManagerScript.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
// 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.
Example!
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
In your terminal, copy below contracts' codes and run deployment scripts to your prefered network:
forge script script/201B_DeployAndSetupProxyAdminScript.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
OpSec Management
// 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.
Example!
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
In your terminal, copy below contracts' codes and run deployment scripts to your prefered network:
forge script script/202A_DeploySuperchainConfigProxyScript.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
// 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.
Example!
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
In your terminal, copy below contracts' codes and run deployment scripts to your prefered network:
forge script script/202B_DeployAndInitializeSuperchainConfigScript.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
OpSec Management
// 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.
Example!
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
In your terminal, copy below contracts' codes and run deployment scripts to your prefered network:
forge script script/203A_DeployProtocolVersionsProxyScript.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
// 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.
Example!
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
In your terminal, copy below contracts' codes and run deployment scripts to your prefered network:
forge script script/203B_DeployAndInitializeProtocolVersionsScript.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
OpSec Management
// 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.
Example!
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