diff --git a/src/L2/predeploys/IL1GasPriceOracle.sol b/src/L2/predeploys/IL1GasPriceOracle.sol index 850a178a..114903e0 100644 --- a/src/L2/predeploys/IL1GasPriceOracle.sol +++ b/src/L2/predeploys/IL1GasPriceOracle.sol @@ -23,6 +23,14 @@ interface IL1GasPriceOracle { /// @param scalar The current blob fee scalar updated. event BlobScalarUpdated(uint256 scalar); + /// @notice Emitted when current compression penalty threshold is updated. + /// @param threshold The new compression penalty threshold. + event PenaltyThresholdUpdated(uint256 threshold); + + /// @notice Emitted when current compression penalty factor is updated. + /// @param factor The new compression penalty factor. + event PenaltyFactorUpdated(uint256 factor); + /// @notice Emitted when current l1 base fee is updated. /// @param l1BaseFee The current l1 base fee updated. event L1BaseFeeUpdated(uint256 l1BaseFee); @@ -47,6 +55,12 @@ interface IL1GasPriceOracle { /// @notice Return the current l1 blob fee scalar. function blobScalar() external view returns (uint256); + /// @notice Return the current compression penalty threshold. + function penaltyThreshold() external view returns (uint256); + + /// @notice Return the current compression penalty factor. + function penaltyFactor() external view returns (uint256); + /// @notice Return the latest known l1 base fee. function l1BaseFee() external view returns (uint256); diff --git a/src/L2/predeploys/L1GasPriceOracle.sol b/src/L2/predeploys/L1GasPriceOracle.sol index 6d535328..28a03792 100644 --- a/src/L2/predeploys/L1GasPriceOracle.sol +++ b/src/L2/predeploys/L1GasPriceOracle.sol @@ -39,6 +39,17 @@ contract L1GasPriceOracle is OwnableBase, IL1GasPriceOracle { /// @dev Thrown when we enable Curie fork after Curie fork. error ErrAlreadyInCurieFork(); + /// @dev Thrown when the compression penalty threshold exceeds `MAX_PENALTY_THRESHOLD`, + /// or is less than 1 * PRECISION. + error ErrInvalidPenaltyThreshold(); + + /// @dev Thrown when the compression penalty factor exceeds `MAX_PENALTY_FACTOR`, + /// or is less than 1 * PRECISION. + error ErrInvalidPenaltyFactor(); + + /// @dev Thrown when we enable Feynman fork after Feynman fork. + error ErrAlreadyInFeynmanFork(); + /************* * Constants * *************/ @@ -70,6 +81,14 @@ contract L1GasPriceOracle is OwnableBase, IL1GasPriceOracle { /// So, the value should not exceed 10^9 * 1e9 normally. uint256 private constant MAX_BLOB_SCALAR = 10**9 * PRECISION; + /// @dev The maximum possible compression penalty threshold after Feynman. + /// The value should not exceed 10^9 * 1e9 normally. + uint256 private constant MAX_PENALTY_THRESHOLD = 10**9 * PRECISION; + + /// @dev The maximum possible compression penalty factor after Feynman. + /// The value should not exceed 10^9 * 1e9 normally. + uint256 private constant MAX_PENALTY_FACTOR = 10**9 * PRECISION; + /************* * Variables * *************/ @@ -98,6 +117,15 @@ contract L1GasPriceOracle is OwnableBase, IL1GasPriceOracle { /// @notice Indicates whether the network has gone through the Curie upgrade. bool public isCurie; + /// @inheritdoc IL1GasPriceOracle + uint256 public override penaltyThreshold; + + /// @inheritdoc IL1GasPriceOracle + uint256 public override penaltyFactor; + + /// @notice Indicates whether the network has gone through the Feynman upgrade. + bool public isFeynman; + /************* * Modifiers * *************/ @@ -121,7 +149,9 @@ contract L1GasPriceOracle is OwnableBase, IL1GasPriceOracle { /// @inheritdoc IL1GasPriceOracle function getL1Fee(bytes memory _data) external view override returns (uint256) { - if (isCurie) { + if (isFeynman) { + return _getL1FeeFeynman(_data); + } else if (isCurie) { return _getL1FeeCurie(_data); } else { return _getL1FeeBeforeCurie(_data); @@ -130,7 +160,7 @@ contract L1GasPriceOracle is OwnableBase, IL1GasPriceOracle { /// @inheritdoc IL1GasPriceOracle function getL1GasUsed(bytes memory _data) public view override returns (uint256) { - if (isCurie) { + if (isFeynman || isCurie) { // It is near zero since we put all transactions to blob. return 0; } else { @@ -202,6 +232,24 @@ contract L1GasPriceOracle is OwnableBase, IL1GasPriceOracle { emit BlobScalarUpdated(_scalar); } + /// Allows the owner to modify the penaltyThreshold. + /// @param _threshold New threshold + function setPenaltyThreshold(uint256 _threshold) external onlyOwner { + if (_threshold < PRECISION || _threshold > MAX_PENALTY_THRESHOLD) revert ErrInvalidPenaltyThreshold(); + + penaltyThreshold = _threshold; + emit PenaltyThresholdUpdated(_threshold); + } + + /// Allows the owner to modify the penaltyFactor. + /// @param _factor New factor + function setPenaltyFactor(uint256 _factor) external onlyOwner { + if (_factor < PRECISION || _factor > MAX_PENALTY_FACTOR) revert ErrInvalidPenaltyFactor(); + + penaltyFactor = _factor; + emit PenaltyFactorUpdated(_factor); + } + /// @notice Update whitelist contract. /// @dev This function can only called by contract owner. /// @param _newWhitelist The address of new whitelist contract. @@ -222,6 +270,16 @@ contract L1GasPriceOracle is OwnableBase, IL1GasPriceOracle { isCurie = true; } + /// @notice Enable the Feynman fork (callable by contract owner). + /// + /// @dev Since this is a predeploy contract, we will directly set the slot while hard fork + /// to avoid external owner operations. + /// The reason that we keep this function is for easy unit testing. + function enableFeynman() external onlyOwner { + if (isFeynman) revert ErrAlreadyInFeynmanFork(); + isFeynman = true; + } + /********************** * Internal Functions * **********************/ @@ -264,4 +322,16 @@ contract L1GasPriceOracle is OwnableBase, IL1GasPriceOracle { // We have bounded the value of `commitScalar` and `blobScalar`, the whole expression won't overflow. return (commitScalar * l1BaseFee + blobScalar * _data.length * l1BlobBaseFee) / PRECISION; } + + /// @dev Internal function to compute the L1 portion of the fee based on the size of the rlp encoded input + /// transaction, the current L1 base fee, and the various dynamic parameters, after the Feynman fork. + /// @param _data Signed fully RLP-encoded transaction to get the L1 fee for. + /// @return L1 fee that should be paid for the tx + function _getL1FeeFeynman(bytes memory _data) private view returns (uint256) { + // We have bounded the value of `commitScalar`, `blobScalar`, and `penalty`, the whole expression won't overflow. + return + ((commitScalar * l1BaseFee + blobScalar * l1BlobBaseFee) * _data.length * penaltyFactor) / + PRECISION / + PRECISION; + } } diff --git a/src/test/L1GasPriceOracle.t.sol b/src/test/L1GasPriceOracle.t.sol index 3d845ad9..7dff1cfb 100644 --- a/src/test/L1GasPriceOracle.t.sol +++ b/src/test/L1GasPriceOracle.t.sol @@ -135,6 +135,23 @@ contract L1GasPriceOracleTest is DSTestPlus { oracle.enableCurie(); } + function testEnableFeynman() external { + // call by non-owner, should revert + hevm.startPrank(address(1)); + hevm.expectRevert("caller is not the owner"); + oracle.enableFeynman(); + hevm.stopPrank(); + + // call by owner, should succeed + assertBoolEq(oracle.isFeynman(), false); + oracle.enableFeynman(); + assertBoolEq(oracle.isFeynman(), true); + + // enable twice, should revert + hevm.expectRevert(L1GasPriceOracle.ErrAlreadyInFeynmanFork.selector); + oracle.enableFeynman(); + } + function testSetL1BaseFee(uint256 _baseFee) external { _baseFee = bound(_baseFee, 0, 1e9 * 20000); // max 20k gwei @@ -232,4 +249,40 @@ contract L1GasPriceOracleTest is DSTestPlus { (_commitScalar * _baseFee + _blobScalar * _blobBaseFee * _data.length) / PRECISION ); } + + function testGetL1GasUsedFeynman(bytes memory _data) external { + oracle.enableFeynman(); + assertEq(oracle.getL1GasUsed(_data), 0); + } + + function testGetL1FeeFeynman( + uint256 _baseFee, + uint256 _blobBaseFee, + uint256 _commitScalar, + uint256 _blobScalar, + uint256 _penaltyThreshold, + uint256 _penaltyFactor, + bytes memory _data + ) external { + _baseFee = bound(_baseFee, 0, 1e9 * 20000); // max 20k gwei + _blobBaseFee = bound(_blobBaseFee, 0, 1e9 * 20000); // max 20k gwei + _commitScalar = bound(_commitScalar, 0, MAX_COMMIT_SCALAR); + _blobScalar = bound(_blobScalar, 0, MAX_BLOB_SCALAR); + _penaltyThreshold = bound(_penaltyThreshold, 1e9, 1e9 * 5); + _penaltyFactor = bound(_penaltyFactor, 1e9, 1e9 * 10); // min 1x, max 10x penalty + + oracle.enableFeynman(); + oracle.setCommitScalar(_commitScalar); + oracle.setBlobScalar(_blobScalar); + oracle.setL1BaseFeeAndBlobBaseFee(_baseFee, _blobBaseFee); + oracle.setPenaltyThreshold(_penaltyThreshold); + oracle.setPenaltyFactor(_penaltyFactor); + + assertEq( + oracle.getL1Fee(_data), + ((_commitScalar * _baseFee + _blobScalar * _blobBaseFee) * _data.length * _penaltyFactor) / + PRECISION / + PRECISION + ); + } }