Commit abe39e98 authored by Diego's avatar Diego Committed by GitHub

feat/interop: create `SystemConfigInterop` (#10586)

* contracts-bedrock: create OptimismPortalInterop

* contracts-bedrock: add conditional deployment of SystemConfigInterop

* contracts-bedrock: drop initializeOptimismPortalInterop in Deploy script

* contracts-bedrock: remove checkSystemConfigInterop from ChainAssertions

* contracts-bedrock: drop initializeSystemConfigInterop in Deploy script

* contracts-bedrock: drop deploySystemConfigInterop from Deploy script

* contracts-bedrock: drop SystemConfigInterop from Types

* contracts-bedrock: drop legacy import in ChainAssertions

* contracts-bedrock: drop legacy fields in Deploy script

* contracts-bedrock: drop unnecessary diffs

* contracts-bedrock: add conditional deployment of SystemConfigInterop

* contracts-bedrock: drop initializeOptimismPortalInterop in Deploy script

* contracts-bedrock: create tests for SystemConfigInterop

* contracts-bedrock: reorder dependencies in tests for SystemConfigInterop

* contracts-bedrock: add SystemConfigInterop to tests Setup

* contracts-bedrock: update tests for SystemConfigInterop

* contracts-bedrock: restore Deploy script

* contracts-bedrock: restore ChainAssertions

* contracts-bedrock: restore Types

* contracts-bedrock: drop SystemConfigInterop from Setup script

* contracts-bedrock: update SystemConfigInterop

* contracts-bedrock: add not vm check in SystemConfigInterop tests

* contracts-bedrock: update tests for SystemConfigInterop

* contracts-bedrock: define internal getter for systemConfigInterop in tests

* contracts-bedrock: restrict visibility of function in tests for SystemConfig

* contracts-bedrock: fix tests for SystemConfigInterop

* contracts-bedrock: fix tests for SystemConfigInterop

* contracts-bedrock: reinitialize for gas paying test for SystemConfigInterop

* contracts-bedrock: reorder functions in tests for SystemConfigInterop

* contracts-bedrock: use ConfigType.GAS_PAYING_TOKEN in tests for SystemConfigInterop

* contracts-bedrock: use assumeNotForgeAddress in tests for SystemConfigInterop

---------
Co-authored-by: default avatarMark Tyneway <mark.tyneway@gmail.com>
parent 4c3f63de
......@@ -29,6 +29,7 @@ import { L2OutputOracle } from "src/L1/L2OutputOracle.sol";
import { OptimismMintableERC20Factory } from "src/universal/OptimismMintableERC20Factory.sol";
import { SuperchainConfig } from "src/L1/SuperchainConfig.sol";
import { SystemConfig } from "src/L1/SystemConfig.sol";
import { SystemConfigInterop } from "src/L1/SystemConfigInterop.sol";
import { ResourceMetering } from "src/L1/ResourceMetering.sol";
import { DataAvailabilityChallenge } from "src/L1/DataAvailabilityChallenge.sol";
import { Constants } from "src/libraries/Constants.sol";
......@@ -812,19 +813,20 @@ contract Deploy is Deployer {
/// @notice Deploy the SystemConfig
function deploySystemConfig() public broadcast returns (address addr_) {
console.log("Deploying SystemConfig implementation");
SystemConfig config = new SystemConfig{ salt: _implSalt() }();
save("SystemConfig", address(config));
console.log("SystemConfig deployed at %s", address(config));
if (cfg.useInterop()) {
addr_ = address(new SystemConfigInterop{ salt: _implSalt() }());
} else {
addr_ = address(new SystemConfig{ salt: _implSalt() }());
}
save("SystemConfig", addr_);
console.log("SystemConfig deployed at %s", addr_);
// Override the `SystemConfig` contract to the deployed implementation. This is necessary
// to check the `SystemConfig` implementation alongside dependent contracts, which
// are always proxies.
Types.ContractSet memory contracts = _proxiesUnstrict();
contracts.SystemConfig = address(config);
contracts.SystemConfig = addr_;
ChainAssertions.checkSystemConfig({ _contracts: contracts, _cfg: cfg, _isProxy: false });
addr_ = address(config);
}
/// @notice Deploy the L1StandardBridge
......
......@@ -52,8 +52,12 @@
"sourceCodeHash": "0xd6a894e371c2c7182b5960c507491f81c3775dda0efedd29475f7c30ca07b004"
},
"src/L1/SystemConfig.sol": {
"initCodeHash": "0x6160c9c96d0972e0761ab3b9d4f6d1b8543288c4c3aea9e132008a40e4c5d202",
"sourceCodeHash": "0xd27df6e227cc4728471641aec25a1fe6f79a12ce96d3ce6c1774d9411a54133e"
"initCodeHash": "0x3324c93485f594bccb2af1a0c5a3551948ae9b347baea371764ce8fe239c39be",
"sourceCodeHash": "0xaed39fb8a0ce4b8d7a97ead42074e0c672fa18a58a57227b9d32abe2c3600223"
},
"src/L1/SystemConfigInterop.sol": {
"initCodeHash": "0x710484da188ec16a0ade97d99ecd4f5e910bc87ad01e448db4cf3f0e5050aaee",
"sourceCodeHash": "0x40d708140ee6345e146e358c169a191dbbc991782560a2dcbf90ba45a82f7117"
},
"src/L2/BaseFeeVault.sol": {
"initCodeHash": "0x2744d34573be83206d1b75d049d18a7bb37f9058e68c0803e5008c46b0dc2474",
......
......@@ -697,7 +697,7 @@
"type": "string"
}
],
"stateMutability": "view",
"stateMutability": "pure",
"type": "function"
},
{
......
[
{
"bytes": "1",
"label": "_initialized",
"offset": 0,
"slot": "0",
"type": "uint8"
},
{
"bytes": "1",
"label": "_initializing",
"offset": 1,
"slot": "0",
"type": "bool"
},
{
"bytes": "1600",
"label": "__gap",
"offset": 0,
"slot": "1",
"type": "uint256[50]"
},
{
"bytes": "20",
"label": "_owner",
"offset": 0,
"slot": "51",
"type": "address"
},
{
"bytes": "1568",
"label": "__gap",
"offset": 0,
"slot": "52",
"type": "uint256[49]"
},
{
"bytes": "32",
"label": "overhead",
"offset": 0,
"slot": "101",
"type": "uint256"
},
{
"bytes": "32",
"label": "scalar",
"offset": 0,
"slot": "102",
"type": "uint256"
},
{
"bytes": "32",
"label": "batcherHash",
"offset": 0,
"slot": "103",
"type": "bytes32"
},
{
"bytes": "8",
"label": "gasLimit",
"offset": 0,
"slot": "104",
"type": "uint64"
},
{
"bytes": "4",
"label": "basefeeScalar",
"offset": 8,
"slot": "104",
"type": "uint32"
},
{
"bytes": "4",
"label": "blobbasefeeScalar",
"offset": 12,
"slot": "104",
"type": "uint32"
},
{
"bytes": "32",
"label": "_resourceConfig",
"offset": 0,
"slot": "105",
"type": "struct ResourceMetering.ResourceConfig"
}
]
\ No newline at end of file
......@@ -124,8 +124,10 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken {
event ConfigUpdate(uint256 indexed version, UpdateType indexed updateType, bytes data);
/// @notice Semantic version.
/// @custom:semver 2.3.0-beta.1
string public constant version = "2.3.0-beta.1";
/// @custom:semver 2.3.0-beta.2
function version() public pure virtual returns (string memory) {
return "2.3.0-beta.2";
}
/// @notice Constructs the SystemConfig contract. Cannot set
/// the owner to `address(0)` due to the Ownable contract's
......@@ -305,7 +307,7 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken {
/// to set the token address. This prevents the token address from being changed
/// and makes it explicitly opt-in to use custom gas token.
/// @param _token Address of the gas paying token.
function _setGasPayingToken(address _token) internal {
function _setGasPayingToken(address _token) internal virtual {
if (_token != address(0) && _token != Constants.ETHER && !isCustomGasToken()) {
require(
ERC20(_token).decimals() == GAS_PAYING_TOKEN_DECIMALS, "SystemConfig: bad decimals of gas paying token"
......
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import { Constants } from "src/libraries/Constants.sol";
import { OptimismPortalInterop as OptimismPortal } from "src/L1/OptimismPortalInterop.sol";
import { GasPayingToken } from "src/libraries/GasPayingToken.sol";
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { SystemConfig } from "src/L1/SystemConfig.sol";
import { ConfigType } from "src/L2/L1BlockInterop.sol";
import { StaticConfig } from "src/libraries/StaticConfig.sol";
/// @title SystemConfigInterop
/// @notice The SystemConfig contract is used to manage configuration of an Optimism network.
/// All configuration is stored on L1 and picked up by L2 as part of the derviation of
/// the L2 chain.
contract SystemConfigInterop is SystemConfig {
/// @custom:semver +interop
function version() public pure override returns (string memory) {
return string.concat(super.version(), "+interop");
}
/// @notice Internal setter for the gas paying token address, includes validation.
/// The token must not already be set and must be non zero and not the ether address
/// to set the token address. This prevents the token address from being changed
/// and makes it explicitly opt-in to use custom gas token. Additionally,
/// OptimismPortal's address must be non zero, since otherwise the call to set the
/// config for the gas paying token to OptimismPortal will fail.
/// @param _token Address of the gas paying token.
function _setGasPayingToken(address _token) internal override {
if (_token != address(0) && _token != Constants.ETHER && !isCustomGasToken()) {
require(
ERC20(_token).decimals() == GAS_PAYING_TOKEN_DECIMALS, "SystemConfig: bad decimals of gas paying token"
);
bytes32 name = GasPayingToken.sanitize(ERC20(_token).name());
bytes32 symbol = GasPayingToken.sanitize(ERC20(_token).symbol());
// Set the gas paying token in storage and in the OptimismPortal.
GasPayingToken.set({ _token: _token, _decimals: GAS_PAYING_TOKEN_DECIMALS, _name: name, _symbol: symbol });
OptimismPortal(payable(optimismPortal())).setConfig(
ConfigType.SET_GAS_PAYING_TOKEN,
StaticConfig.encodeSetGasPayingToken({
_token: _token,
_decimals: GAS_PAYING_TOKEN_DECIMALS,
_name: name,
_symbol: symbol
})
);
}
}
/// @notice Adds a chain to the interop dependency set. Can only be called by the owner.
/// @param _chainId Chain ID of chain to add.
function addDependency(uint256 _chainId) external onlyOwner {
OptimismPortal(payable(optimismPortal())).setConfig(
ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(_chainId)
);
}
/// @notice Removes a chain from the interop dependency set. Can only be called by the owner.
/// @param _chainId Chain ID of the chain to remove.
function removeDependency(uint256 _chainId) external onlyOwner {
OptimismPortal(payable(optimismPortal())).setConfig(
ConfigType.REMOVE_DEPENDENCY, StaticConfig.encodeRemoveDependency(_chainId)
);
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
// Testing utilities
import { CommonTest } from "test/setup/CommonTest.sol";
// Libraries
import { Constants } from "src/libraries/Constants.sol";
import { StaticConfig } from "src/libraries/StaticConfig.sol";
import { GasPayingToken } from "src/libraries/GasPayingToken.sol";
// Target contract dependencies
import { SystemConfig } from "src/L1/SystemConfig.sol";
import { SystemConfigInterop } from "src/L1/SystemConfigInterop.sol";
import { OptimismPortalInterop } from "src/L1/OptimismPortalInterop.sol";
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { ConfigType } from "src/L2/L1BlockInterop.sol";
contract SystemConfigInterop_Test is CommonTest {
/// @notice Marked virtual to be overridden in
/// test/kontrol/deployment/DeploymentSummary.t.sol
function setUp() public virtual override {
super.enableInterop();
super.setUp();
}
/// @dev Tests that the gas paying token can be set.
function testFuzz_setGasPayingToken_succeeds(
address _token,
string calldata _name,
string calldata _symbol
)
public
{
assumeNotForgeAddress(_token);
vm.assume(_token != address(0));
vm.assume(_token != Constants.ETHER);
vm.assume(bytes(_name).length <= 32);
vm.assume(bytes(_symbol).length <= 32);
vm.mockCall(_token, abi.encodeWithSelector(ERC20.decimals.selector), abi.encode(18));
vm.mockCall(_token, abi.encodeWithSelector(ERC20.name.selector), abi.encode(_name));
vm.mockCall(_token, abi.encodeWithSelector(ERC20.symbol.selector), abi.encode(_symbol));
vm.expectCall(
address(optimismPortal),
abi.encodeCall(
OptimismPortalInterop.setConfig,
(
ConfigType.SET_GAS_PAYING_TOKEN,
StaticConfig.encodeSetGasPayingToken({
_token: _token,
_decimals: 18,
_name: GasPayingToken.sanitize(_name),
_symbol: GasPayingToken.sanitize(_symbol)
})
)
)
);
_cleanStorageAndInit(_token);
}
/// @dev Tests that a dependency can be added.
function testFuzz_addDependency_succeeds(uint256 _chainId) public {
vm.expectCall(
address(optimismPortal),
abi.encodeCall(
OptimismPortalInterop.setConfig, (ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(_chainId))
)
);
vm.prank(systemConfig.owner());
_systemConfigInterop().addDependency(_chainId);
}
/// @dev Tests that adding a dependency as not the owner reverts.
function testFuzz_addDependency_notOwner_reverts(uint256 _chainId) public {
vm.expectRevert("Ownable: caller is not the owner");
_systemConfigInterop().addDependency(_chainId);
}
/// @dev Tests that a dependency can be removed.
function testFuzz_removeDependency_succeeds(uint256 _chainId) public {
vm.expectCall(
address(optimismPortal),
abi.encodeCall(
OptimismPortalInterop.setConfig,
(ConfigType.REMOVE_DEPENDENCY, StaticConfig.encodeRemoveDependency(_chainId))
)
);
vm.prank(systemConfig.owner());
_systemConfigInterop().removeDependency(_chainId);
}
/// @dev Tests that removing a dependency as not the owner reverts.
function testFuzz_removeDependency_notOwner_reverts(uint256 _chainId) public {
vm.expectRevert("Ownable: caller is not the owner");
_systemConfigInterop().removeDependency(_chainId);
}
/// @dev Helper to clean storage and then initialize the system config with an arbitrary gas token address.
function _cleanStorageAndInit(address _token) internal {
// Wipe out the initialized slot so the proxy can be initialized again
vm.store(address(systemConfig), bytes32(0), bytes32(0));
vm.store(address(systemConfig), GasPayingToken.GAS_PAYING_TOKEN_SLOT, bytes32(0));
vm.store(address(systemConfig), GasPayingToken.GAS_PAYING_TOKEN_NAME_SLOT, bytes32(0));
vm.store(address(systemConfig), GasPayingToken.GAS_PAYING_TOKEN_SYMBOL_SLOT, bytes32(0));
systemConfig.initialize({
_owner: alice,
_basefeeScalar: 2100,
_blobbasefeeScalar: 1000000,
_batcherHash: bytes32(hex"abcd"),
_gasLimit: 30_000_000,
_unsafeBlockSigner: address(1),
_config: Constants.DEFAULT_RESOURCE_CONFIG(),
_batchInbox: address(0),
_addresses: SystemConfig.Addresses({
l1CrossDomainMessenger: address(0),
l1ERC721Bridge: address(0),
disputeGameFactory: address(0),
l1StandardBridge: address(0),
optimismPortal: address(optimismPortal),
optimismMintableERC20Factory: address(0),
gasPayingToken: _token
})
});
}
/// @dev Returns the SystemConfigInterop instance.
function _systemConfigInterop() internal view returns (SystemConfigInterop) {
return SystemConfigInterop(address(systemConfig));
}
}
......@@ -30,7 +30,7 @@ contract DeploymentSummary is DeploymentSummaryCode {
address internal constant safeSingletonAddress = 0x90193C961A926261B756D1E5bb255e67ff9498A1;
address internal constant superchainConfigAddress = 0x068E44eB31e111028c41598E4535be7468674D0A;
address internal constant superchainConfigProxyAddress = 0xDEb1E9a6Be7Baf84208BB6E10aC9F9bbE1D70809;
address internal constant systemConfigAddress = 0x1182f45ae539e18534cbCC222c1C3D972B5Ba24D;
address internal constant systemConfigAddress = 0x67866A5052E5302aaD08e9f352331fd8622eB6DC;
address internal constant systemConfigProxyAddress = 0x1c23A6d89F95ef3148BCDA8E242cAb145bf9c0E4;
address internal constant systemOwnerSafeAddress = 0x7d039be7F9b5190147621b02e82B250e1D748e02;
......@@ -284,7 +284,7 @@ contract DeploymentSummary is DeploymentSummaryCode {
value = hex"0000000000000000000000000000000000000000000000000000000000000003";
vm.store(systemOwnerSafeAddress, slot, value);
slot = hex"360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
value = hex"0000000000000000000000001182f45ae539e18534cbcc222c1c3d972b5ba24d";
value = hex"00000000000000000000000067866a5052e5302aad08e9f352331fd8622eb6dc";
vm.store(systemConfigProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000000";
value = hex"0000000000000000000000000000000000000000000000000000000000000001";
......
......@@ -36,7 +36,7 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode {
address internal constant safeSingletonAddress = 0x90193C961A926261B756D1E5bb255e67ff9498A1;
address internal constant superchainConfigAddress = 0x068E44eB31e111028c41598E4535be7468674D0A;
address internal constant superchainConfigProxyAddress = 0xDEb1E9a6Be7Baf84208BB6E10aC9F9bbE1D70809;
address internal constant systemConfigAddress = 0x1182f45ae539e18534cbCC222c1C3D972B5Ba24D;
address internal constant systemConfigAddress = 0x67866A5052E5302aaD08e9f352331fd8622eB6DC;
address internal constant systemConfigProxyAddress = 0x20A42a5a785622c6Ba2576B2D6e924aA82BFA11D;
address internal constant systemOwnerSafeAddress = 0x7d039be7F9b5190147621b02e82B250e1D748e02;
address internal constant acc27Address = 0x12e721c390F5728200a26BBEf206A5F4F7E991f3;
......@@ -376,7 +376,7 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode {
value = hex"0000000000000000000000000000000000000000000000000000000000000003";
vm.store(systemOwnerSafeAddress, slot, value);
slot = hex"360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
value = hex"0000000000000000000000001182f45ae539e18534cbcc222c1c3d972b5ba24d";
value = hex"00000000000000000000000067866a5052e5302aad08e9f352331fd8622eb6dc";
vm.store(systemConfigProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000000";
value = hex"0000000000000000000000000000000000000000000000000000000000000001";
......
......@@ -351,7 +351,7 @@ contract Initializer_Test is Bridge_Initializer {
// Ensure that all L1, L2 `Initializable` contracts are accounted for, in addition to
// OptimismMintableERC20FactoryImpl, OptimismMintableERC20FactoryProxy, OptimismPortal2,
// DisputeGameFactoryImpl, DisputeGameFactoryProxy, DelayedWETHImpl, DelayedWETHProxy.
assertEq(_getNumInitializable() + 3, contracts.length);
assertEq(_getNumInitializable() + 1, contracts.length);
// Attempt to re-initialize all contracts within the `contracts` array.
for (uint256 i; i < contracts.length; i++) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment