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 1 commit
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
63 changes: 63 additions & 0 deletions src/L2/L2SystemContract.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// SPDX-License-Identifier: MIT

pragma solidity =0.8.24;

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

contract L2SystemContract is OwnableUpgradeable {
/*************
* 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 {
baseFeeOverhead = _baseFeeOverhead;
}

/// @notice Updates the base fee scalar.
/// @param _baseFeeScalar The new base fee scalar.
function updateBaseFeeScalar(uint256 _baseFeeScalar) external onlyOwner {
baseFeeScalar = _baseFeeScalar;
}
}
127 changes: 127 additions & 0 deletions src/test/L2SystemContract.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// 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 {L2SystemContract} from "../L2/L2SystemContract.sol";

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

L2SystemContract public l2SystemContract;

event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

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

L2SystemContract implementation = new L2SystemContract();
address proxy = address(new TransparentUpgradeableProxy(address(implementation), admin, ""));
l2SystemContract = L2SystemContract(proxy);

l2SystemContract.initialize(owner);
}

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

// revert when initialize again
vm.expectRevert("Initializable: contract is already initialized");
l2SystemContract.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");
l2SystemContract.updateBaseFeeOverhead(newBaseFeeOverhead);

// Test owner can update base fee overhead
assertEq(l2SystemContract.baseFeeOverhead(), 0);
vm.prank(owner);
l2SystemContract.updateBaseFeeOverhead(newBaseFeeOverhead);
assertEq(l2SystemContract.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");
l2SystemContract.updateBaseFeeScalar(newBaseFeeScalar);

// Test owner can update base fee scalar
assertEq(l2SystemContract.baseFeeScalar(), 0);
vm.prank(owner);
l2SystemContract.updateBaseFeeScalar(newBaseFeeScalar);
assertEq(l2SystemContract.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);
l2SystemContract.updateBaseFeeScalar(baseFeeScalar);
vm.prank(owner);
l2SystemContract.updateBaseFeeOverhead(baseFeeOverhead);

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

// Test getL2BaseFee function
uint256 actualL2BaseFee = l2SystemContract.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);
l2SystemContract.updateBaseFeeScalar(baseFeeScalar);
vm.prank(owner);
l2SystemContract.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 = l2SystemContract.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);
l2SystemContract.updateBaseFeeScalar(baseFeeScalar);
vm.prank(owner);
l2SystemContract.updateBaseFeeOverhead(baseFeeOverhead);

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

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