Skip to content

add L2SystemContract #114

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions src/L2/L2SystemConfig.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// SPDX-License-Identifier: MIT

pragma solidity =0.8.24;

import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

contract L2SystemConfig is OwnableUpgradeable {
/**********
* Events *
**********/

/// @notice Emitted when the base fee overhead is updated.
/// @param oldBaseFeeOverhead The old base fee overhead.
/// @param newBaseFeeOverhead The new base fee overhead.
event BaseFeeOverheadUpdated(uint256 oldBaseFeeOverhead, uint256 newBaseFeeOverhead);

/// @notice Emitted when the base fee scalar is updated.
/// @param oldBaseFeeScalar The old base fee scalar.
/// @param newBaseFeeScalar The new base fee scalar.
event BaseFeeScalarUpdated(uint256 oldBaseFeeScalar, uint256 newBaseFeeScalar);

/*************
* Constants *
*************/

uint256 private constant PRECISION = 1e18;

/*********************
* Storage Variables *
*********************/

/// @notice The base fee overhead. This is part of the L2 base fee calculation.
uint256 public baseFeeOverhead;

/// @notice The base fee scalar. This is part of the L2 base fee calculation.
uint256 public baseFeeScalar;

/***************
* Constructor *
***************/

constructor() {
_disableInitializers();
}

function initialize(address _owner) external initializer {
__Ownable_init();
transferOwnership(_owner);
}

/*************************
* Public View Functions *
*************************/

/// @notice Calculates the L2 base fee based on the L1 base fee.
/// @param l1BaseFee The L1 base fee.
/// @return l2BaseFee The L2 base fee.
function getL2BaseFee(uint256 l1BaseFee) public view returns (uint256 l2BaseFee) {
l2BaseFee = (l1BaseFee * baseFeeScalar) / PRECISION + baseFeeOverhead;
}

/************************
* Restricted Functions *
************************/

/// @notice Updates the base fee overhead.
/// @param _baseFeeOverhead The new base fee overhead.
function updateBaseFeeOverhead(uint256 _baseFeeOverhead) external onlyOwner {
uint256 oldBaseFeeOverhead = baseFeeOverhead;
baseFeeOverhead = _baseFeeOverhead;
emit BaseFeeOverheadUpdated(oldBaseFeeOverhead, _baseFeeOverhead);
}

/// @notice Updates the base fee scalar.
/// @param _baseFeeScalar The new base fee scalar.
function updateBaseFeeScalar(uint256 _baseFeeScalar) external onlyOwner {
uint256 oldBaseFeeScalar = baseFeeScalar;
baseFeeScalar = _baseFeeScalar;
emit BaseFeeScalarUpdated(oldBaseFeeScalar, _baseFeeScalar);
}
}
132 changes: 132 additions & 0 deletions src/test/L2SystemConfig.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
// SPDX-License-Identifier: MIT

pragma solidity =0.8.24;

import {Test} from "forge-std/Test.sol";

import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";

import {L2SystemConfig} from "../L2/L2SystemConfig.sol";

