Skip to content

feat(feynman): update L1GasPriceOracle #124

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
Jun 25, 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
14 changes: 14 additions & 0 deletions src/L2/predeploys/IL1GasPriceOracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);

Expand Down
74 changes: 72 additions & 2 deletions src/L2/predeploys/L1GasPriceOracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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 *
*************/
Expand Down Expand Up @@ -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 *
*************/
Expand Down Expand Up @@ -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 *
*************/
Expand All @@ -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);
Expand All @@ -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 {
Expand Down Expand Up @@ -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.
Expand All @@ -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 *
**********************/
Expand Down Expand Up @@ -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;
}
}
53 changes: 53 additions & 0 deletions src/test/L1GasPriceOracle.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
);
}
}
Loading