diff --git a/packages/protocol/contracts/L2/Lib1559Math.sol b/packages/protocol/contracts/L2/Lib1559Math.sol index 2bbd80f519..8904ad8512 100644 --- a/packages/protocol/contracts/L2/Lib1559Math.sol +++ b/packages/protocol/contracts/L2/Lib1559Math.sol @@ -17,6 +17,7 @@ library Lib1559Math { function calc1559BaseFee( uint32 _gasTargetPerL1Block, uint8 _adjustmentQuotient, + uint64 _gasExcessMinValue, uint64 _gasExcess, uint64 _gasIssuance, uint32 _parentGasUsed @@ -29,7 +30,7 @@ library Lib1559Math { // value as this has already happened uint256 excess = uint256(_gasExcess) + _parentGasUsed; excess = excess > _gasIssuance ? excess - _gasIssuance : 1; - gasExcess_ = uint64(excess.min(type(uint64).max)); + gasExcess_ = uint64(excess.min(type(uint64).max).max(_gasExcessMinValue)); // The base fee per gas used by this block is the spot price at the // bonding curve, regardless the actual amount of gas used by this @@ -56,6 +57,8 @@ library Lib1559Math { revert EIP1559_INVALID_PARAMS(); } + // Note that `/ _adjustmentFactor` can be removed which will make basefee increment faster. + // Alternatively, a larger initial value can be used for `_gasExcess`. return _ethQty(_gasExcess, _adjustmentFactor) / LibFixedPointMath.SCALING_FACTOR / _adjustmentFactor; } diff --git a/packages/protocol/contracts/L2/LibL2Config.sol b/packages/protocol/contracts/L2/LibL2Config.sol new file mode 100644 index 0000000000..754dfeed32 --- /dev/null +++ b/packages/protocol/contracts/L2/LibL2Config.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +/// @title LibL2Config +library LibL2Config { + struct Config { + uint32 gasTargetPerL1Block; + uint8 basefeeAdjustmentQuotient; + uint64 gasExcessMinValue; + } + + /// @notice Returns EIP1559 related configurations. + /// @return config_ struct containing configuration parameters. + function get() internal pure returns (Config memory config_) { + // Assuming we sell 3x more blockspace than Ethereum: 15_000_000 * 4 + // Note that Brecht's concern is that this value may be too large. + // We need to monitor L2 state growth and lower this value when necessary. + config_.gasTargetPerL1Block = 60_000_000; + config_.basefeeAdjustmentQuotient = 8; + + // This value is picked to make the min base fee close to but slightly smaller than 0.1gwei + config_.gasExcessMinValue = 18_435_000_000; + } +} diff --git a/packages/protocol/contracts/L2/TaikoL2.sol b/packages/protocol/contracts/L2/TaikoL2.sol index eb0c7e09f4..94b137d8ac 100644 --- a/packages/protocol/contracts/L2/TaikoL2.sol +++ b/packages/protocol/contracts/L2/TaikoL2.sol @@ -9,6 +9,7 @@ import "../libs/LibAddress.sol"; import "../signal/ISignalService.sol"; import "../signal/LibSignals.sol"; import "./Lib1559Math.sol"; +import "./LibL2Config.sol"; /// @title TaikoL2 /// @notice Taiko L2 is a smart contract that handles cross-layer message @@ -21,11 +22,6 @@ contract TaikoL2 is EssentialContract { using LibAddress for address; using SafeERC20 for IERC20; - struct Config { - uint32 gasTargetPerL1Block; - uint8 basefeeAdjustmentQuotient; - } - /// @notice Golden touch address is the only address that can do the anchor transaction. address public constant GOLDEN_TOUCH_ADDRESS = 0x0000777735367b36bC9B61C50022d9D0700dB4Ec; @@ -208,12 +204,13 @@ contract TaikoL2 is EssentialContract { view returns (uint256 basefee_, uint64 gasExcess_) { - Config memory config = getConfig(); + LibL2Config.Config memory config = getConfig(); uint64 gasIssuance = uint64(_l1BlockId - lastSyncedBlock) * config.gasTargetPerL1Block; (basefee_, gasExcess_) = Lib1559Math.calc1559BaseFee( config.gasTargetPerL1Block, config.basefeeAdjustmentQuotient, + config.gasExcessMinValue, gasExcess, gasIssuance, _parentGasUsed @@ -232,12 +229,8 @@ contract TaikoL2 is EssentialContract { /// @notice Returns EIP1559 related configurations. /// @return config_ struct containing configuration parameters. - function getConfig() public view virtual returns (Config memory config_) { - // Assuming we sell 3x more blockspace than Ethereum: 15_000_000 * 4 - // Note that Brecht's concern is that this value may be too large. - // We need to monitor L2 state growth and lower this value when necessary. - config_.gasTargetPerL1Block = 60_000_000; - config_.basefeeAdjustmentQuotient = 8; + function getConfig() public view virtual returns (LibL2Config.Config memory) { + return LibL2Config.get(); } /// @notice Tells if we need to validate basefee (for simulation). diff --git a/packages/protocol/genesis/test_config.js b/packages/protocol/genesis/test_config.js index ccbee737f2..c0a90698ed 100644 --- a/packages/protocol/genesis/test_config.js +++ b/packages/protocol/genesis/test_config.js @@ -111,7 +111,7 @@ module.exports = { }; }, param1559: { - gasExcess: 1, + gasExcess: 20000000000, }, predeployERC20: true, }; diff --git a/packages/protocol/test/L2/Lib1559Math.t.sol b/packages/protocol/test/L2/Lib1559Math.t.sol index a4edccf67a..3dd5743258 100644 --- a/packages/protocol/test/L2/Lib1559Math.t.sol +++ b/packages/protocol/test/L2/Lib1559Math.t.sol @@ -7,30 +7,33 @@ contract TestLib1559Math is TaikoTest { using LibMath for uint256; function test_eip1559_math() external { - uint256 gasTarget = 15 * 1e6 * 10; - uint256 adjustmentQuotient = 8; - uint256 adjustmentFactor = gasTarget * adjustmentQuotient; - // The expected values are calculated in eip1559_util.py - _assertAmostEq( - 999_999_916, - Lib1559Math.basefee({ _gasExcess: 49_954_623_777, _adjustmentFactor: adjustmentFactor }) - ); - - _assertAmostEq( - 48_246_703_821_869_050_543_408_253_349_256_099_602_613_005_189_120, - Lib1559Math.basefee({ - _gasExcess: LibFixedPointMath.MAX_EXP_INPUT * adjustmentFactor - / LibFixedPointMath.SCALING_FACTOR, - _adjustmentFactor: adjustmentFactor - }) - ); - } + LibL2Config.Config memory config = LibL2Config.get(); + uint256 adjustmentFactor = config.gasTargetPerL1Block * config.basefeeAdjustmentQuotient; + + uint256 baseFee; + uint256 i; + + baseFee = Lib1559Math.basefee(config.gasExcessMinValue, adjustmentFactor); + assertEq(baseFee, 99_627_953); // slightly smaller than 0.1gwei + console2.log("gasExcessMinValue:", config.gasExcessMinValue); + console2.log("min base fee:", baseFee); + + for (; baseFee < 1 gwei; ++i) { + baseFee = Lib1559Math.basefee(config.gasTargetPerL1Block * i, adjustmentFactor); + console2.log("base fee:", i, baseFee); + } + + // base fee will reach 1 gwei if gasExcess > 19620000000 + console2.log("base fee will reach 1 gwei if gasExcess >", config.gasTargetPerL1Block * i); + assertEq(i, 327); + + for (; baseFee < 10 gwei; ++i) { + baseFee = Lib1559Math.basefee(config.gasTargetPerL1Block * i, adjustmentFactor); + console2.log("base fee:", i, baseFee); + } - // Assert the different between two number is smaller than 1/1000000 - function _assertAmostEq(uint256 _a, uint256 _b) private { - uint256 min = _a.min(_b); - uint256 max = _a.max(_b); - assertTrue(max > 0 && ((max - min) * 1_000_000) / max <= 1); - console2.log(_a, " <> ", _b); + // base fee will reach 10 gwei if gasExcess > 20760000000 + console2.log("base fee will reach 10 gwei if gasExcess >", config.gasTargetPerL1Block * i); + assertEq(i, 346); } } diff --git a/packages/protocol/test/L2/TaikoL2.t.sol b/packages/protocol/test/L2/TaikoL2.t.sol index fe946766ae..7a3971ec92 100644 --- a/packages/protocol/test/L2/TaikoL2.t.sol +++ b/packages/protocol/test/L2/TaikoL2.t.sol @@ -55,7 +55,9 @@ contract TestTaikoL2 is TaikoTest { ) ); - L2.setConfigAndExcess(TaikoL2.Config(gasTarget, quotient), gasExcess); + L2.setConfigAndExcess( + LibL2Config.Config(gasTarget, quotient, uint64(gasTarget) * 300), gasExcess + ); ss.authorize(address(L2), true); @@ -67,46 +69,9 @@ contract TestTaikoL2 is TaikoTest { vm.deal(address(L2), 100 ether); } - function test_L2_AnchorTx_with_constant_block_time() external { - for (uint256 i; i < 100; ++i) { - vm.fee(1); - - vm.prank(L2.GOLDEN_TOUCH_ADDRESS()); - _anchor(BLOCK_GAS_LIMIT); - - vm.roll(block.number + 1); - vm.warp(block.timestamp + 30); - } - } - - function test_L2_AnchorTx_with_decreasing_block_time() external { - for (uint256 i; i < 32; ++i) { - vm.fee(1); - - vm.prank(L2.GOLDEN_TOUCH_ADDRESS()); - _anchor(BLOCK_GAS_LIMIT); - - vm.roll(block.number + 1); - vm.warp(block.timestamp + 30 - i); - } - } - - function test_L2_AnchorTx_with_increasing_block_time() external { - for (uint256 i; i < 30; ++i) { - vm.fee(1); - - vm.prank(L2.GOLDEN_TOUCH_ADDRESS()); - _anchor(BLOCK_GAS_LIMIT); - - vm.roll(block.number + 1); - - vm.warp(block.timestamp + 30 + i); - } - } - // calling anchor in the same block more than once should fail function test_L2_AnchorTx_revert_in_same_block() external { - vm.fee(1); + vm.fee(40_253_331); vm.prank(L2.GOLDEN_TOUCH_ADDRESS()); _anchor(BLOCK_GAS_LIMIT); diff --git a/packages/protocol/contracts/L2/TaikoL2EIP1559Configurable.sol b/packages/protocol/test/L2/TaikoL2EIP1559Configurable.sol similarity index 80% rename from packages/protocol/contracts/L2/TaikoL2EIP1559Configurable.sol rename to packages/protocol/test/L2/TaikoL2EIP1559Configurable.sol index 1ae90719fd..69661686af 100644 --- a/packages/protocol/contracts/L2/TaikoL2EIP1559Configurable.sol +++ b/packages/protocol/test/L2/TaikoL2EIP1559Configurable.sol @@ -1,21 +1,21 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.24; -import "./TaikoL2.sol"; +import "../../contracts/L2/TaikoL2.sol"; /// @title TaikoL2EIP1559Configurable /// @notice TaikoL2 with a setter to change EIP-1559 configurations and states. /// @custom:security-contact security@taiko.xyz contract TaikoL2EIP1559Configurable is TaikoL2 { /// @notice EIP-1559 configuration. - Config public customConfig; + LibL2Config.Config public customConfig; uint256[49] private __gap; /// @notice Emits when the EIP-1559 configuration and gas excess are changed. /// @param config The new EIP-1559 config. /// @param gasExcess The new gas excess. - event ConfigAndExcessChanged(Config config, uint64 gasExcess); + event ConfigAndExcessChanged(LibL2Config.Config config, uint64 gasExcess); error L2_INVALID_CONFIG(); @@ -23,7 +23,7 @@ contract TaikoL2EIP1559Configurable is TaikoL2 { /// @param _newConfig The new EIP1559 config. /// @param _newGasExcess The new gas excess function setConfigAndExcess( - Config memory _newConfig, + LibL2Config.Config memory _newConfig, uint64 _newGasExcess ) external @@ -40,7 +40,7 @@ contract TaikoL2EIP1559Configurable is TaikoL2 { } /// @inheritdoc TaikoL2 - function getConfig() public view override returns (Config memory) { + function getConfig() public view override returns (LibL2Config.Config memory) { return customConfig; } } diff --git a/packages/protocol/test/L2/TaikoL2NoFeeCheck.t.sol b/packages/protocol/test/L2/TaikoL2NoFeeCheck.t.sol index 0caba175d2..9bdd5c3b74 100644 --- a/packages/protocol/test/L2/TaikoL2NoFeeCheck.t.sol +++ b/packages/protocol/test/L2/TaikoL2NoFeeCheck.t.sol @@ -55,7 +55,9 @@ contract TestTaikoL2NoFeeCheck is TaikoTest { ) ); - L2.setConfigAndExcess(TaikoL2.Config(gasTarget, quotient), gasExcess); + L2.setConfigAndExcess( + LibL2Config.Config(gasTarget, quotient, uint64(gasTarget) * 300), gasExcess + ); ss.authorize(address(L2), true); diff --git a/packages/protocol/test/TaikoTest.sol b/packages/protocol/test/TaikoTest.sol index 3e98afd532..e30ada7853 100644 --- a/packages/protocol/test/TaikoTest.sol +++ b/packages/protocol/test/TaikoTest.sol @@ -31,7 +31,6 @@ import "../contracts/L1/hooks/AssignmentHook.sol"; import "../contracts/L1/provers/GuardianProver.sol"; import "../contracts/L2/Lib1559Math.sol"; -import "../contracts/L2/TaikoL2EIP1559Configurable.sol"; import "../contracts/L2/TaikoL2.sol"; import "../contracts/L2/DelegateOwner.sol"; @@ -41,6 +40,7 @@ import "../contracts/team/airdrop/ERC20Airdrop2.sol"; import "../contracts/team/airdrop/ERC721Airdrop.sol"; import "../test/common/erc20/FreeMintERC20.sol"; +import "../test/L2/TaikoL2EIP1559Configurable.sol"; import "./DeployCapability.sol"; import "./HelperContracts.sol";