contract L2SystemConfigTest is Test {
address public admin;
address public owner;
address public nonOwner;

L2SystemConfig public l2SystemConfig;

event BaseFeeOverheadUpdated(uint256 oldBaseFeeOverhead, uint256 newBaseFeeOverhead);
event BaseFeeScalarUpdated(uint256 oldBaseFeeScalar, uint256 newBaseFeeScalar);

function setUp() public {
admin = makeAddr("admin");
owner = makeAddr("owner");
nonOwner = makeAddr("nonOwner");

L2SystemConfig implementation = new L2SystemConfig();
address proxy = address(new TransparentUpgradeableProxy(address(implementation), admin, ""));
l2SystemConfig = L2SystemConfig(proxy);

l2SystemConfig.initialize(owner);
}

function test_Initialize() public {
// Test initialization
assertEq(l2SystemConfig.owner(), owner);

// revert when initialize again
vm.expectRevert("Initializable: contract is already initialized");
l2SystemConfig.initialize(owner);
}

function test_UpdateBaseFeeOverhead(uint256 newBaseFeeOverhead) public {
// Test that only owner can update base fee overhead
vm.prank(nonOwner);
vm.expectRevert("Ownable: caller is not the owner");
l2SystemConfig.updateBaseFeeOverhead(newBaseFeeOverhead);

// Test owner can update base fee overhead
assertEq(l2SystemConfig.baseFeeOverhead(), 0);
vm.prank(owner);
vm.expectEmit(true, true, true, true);
emit BaseFeeOverheadUpdated(0, newBaseFeeOverhead);
l2SystemConfig.updateBaseFeeOverhead(newBaseFeeOverhead);
assertEq(l2SystemConfig.baseFeeOverhead(), newBaseFeeOverhead);
}

function test_UpdateBaseFeeScalar(uint256 newBaseFeeScalar) public {
// Test that only owner can update base fee scalar
vm.prank(nonOwner);
vm.expectRevert("Ownable: caller is not the owner");
l2SystemConfig.updateBaseFeeScalar(newBaseFeeScalar);

// Test owner can update base fee scalar
assertEq(l2SystemConfig.baseFeeScalar(), 0);
vm.prank(owner);
vm.expectEmit(true, true, true, true);
emit BaseFeeScalarUpdated(0, newBaseFeeScalar);
l2SystemConfig.updateBaseFeeScalar(newBaseFeeScalar);
assertEq(l2SystemConfig.baseFeeScalar(), newBaseFeeScalar);
}

function test_GetL2BaseFee(
uint256 l1BaseFee,
uint256 baseFeeScalar,
uint256 baseFeeOverhead
) public {
l1BaseFee = bound(l1BaseFee, 0, type(uint64).max);
baseFeeScalar = bound(baseFeeScalar, 0, type(uint128).max);
baseFeeOverhead = bound(baseFeeOverhead, 0, type(uint128).max);

// Set up the contract state
vm.prank(owner);
l2SystemConfig.updateBaseFeeScalar(baseFeeScalar);
vm.prank(owner);
l2SystemConfig.updateBaseFeeOverhead(baseFeeOverhead);

// Calculate expected L2 base fee
uint256 expectedL2BaseFee = (l1BaseFee * baseFeeScalar) / 1e18 + baseFeeOverhead;

// Test getL2BaseFee function
uint256 actualL2BaseFee = l2SystemConfig.getL2BaseFee(l1BaseFee);
assertEq(actualL2BaseFee, expectedL2BaseFee);
}

function test_GetL2BaseFeeWithZeroL1BaseFee() public {
uint256 l1BaseFee = 0;
uint256 baseFeeScalar = 2000;
uint256 baseFeeOverhead = 1000;

// Set up the contract state
vm.prank(owner);
l2SystemConfig.updateBaseFeeScalar(baseFeeScalar);
vm.prank(owner);
l2SystemConfig.updateBaseFeeOverhead(baseFeeOverhead);

// Calculate expected L2 base fee
uint256 expectedL2BaseFee = baseFeeOverhead; // When L1 base fee is 0, only overhead is added

// Test getL2BaseFee function
uint256 actualL2BaseFee = l2SystemConfig.getL2BaseFee(l1BaseFee);
assertEq(actualL2BaseFee, expectedL2BaseFee);
}

function test_GetL2BaseFeeWithLargeValues() public {
uint256 l1BaseFee = 1e18; // 1 ETH
uint256 baseFeeScalar = 2e18; // 2x multiplier
uint256 baseFeeOverhead = 1e17; // 0.1 ETH

// Set up the contract state
vm.prank(owner);
l2SystemConfig.updateBaseFeeScalar(baseFeeScalar);
vm.prank(owner);
l2SystemConfig.updateBaseFeeOverhead(baseFeeOverhead);

// Calculate expected L2 base fee
uint256 expectedL2BaseFee = (l1BaseFee * baseFeeScalar) / 1e18 + baseFeeOverhead;

// Test getL2BaseFee function
uint256 actualL2BaseFee = l2SystemConfig.getL2BaseFee(l1BaseFee);
assertEq(actualL2BaseFee, expectedL2BaseFee);
}
}
Loading