github.com/ethereum-optimism/optimism@v1.7.2/packages/contracts-bedrock/src/L1/SystemConfig.sol (about) 1 // SPDX-License-Identifier: MIT 2 pragma solidity 0.8.15; 3 4 import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; 5 import { ISemver } from "src/universal/ISemver.sol"; 6 import { ResourceMetering } from "src/L1/ResourceMetering.sol"; 7 import { Storage } from "src/libraries/Storage.sol"; 8 import { Constants } from "src/libraries/Constants.sol"; 9 10 /// @title SystemConfig 11 /// @notice The SystemConfig contract is used to manage configuration of an Optimism network. 12 /// All configuration is stored on L1 and picked up by L2 as part of the derviation of 13 /// the L2 chain. 14 contract SystemConfig is OwnableUpgradeable, ISemver { 15 /// @notice Enum representing different types of updates. 16 /// @custom:value BATCHER Represents an update to the batcher hash. 17 /// @custom:value GAS_CONFIG Represents an update to txn fee config on L2. 18 /// @custom:value GAS_LIMIT Represents an update to gas limit on L2. 19 /// @custom:value UNSAFE_BLOCK_SIGNER Represents an update to the signer key for unsafe 20 /// block distrubution. 21 enum UpdateType { 22 BATCHER, 23 GAS_CONFIG, 24 GAS_LIMIT, 25 UNSAFE_BLOCK_SIGNER 26 } 27 28 /// @notice Struct representing the addresses of L1 system contracts. These should be the 29 /// proxies and are network specific. 30 struct Addresses { 31 address l1CrossDomainMessenger; 32 address l1ERC721Bridge; 33 address l1StandardBridge; 34 address l2OutputOracle; 35 address optimismPortal; 36 address optimismMintableERC20Factory; 37 } 38 39 /// @notice Version identifier, used for upgrades. 40 uint256 public constant VERSION = 0; 41 42 /// @notice Storage slot that the unsafe block signer is stored at. 43 /// Storing it at this deterministic storage slot allows for decoupling the storage 44 /// layout from the way that `solc` lays out storage. The `op-node` uses a storage 45 /// proof to fetch this value. 46 /// @dev NOTE: this value will be migrated to another storage slot in a future version. 47 /// User input should not be placed in storage in this contract until this migration 48 /// happens. It is unlikely that keccak second preimage resistance will be broken, 49 /// but it is better to be safe than sorry. 50 bytes32 public constant UNSAFE_BLOCK_SIGNER_SLOT = keccak256("systemconfig.unsafeblocksigner"); 51 52 /// @notice Storage slot that the L1CrossDomainMessenger address is stored at. 53 bytes32 public constant L1_CROSS_DOMAIN_MESSENGER_SLOT = 54 bytes32(uint256(keccak256("systemconfig.l1crossdomainmessenger")) - 1); 55 56 /// @notice Storage slot that the L1ERC721Bridge address is stored at. 57 bytes32 public constant L1_ERC_721_BRIDGE_SLOT = bytes32(uint256(keccak256("systemconfig.l1erc721bridge")) - 1); 58 59 /// @notice Storage slot that the L1StandardBridge address is stored at. 60 bytes32 public constant L1_STANDARD_BRIDGE_SLOT = bytes32(uint256(keccak256("systemconfig.l1standardbridge")) - 1); 61 62 /// @notice Storage slot that the L2OutputOracle address is stored at. 63 bytes32 public constant L2_OUTPUT_ORACLE_SLOT = bytes32(uint256(keccak256("systemconfig.l2outputoracle")) - 1); 64 65 /// @notice Storage slot that the OptimismPortal address is stored at. 66 bytes32 public constant OPTIMISM_PORTAL_SLOT = bytes32(uint256(keccak256("systemconfig.optimismportal")) - 1); 67 68 /// @notice Storage slot that the OptimismMintableERC20Factory address is stored at. 69 bytes32 public constant OPTIMISM_MINTABLE_ERC20_FACTORY_SLOT = 70 bytes32(uint256(keccak256("systemconfig.optimismmintableerc20factory")) - 1); 71 72 /// @notice Storage slot that the batch inbox address is stored at. 73 bytes32 public constant BATCH_INBOX_SLOT = bytes32(uint256(keccak256("systemconfig.batchinbox")) - 1); 74 75 /// @notice Storage slot for block at which the op-node can start searching for logs from. 76 bytes32 public constant START_BLOCK_SLOT = bytes32(uint256(keccak256("systemconfig.startBlock")) - 1); 77 78 /// @notice Fixed L2 gas overhead. Used as part of the L2 fee calculation. 79 uint256 public overhead; 80 81 /// @notice Dynamic L2 gas overhead. Used as part of the L2 fee calculation. 82 uint256 public scalar; 83 84 /// @notice Identifier for the batcher. 85 /// For version 1 of this configuration, this is represented as an address left-padded 86 /// with zeros to 32 bytes. 87 bytes32 public batcherHash; 88 89 /// @notice L2 block gas limit. 90 uint64 public gasLimit; 91 92 /// @notice The configuration for the deposit fee market. 93 /// Used by the OptimismPortal to meter the cost of buying L2 gas on L1. 94 /// Set as internal with a getter so that the struct is returned instead of a tuple. 95 ResourceMetering.ResourceConfig internal _resourceConfig; 96 97 /// @notice Emitted when configuration is updated. 98 /// @param version SystemConfig version. 99 /// @param updateType Type of update. 100 /// @param data Encoded update data. 101 event ConfigUpdate(uint256 indexed version, UpdateType indexed updateType, bytes data); 102 103 /// @notice Semantic version. 104 /// @custom:semver 1.12.0 105 string public constant version = "1.12.0"; 106 107 /// @notice Constructs the SystemConfig contract. Cannot set 108 /// the owner to `address(0)` due to the Ownable contract's 109 /// implementation, so set it to `address(0xdEaD)` 110 /// @dev START_BLOCK_SLOT is set to type(uint256).max here so that it will be a dead value 111 /// in the singleton and is skipped by initialize when setting the start block. 112 constructor() { 113 Storage.setUint(START_BLOCK_SLOT, type(uint256).max); 114 initialize({ 115 _owner: address(0xdEaD), 116 _overhead: 0, 117 _scalar: 0, 118 _batcherHash: bytes32(0), 119 _gasLimit: 1, 120 _unsafeBlockSigner: address(0), 121 _config: ResourceMetering.ResourceConfig({ 122 maxResourceLimit: 1, 123 elasticityMultiplier: 1, 124 baseFeeMaxChangeDenominator: 2, 125 minimumBaseFee: 0, 126 systemTxMaxGas: 0, 127 maximumBaseFee: 0 128 }), 129 _batchInbox: address(0), 130 _addresses: SystemConfig.Addresses({ 131 l1CrossDomainMessenger: address(0), 132 l1ERC721Bridge: address(0), 133 l1StandardBridge: address(0), 134 l2OutputOracle: address(0), 135 optimismPortal: address(0), 136 optimismMintableERC20Factory: address(0) 137 }) 138 }); 139 } 140 141 /// @notice Initializer. 142 /// The resource config must be set before the require check. 143 /// @param _owner Initial owner of the contract. 144 /// @param _overhead Initial overhead value. 145 /// @param _scalar Initial scalar value. 146 /// @param _batcherHash Initial batcher hash. 147 /// @param _gasLimit Initial gas limit. 148 /// @param _unsafeBlockSigner Initial unsafe block signer address. 149 /// @param _config Initial ResourceConfig. 150 /// @param _batchInbox Batch inbox address. An identifier for the op-node to find 151 /// canonical data. 152 /// @param _addresses Set of L1 contract addresses. These should be the proxies. 153 function initialize( 154 address _owner, 155 uint256 _overhead, 156 uint256 _scalar, 157 bytes32 _batcherHash, 158 uint64 _gasLimit, 159 address _unsafeBlockSigner, 160 ResourceMetering.ResourceConfig memory _config, 161 address _batchInbox, 162 SystemConfig.Addresses memory _addresses 163 ) 164 public 165 initializer 166 { 167 __Ownable_init(); 168 transferOwnership(_owner); 169 170 // These are set in ascending order of their UpdateTypes. 171 _setBatcherHash(_batcherHash); 172 _setGasConfig({ _overhead: _overhead, _scalar: _scalar }); 173 _setGasLimit(_gasLimit); 174 175 Storage.setAddress(UNSAFE_BLOCK_SIGNER_SLOT, _unsafeBlockSigner); 176 Storage.setAddress(BATCH_INBOX_SLOT, _batchInbox); 177 Storage.setAddress(L1_CROSS_DOMAIN_MESSENGER_SLOT, _addresses.l1CrossDomainMessenger); 178 Storage.setAddress(L1_ERC_721_BRIDGE_SLOT, _addresses.l1ERC721Bridge); 179 Storage.setAddress(L1_STANDARD_BRIDGE_SLOT, _addresses.l1StandardBridge); 180 Storage.setAddress(L2_OUTPUT_ORACLE_SLOT, _addresses.l2OutputOracle); 181 Storage.setAddress(OPTIMISM_PORTAL_SLOT, _addresses.optimismPortal); 182 Storage.setAddress(OPTIMISM_MINTABLE_ERC20_FACTORY_SLOT, _addresses.optimismMintableERC20Factory); 183 184 _setStartBlock(); 185 186 _setResourceConfig(_config); 187 require(_gasLimit >= minimumGasLimit(), "SystemConfig: gas limit too low"); 188 } 189 190 /// @notice Returns the minimum L2 gas limit that can be safely set for the system to 191 /// operate. The L2 gas limit must be larger than or equal to the amount of 192 /// gas that is allocated for deposits per block plus the amount of gas that 193 /// is allocated for the system transaction. 194 /// This function is used to determine if changes to parameters are safe. 195 /// @return uint64 Minimum gas limit. 196 function minimumGasLimit() public view returns (uint64) { 197 return uint64(_resourceConfig.maxResourceLimit) + uint64(_resourceConfig.systemTxMaxGas); 198 } 199 200 /// @notice High level getter for the unsafe block signer address. 201 /// Unsafe blocks can be propagated across the p2p network if they are signed by the 202 /// key corresponding to this address. 203 /// @return addr_ Address of the unsafe block signer. 204 function unsafeBlockSigner() public view returns (address addr_) { 205 addr_ = Storage.getAddress(UNSAFE_BLOCK_SIGNER_SLOT); 206 } 207 208 /// @notice Getter for the L1CrossDomainMessenger address. 209 function l1CrossDomainMessenger() external view returns (address addr_) { 210 addr_ = Storage.getAddress(L1_CROSS_DOMAIN_MESSENGER_SLOT); 211 } 212 213 /// @notice Getter for the L1ERC721Bridge address. 214 function l1ERC721Bridge() external view returns (address addr_) { 215 addr_ = Storage.getAddress(L1_ERC_721_BRIDGE_SLOT); 216 } 217 218 /// @notice Getter for the L1StandardBridge address. 219 function l1StandardBridge() external view returns (address addr_) { 220 addr_ = Storage.getAddress(L1_STANDARD_BRIDGE_SLOT); 221 } 222 223 /// @notice Getter for the L2OutputOracle address. 224 function l2OutputOracle() external view returns (address addr_) { 225 addr_ = Storage.getAddress(L2_OUTPUT_ORACLE_SLOT); 226 } 227 228 /// @notice Getter for the OptimismPortal address. 229 function optimismPortal() external view returns (address addr_) { 230 addr_ = Storage.getAddress(OPTIMISM_PORTAL_SLOT); 231 } 232 233 /// @notice Getter for the OptimismMintableERC20Factory address. 234 function optimismMintableERC20Factory() external view returns (address addr_) { 235 addr_ = Storage.getAddress(OPTIMISM_MINTABLE_ERC20_FACTORY_SLOT); 236 } 237 238 /// @notice Getter for the BatchInbox address. 239 function batchInbox() external view returns (address addr_) { 240 addr_ = Storage.getAddress(BATCH_INBOX_SLOT); 241 } 242 243 /// @notice Getter for the StartBlock number. 244 function startBlock() external view returns (uint256 startBlock_) { 245 startBlock_ = Storage.getUint(START_BLOCK_SLOT); 246 } 247 248 /// @notice Updates the unsafe block signer address. Can only be called by the owner. 249 /// @param _unsafeBlockSigner New unsafe block signer address. 250 function setUnsafeBlockSigner(address _unsafeBlockSigner) external onlyOwner { 251 _setUnsafeBlockSigner(_unsafeBlockSigner); 252 } 253 254 /// @notice Updates the unsafe block signer address. 255 /// @param _unsafeBlockSigner New unsafe block signer address. 256 function _setUnsafeBlockSigner(address _unsafeBlockSigner) internal { 257 Storage.setAddress(UNSAFE_BLOCK_SIGNER_SLOT, _unsafeBlockSigner); 258 259 bytes memory data = abi.encode(_unsafeBlockSigner); 260 emit ConfigUpdate(VERSION, UpdateType.UNSAFE_BLOCK_SIGNER, data); 261 } 262 263 /// @notice Updates the batcher hash. Can only be called by the owner. 264 /// @param _batcherHash New batcher hash. 265 function setBatcherHash(bytes32 _batcherHash) external onlyOwner { 266 _setBatcherHash(_batcherHash); 267 } 268 269 /// @notice Internal function for updating the batcher hash. 270 /// @param _batcherHash New batcher hash. 271 function _setBatcherHash(bytes32 _batcherHash) internal { 272 batcherHash = _batcherHash; 273 274 bytes memory data = abi.encode(_batcherHash); 275 emit ConfigUpdate(VERSION, UpdateType.BATCHER, data); 276 } 277 278 /// @notice Updates gas config. Can only be called by the owner. 279 /// @param _overhead New overhead value. 280 /// @param _scalar New scalar value. 281 function setGasConfig(uint256 _overhead, uint256 _scalar) external onlyOwner { 282 _setGasConfig(_overhead, _scalar); 283 } 284 285 /// @notice Internal function for updating the gas config. 286 /// @param _overhead New overhead value. 287 /// @param _scalar New scalar value. 288 function _setGasConfig(uint256 _overhead, uint256 _scalar) internal { 289 overhead = _overhead; 290 scalar = _scalar; 291 292 bytes memory data = abi.encode(_overhead, _scalar); 293 emit ConfigUpdate(VERSION, UpdateType.GAS_CONFIG, data); 294 } 295 296 /// @notice Updates the L2 gas limit. Can only be called by the owner. 297 /// @param _gasLimit New gas limit. 298 function setGasLimit(uint64 _gasLimit) external onlyOwner { 299 _setGasLimit(_gasLimit); 300 } 301 302 /// @notice Internal function for updating the L2 gas limit. 303 /// @param _gasLimit New gas limit. 304 function _setGasLimit(uint64 _gasLimit) internal { 305 require(_gasLimit >= minimumGasLimit(), "SystemConfig: gas limit too low"); 306 gasLimit = _gasLimit; 307 308 bytes memory data = abi.encode(_gasLimit); 309 emit ConfigUpdate(VERSION, UpdateType.GAS_LIMIT, data); 310 } 311 312 /// @notice Sets the start block in a backwards compatible way. Proxies 313 /// that were initialized before the startBlock existed in storage 314 /// can have their start block set by a user provided override. 315 /// A start block of 0 indicates that there is no override and the 316 /// start block will be set by `block.number`. 317 /// @dev This logic is used to patch legacy deployments with new storage values. 318 /// Use the override if it is provided as a non zero value and the value 319 /// has not already been set in storage. Use `block.number` if the value 320 /// has already been set in storage 321 function _setStartBlock() internal { 322 if (Storage.getUint(START_BLOCK_SLOT) == 0) { 323 Storage.setUint(START_BLOCK_SLOT, block.number); 324 } 325 } 326 327 /// @notice A getter for the resource config. 328 /// Ensures that the struct is returned instead of a tuple. 329 /// @return ResourceConfig 330 function resourceConfig() external view returns (ResourceMetering.ResourceConfig memory) { 331 return _resourceConfig; 332 } 333 334 /// @notice An external setter for the resource config. 335 /// In the future, this method may emit an event that the `op-node` picks up 336 /// for when the resource config is changed. 337 /// @param _config The new resource config values. 338 function setResourceConfig(ResourceMetering.ResourceConfig memory _config) external onlyOwner { 339 _setResourceConfig(_config); 340 } 341 342 /// @notice An internal setter for the resource config. 343 /// Ensures that the config is sane before storing it by checking for invariants. 344 /// @param _config The new resource config. 345 function _setResourceConfig(ResourceMetering.ResourceConfig memory _config) internal { 346 // Min base fee must be less than or equal to max base fee. 347 require( 348 _config.minimumBaseFee <= _config.maximumBaseFee, "SystemConfig: min base fee must be less than max base" 349 ); 350 // Base fee change denominator must be greater than 1. 351 require(_config.baseFeeMaxChangeDenominator > 1, "SystemConfig: denominator must be larger than 1"); 352 // Max resource limit plus system tx gas must be less than or equal to the L2 gas limit. 353 // The gas limit must be increased before these values can be increased. 354 require(_config.maxResourceLimit + _config.systemTxMaxGas <= gasLimit, "SystemConfig: gas limit too low"); 355 // Elasticity multiplier must be greater than 0. 356 require(_config.elasticityMultiplier > 0, "SystemConfig: elasticity multiplier cannot be 0"); 357 // No precision loss when computing target resource limit. 358 require( 359 ((_config.maxResourceLimit / _config.elasticityMultiplier) * _config.elasticityMultiplier) 360 == _config.maxResourceLimit, 361 "SystemConfig: precision loss with target resource limit" 362 ); 363 364 _resourceConfig = _config; 365 } 366 }