diff --git a/hardhat-test/EnforcedTxGateway.spec.ts b/hardhat-test/EnforcedTxGateway.spec.ts index 672a79f9..cb410c92 100644 --- a/hardhat-test/EnforcedTxGateway.spec.ts +++ b/hardhat-test/EnforcedTxGateway.spec.ts @@ -5,7 +5,7 @@ import { expect } from "chai"; import { BigNumberish, BytesLike, MaxUint256, ZeroAddress, getBytes } from "ethers"; import { ethers } from "hardhat"; -import { EnforcedTxGateway, L1MessageQueueV2, L2GasPriceOracle, MockCaller } from "../typechain"; +import { EnforcedTxGateway, L1MessageQueueV2, L2GasPriceOracle, MockCaller, SystemConfig } from "../typechain"; describe("EnforcedTxGateway.spec", async () => { let deployer: HardhatEthersSigner; @@ -13,6 +13,7 @@ describe("EnforcedTxGateway.spec", async () => { let signer: HardhatEthersSigner; let caller: MockCaller; + let system: SystemConfig; let gateway: EnforcedTxGateway; let oracle: L2GasPriceOracle; let queue: L1MessageQueueV2; @@ -37,6 +38,12 @@ describe("EnforcedTxGateway.spec", async () => { deployer ); + system = await ethers.getContractAt( + "SystemConfig", + await deployProxy("SystemConfig", await admin.getAddress(), []), + deployer + ); + const queueV1 = await ethers.getContractAt( "L1MessageQueueV1", await deployProxy("L1MessageQueueV1", await admin.getAddress(), [ @@ -54,6 +61,7 @@ describe("EnforcedTxGateway.spec", async () => { deployer.address, await gateway.getAddress(), await queueV1.getAddress(), + await system.getAddress(), ]), deployer ); @@ -67,7 +75,12 @@ describe("EnforcedTxGateway.spec", async () => { const MockCaller = await ethers.getContractFactory("MockCaller", deployer); caller = await MockCaller.deploy(); - await queue.initialize(1000000, { overhead: 123, scalar: 10n ** 18n }); + await system.initialize(deployer.address, deployer.address, { + maxGasLimit: 1000000, + baseFeeOverhead: 10n ** 9n, + baseFeeScalar: 10n ** 18n, + }); + await queue.initialize(); await gateway.initialize(queue.getAddress(), feeVault.address); await oracle.initialize(21000, 51000, 8, 16); diff --git a/scripts/foundry/DeployL1SystemConfig.s.sol b/scripts/foundry/DeployL1SystemConfig.s.sol new file mode 100644 index 00000000..1f59beda --- /dev/null +++ b/scripts/foundry/DeployL1SystemConfig.s.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.8.24; + +import {Script} from "forge-std/Script.sol"; +import {SystemConfig} from "../../src/L1/system-contract/SystemConfig.sol"; // adjust the relative path as necessary +import {console} from "forge-std/console.sol"; + +contract DeployL1SystemConfig is Script { + function run() external { + // Retrieve the deployer private key from environment variables + uint256 deployerKey = vm.envUint("L1_DEPLOYER_PRIVATE_KEY"); + + vm.startBroadcast(deployerKey); + + // Deploy the SystemConfig contract with the specified owner. + SystemConfig sysConfig = new SystemConfig(); + + console.log("Deployed SystemConfig Implementation at address:", address(sysConfig)); + + vm.stopBroadcast(); + } +} diff --git a/scripts/foundry/InitializeL1ScrollOwner.s.sol b/scripts/foundry/InitializeL1ScrollOwner.s.sol index cae7a5d2..432ad6dd 100644 --- a/scripts/foundry/InitializeL1ScrollOwner.s.sol +++ b/scripts/foundry/InitializeL1ScrollOwner.s.sol @@ -21,6 +21,7 @@ import {ScrollChain} from "../../src/L1/rollup/ScrollChain.sol"; import {ScrollOwner} from "../../src/misc/ScrollOwner.sol"; import {Whitelist} from "../../src/L2/predeploys/Whitelist.sol"; + // solhint-disable max-states-count // solhint-disable state-visibility // solhint-disable var-name-mixedcase @@ -63,6 +64,9 @@ contract InitializeL1ScrollOwner is Script { address L1_ENFORCED_TX_GATEWAY_PROXY_ADDR = vm.envAddress("L1_ENFORCED_TX_GATEWAY_PROXY_ADDR"); address L1_WHITELIST_ADDR = vm.envAddress("L1_WHITELIST_ADDR"); + address SYSTEM_CONTRACT_ADDR = vm.envAddress("SYSTEM_CONTRACT_ADDR"); + + ScrollOwner owner; function run() external { @@ -81,8 +85,8 @@ contract InitializeL1ScrollOwner is Script { configL1GatewayRouter(); configL1CustomERC20Gateway(); configL1ERC721Gateway(); - configL1ERC1155Gateway(); - + configL1ERC1155Gateway(); + configL1USDCGateway(); configEnforcedTxGateway(); diff --git a/scripts/foundry/InitializeL1SystemConfig.sol b/scripts/foundry/InitializeL1SystemConfig.sol new file mode 100644 index 00000000..c6e9c121 --- /dev/null +++ b/scripts/foundry/InitializeL1SystemConfig.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.8.24; + +import { Script } from "forge-std/Script.sol"; +import { SystemConfig } from "../../src/L1/system-contract/SystemConfig.sol"; +import { ScrollOwner } from "../../src/misc/ScrollOwner.sol"; // Adjust this path as needed + +/** + * @title InitializeL1SystemConfig + * @notice Configures the deployed SystemConfig contract. + * This script grants the Security Council (as defined by L1_SECURITY_COUNCIL_ADDR) + * access to call updateSigner() on the SystemConfig contract with no delay. + */ +contract InitializeL1SystemConfig is Script { + function run() external { + // Retrieve required environment variables. + uint256 deployerKey = vm.envUint("L1_DEPLOYER_PRIVATE_KEY"); + address systemConfigAddr = vm.envAddress("SYSTEM_CONTRACT_ADDR"); + address securityCouncilAddr = vm.envAddress("L1_SECURITY_COUNCIL_ADDR"); + address scrollOwnerAddr = vm.envAddress("L1_SCROLL_OWNER_ADDR"); + + // Compute the role hash for the Security Council with no delay. + bytes32 SECURITY_COUNCIL_NO_DELAY_ROLE = keccak256("SECURITY_COUNCIL_NO_DELAY_ROLE"); + + vm.startBroadcast(deployerKey); + + // Instantiate the ScrollOwner contract instance which manages access control. + ScrollOwner owner = ScrollOwner(payable(scrollOwnerAddr)); + // Instantiate the already-deployed SystemConfig contract. + SystemConfig sys = SystemConfig(systemConfigAddr); + + // Prepare a single-element array containing the function selector for updateSigner. + bytes4[] memory selectors = new bytes4[](1); + selectors[0] = sys.updateSigner.selector; + + // Grant the SECURITY_COUNCIL_NO_DELAY_ROLE permission on SystemConfig, + // so that the Security Council address can call updateSigner() with no delay. + owner.updateAccess( + systemConfigAddr, // Address of the SystemConfig contract. + selectors, // The function selectors (only updateSigner here). + SECURITY_COUNCIL_NO_DELAY_ROLE, + true // Grant access. + ); + + vm.stopBroadcast(); + } +} \ No newline at end of file diff --git a/src/L1/L1ScrollMessenger.sol b/src/L1/L1ScrollMessenger.sol index 52655a1d..c6bcd728 100644 --- a/src/L1/L1ScrollMessenger.sol +++ b/src/L1/L1ScrollMessenger.sol @@ -1,5 +1,4 @@ // SPDX-License-Identifier: MIT - pragma solidity =0.8.24; import {IScrollChain} from "./rollup/IScrollChain.sol"; @@ -11,6 +10,7 @@ import {IScrollMessenger} from "../libraries/IScrollMessenger.sol"; import {ScrollMessengerBase} from "../libraries/ScrollMessengerBase.sol"; import {WithdrawTrieVerifier} from "../libraries/verifier/WithdrawTrieVerifier.sol"; + import {IMessageDropCallback} from "../libraries/callbacks/IMessageDropCallback.sol"; // solhint-disable avoid-low-level-calls diff --git a/src/L1/rollup/L1MessageQueueV2.sol b/src/L1/rollup/L1MessageQueueV2.sol index 896c8756..ca9e9560 100644 --- a/src/L1/rollup/L1MessageQueueV2.sol +++ b/src/L1/rollup/L1MessageQueueV2.sol @@ -8,6 +8,8 @@ import {AddressAliasHelper} from "../../libraries/common/AddressAliasHelper.sol" import {IL1MessageQueueV1} from "./IL1MessageQueueV1.sol"; import {IL1MessageQueueV2} from "./IL1MessageQueueV2.sol"; +import {SystemConfig} from "../system-contract/SystemConfig.sol"; + // solhint-disable no-empty-blocks // solhint-disable no-inline-assembly // solhint-disable reason-string @@ -69,14 +71,8 @@ contract L1MessageQueueV2 is OwnableUpgradeable, IL1MessageQueueV2 { /// @notice The address of L1MessageQueueV1 contract. address public immutable messageQueueV1; - /*********** - * Structs * - ***********/ - - struct L2BaseFeeParameters { - uint128 overhead; - uint128 scalar; - } + /// @notice The address of `SystemConfig` contract. + address public immutable systemConfig; /********************* * Storage Variables * @@ -103,11 +99,6 @@ contract L1MessageQueueV2 is OwnableUpgradeable, IL1MessageQueueV2 { /// @inheritdoc IL1MessageQueueV2 uint256 public nextUnfinalizedQueueIndex; - /// @notice The max gas limit of L1 transactions. - uint256 public maxGasLimit; - - L2BaseFeeParameters public l2BaseFeeParameters; - /// @dev The storage slots for future usage. uint256[45] private __gap; @@ -134,7 +125,8 @@ contract L1MessageQueueV2 is OwnableUpgradeable, IL1MessageQueueV2 { address _messenger, address _scrollChain, address _enforcedTxGateway, - address _messageQueueV1 + address _messageQueueV1, + address _systemConfig ) { _disableInitializers(); @@ -142,17 +134,13 @@ contract L1MessageQueueV2 is OwnableUpgradeable, IL1MessageQueueV2 { scrollChain = _scrollChain; enforcedTxGateway = _enforcedTxGateway; messageQueueV1 = _messageQueueV1; + systemConfig = _systemConfig; } /// @notice Initialize the storage of L1MessageQueue. - /// - /// @param _maxGasLimit The maximum gas limit allowed in single transaction. - function initialize(uint256 _maxGasLimit, L2BaseFeeParameters memory _params) external initializer { + function initialize() external initializer { OwnableUpgradeable.__Ownable_init(); - _updateL2BaseFeeParameters(_params.overhead, _params.scalar); - _updateMaxGasLimit(_maxGasLimit); - uint256 _nextCrossDomainMessageIndex = IL1MessageQueueV1(messageQueueV1).nextCrossDomainMessageIndex(); nextCrossDomainMessageIndex = _nextCrossDomainMessageIndex; nextUnfinalizedQueueIndex = _nextCrossDomainMessageIndex; @@ -169,10 +157,10 @@ contract L1MessageQueueV2 is OwnableUpgradeable, IL1MessageQueueV2 { /// @inheritdoc IL1MessageQueueV2 function estimatedL2BaseFee() public view returns (uint256) { - L2BaseFeeParameters memory parameters = l2BaseFeeParameters; + (, uint256 overhead, uint256 scalar) = SystemConfig(systemConfig).messageQueueParameters(); // this is unlikely to happen, use unchecked here unchecked { - return (block.basefee * parameters.scalar) / PRECISION + parameters.overhead; + return (block.basefee * scalar) / PRECISION + overhead; } } @@ -372,47 +360,10 @@ contract L1MessageQueueV2 is OwnableUpgradeable, IL1MessageQueueV2 { } } - /************************ - * Restricted Functions * - ************************/ - - /// @notice Update the parameters for l2 base fee formula. - /// @param overhead The value of overhead in l2 base fee formula. - /// @param scalar The value of scalar in l2 base fee formula. - function updateL2BaseFeeParameters(uint128 overhead, uint128 scalar) external onlyOwner { - _updateL2BaseFeeParameters(overhead, scalar); - } - - /// @notice Update the max gas limit. - /// @param _newMaxGasLimit The new max gas limit. - function updateMaxGasLimit(uint256 _newMaxGasLimit) external onlyOwner { - _updateMaxGasLimit(_newMaxGasLimit); - } - /********************** * Internal Functions * **********************/ - /// @dev Internal function to update the parameters for l2 base fee formula. - /// @param overhead The value of overhead in l2 base fee formula. - /// @param scalar The value of scalar in l2 base fee formula. - function _updateL2BaseFeeParameters(uint128 overhead, uint128 scalar) internal { - l2BaseFeeParameters = L2BaseFeeParameters(overhead, scalar); - - emit UpdateL2BaseFeeParameters(overhead, scalar); - } - - /// @dev Internal function to update the max gas limit. - /// @param _newMaxGasLimit The new max gas limit. - function _updateMaxGasLimit(uint256 _newMaxGasLimit) internal { - if (_newMaxGasLimit < INTRINSIC_GAS_TX) revert ErrorGasLimitBelowIntrinsicGas(); - - uint256 _oldMaxGasLimit = maxGasLimit; - maxGasLimit = _newMaxGasLimit; - - emit UpdateMaxGasLimit(_oldMaxGasLimit, _newMaxGasLimit); - } - /// @dev Internal function to queue a L1 transaction. /// @param _sender The address of sender who will initiate this transaction in L2. /// @param _target The address of target contract to call in L2. @@ -444,6 +395,7 @@ contract L1MessageQueueV2 is OwnableUpgradeable, IL1MessageQueueV2 { /// @param _gasLimit The value of given gas limit. /// @param _calldata The calldata for this message. function _validateGasLimit(uint256 _gasLimit, bytes calldata _calldata) internal view { + (uint256 maxGasLimit, , ) = SystemConfig(systemConfig).messageQueueParameters(); if (_gasLimit > maxGasLimit) revert ErrorGasLimitExceeded(); // check if the gas limit is above intrinsic gas uint256 intrinsicGas = calculateIntrinsicGasFee(_calldata); diff --git a/src/L1/system-contract/SystemConfig.sol b/src/L1/system-contract/SystemConfig.sol new file mode 100644 index 00000000..1251aa5a --- /dev/null +++ b/src/L1/system-contract/SystemConfig.sol @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: MIT +pragma solidity =0.8.24; + +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; + +contract SystemConfig is OwnableUpgradeable { + /*********** + * Structs * + ***********/ + + /// @param maxGasLimit The maximum gas limit allowed for each L1 message. + /// @param baseFeeOverhead The overhead used to calculate l2 base fee. + /// @param baseFeeScalar The scalar used to calculate l2 base fee. + struct MessageQueueParameters { + uint32 maxGasLimit; + uint112 baseFeeOverhead; + uint112 baseFeeScalar; + } + + /********************* + * Storage Variables * + *********************/ + + /// @notice The parameters for message queue. + MessageQueueParameters public messageQueueParameters; + + /// @dev The address of current authorized signer. + address private currentSigner; + + /*************** + * Constructor * + ***************/ + + constructor() { + _disableInitializers(); + } + + function initialize( + address _owner, + address _signer, + MessageQueueParameters memory _params + ) external initializer { + __Ownable_init(); + transferOwnership(_owner); + + currentSigner = _signer; + messageQueueParameters = _params; + } + + /************************* + * Public View Functions * + *************************/ + + /// @notice Return the current authorized signer. + /// @return The authorized signer address. + function getSigner() external view returns (address) { + return currentSigner; + } + + /************************ + * Restricted Functions * + ************************/ + + /// @notice Update the message queue parameters. + /// @param _params The new message queue parameters. + function updateMessageQueueParameters(MessageQueueParameters memory _params) external onlyOwner { + messageQueueParameters = _params; + } + + /// @notice Update the current signer. + /// @dev Only the owner can call this function. + /// @param _newSigner The address of the new authorized signer. + function updateSigner(address _newSigner) external onlyOwner { + currentSigner = _newSigner; + } +}