Skip to content
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
32 changes: 29 additions & 3 deletions contracts/src/L2/predeploys/IL1GasPriceOracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,34 +15,55 @@ interface IL1GasPriceOracle {
/// @param scalar The current fee scalar updated.
event ScalarUpdated(uint256 scalar);

/// @notice Emitted when current commit fee scalar is updated.
/// @param scalar The current commit fee scalar updated.
event CommitScalarUpdated(uint256 scalar);

/// @notice Emitted when current blob fee scalar is updated.
/// @param scalar The current blob fee scalar updated.
event BlobScalarUpdated(uint256 scalar);

/// @notice Emitted when current l1 base fee is updated.
/// @param l1BaseFee The current l1 base fee updated.
event L1BaseFeeUpdated(uint256 l1BaseFee);

/// @notice Emitted when current l1 blob base fee is updated.
/// @param l1BlobBaseFee The current l1 blob base fee updated.
event L1BlobBaseFeeUpdated(uint256 l1BlobBaseFee);

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

/// @notice Return the current l1 fee overhead.
function overhead() external view returns (uint256);

/// @notice Return the current l1 fee scalar.
/// @notice Return the current l1 fee scalar before Curie fork.
function scalar() external view returns (uint256);

/// @notice Return the current l1 commit fee scalar.
function commitScalar() external view returns (uint256);

/// @notice Return the current l1 blob fee scalar.
function blobScalar() external view returns (uint256);

/// @notice Return the latest known l1 base fee.
function l1BaseFee() external view returns (uint256);

/// @notice Return the latest known l1 blob base fee.
function l1BlobBaseFee() external view returns (uint256);

/// @notice Computes 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.
/// @param data Unsigned fully RLP-encoded transaction to get the L1 fee for.
/// @param data Signed fully RLP-encoded transaction to get the L1 fee for.
/// @return L1 fee that should be paid for the tx
function getL1Fee(bytes memory data) external view returns (uint256);

/// @notice Computes the amount of L1 gas used for a transaction. Adds the overhead which
/// represents the per-transaction gas overhead of posting the transaction and state
/// roots to L1. Adds 74 bytes of padding to account for the fact that the input does
/// not have a signature.
/// @param data Unsigned fully RLP-encoded transaction to get the L1 gas for.
/// @param data Signed fully RLP-encoded transaction to get the L1 gas for.
/// @return Amount of L1 gas used to publish the transaction.
function getL1GasUsed(bytes memory data) external view returns (uint256);

Expand All @@ -53,4 +74,9 @@ interface IL1GasPriceOracle {
/// @notice Allows whitelisted caller to modify the l1 base fee.
/// @param _l1BaseFee New l1 base fee.
function setL1BaseFee(uint256 _l1BaseFee) external;

/// @notice Allows whitelisted caller to modify the l1 base fee.
/// @param _l1BaseFee New l1 base fee.
/// @param _l1BlobBaseFee New l1 blob base fee.
function setL1BaseFeeAndBlobBaseFee(uint256 _l1BaseFee, uint256 _l1BlobBaseFee) external;
}
178 changes: 156 additions & 22 deletions contracts/src/L2/predeploys/L1GasPriceOracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,28 @@ contract L1GasPriceOracle is OwnableBase, IL1GasPriceOracle {
/// @param _newWhitelist The address of new whitelist contract.
event UpdateWhitelist(address _oldWhitelist, address _newWhitelist);

/**********
* Errors *
**********/

/// @dev Thrown when the blob fee scalar exceed `MAX_BLOB_SCALAR`.
error ErrExceedMaxBlobScalar();

/// @dev Thrown when the commit fee scalar exceed `MAX_COMMIT_SCALAR`.
error ErrExceedMaxCommitScalar();

/// @dev Thrown when the l1 fee overhead exceed `MAX_OVERHEAD`.
error ErrExceedMaxOverhead();

/// @dev Thrown when the l1 fee scalar exceed `MAX_SCALAR`.
error ErrExceedMaxScalar();

/// @dev Thrown when the caller is not whitelisted.
error ErrCallerNotWhitelisted();

/// @dev Thrown when we enable Curie fork after Curie fork.
error ErrAlreadyInCurieFork();

/*************
* Constants *
*************/
Expand All @@ -28,9 +50,25 @@ contract L1GasPriceOracle is OwnableBase, IL1GasPriceOracle {
/// Computed based on current l1 block gas limit.
uint256 private constant MAX_OVERHEAD = 30000000 / 16;

/// @dev The maximum possible l1 fee scale.
/// @dev The maximum possible l1 fee scale before Curie.
/// x1000 should be enough.
uint256 private constant MAX_SCALE = 1000 * PRECISION;
uint256 private constant MAX_SCALAR = 1000 * PRECISION;

/// @dev The maximum possible l1 commit fee scalar after Curie.
/// We derive the commit scalar by
/// ```
/// commit_scalar = commit_gas_per_tx * fluctuation_multiplier * 1e9
/// ```
/// So, the value should not exceed 10^18 * 1e9 normally.
uint256 private constant MAX_COMMIT_SCALAR = 10 ** 18 * PRECISION;

/// @dev The maximum possible l1 blob fee scalar after Curie.
/// We derive the blob scalar by
/// ```
/// blob_scalar = fluctuation_multiplier / compression_ratio / blob_util_ratio * 1e9
/// ```
/// So, the value should not exceed 10^18 * 1e9 normally.
uint256 private constant MAX_BLOB_SCALAR = 10 ** 18 * PRECISION;

/*************
* Variables *
Expand All @@ -48,6 +86,27 @@ contract L1GasPriceOracle is OwnableBase, IL1GasPriceOracle {
/// @notice The address of whitelist contract.
IWhitelist public whitelist;

/// @inheritdoc IL1GasPriceOracle
uint256 public override l1BlobBaseFee;

/// @inheritdoc IL1GasPriceOracle
uint256 public override commitScalar;

/// @inheritdoc IL1GasPriceOracle
uint256 public override blobScalar;

/// @notice Indicates whether the network has gone through the Curie upgrade.
bool public isCurie;

/*************
* Modifiers *
*************/

modifier onlyWhitelistedSender() {
if (!whitelist.isSenderAllowed(msg.sender)) revert ErrCallerNotWhitelisted();
_;
}

/***************
* Constructor *
***************/
Expand All @@ -62,26 +121,20 @@ contract L1GasPriceOracle is OwnableBase, IL1GasPriceOracle {

/// @inheritdoc IL1GasPriceOracle
function getL1Fee(bytes memory _data) external view override returns (uint256) {
uint256 _l1GasUsed = getL1GasUsed(_data);
uint256 _l1Fee = _l1GasUsed * l1BaseFee;
return (_l1Fee * scalar) / PRECISION;
if (isCurie) {
return _getL1FeeCurie(_data);
} else {
return _getL1FeeBeforeCurie(_data);
}
}

/// @inheritdoc IL1GasPriceOracle
/// @dev The `_data` is the RLP-encoded transaction with signature. And we also reserve additional
/// 4 bytes in the non-zero bytes to store the number of bytes in the RLP-encoded transaction.
function getL1GasUsed(bytes memory _data) public view override returns (uint256) {
uint256 _total = 0;
uint256 _length = _data.length;
unchecked {
for (uint256 i = 0; i < _length; i++) {
if (_data[i] == 0) {
_total += 4;
} else {
_total += 16;
}
}
return _total + overhead + (4 * 16);
if (isCurie) {
// It is near zero since we put all transactions to blob.
return 0;
} else {
return _getL1GasUsedBeforeCurie(_data);
}
}

Expand All @@ -90,12 +143,22 @@ contract L1GasPriceOracle is OwnableBase, IL1GasPriceOracle {
*****************************/

/// @inheritdoc IL1GasPriceOracle
function setL1BaseFee(uint256 _l1BaseFee) external override {
require(whitelist.isSenderAllowed(msg.sender), "Not whitelisted sender");
function setL1BaseFee(uint256 _l1BaseFee) external override onlyWhitelistedSender {
l1BaseFee = _l1BaseFee;

emit L1BaseFeeUpdated(_l1BaseFee);
}

/// @inheritdoc IL1GasPriceOracle
function setL1BaseFeeAndBlobBaseFee(
uint256 _l1BaseFee,
uint256 _l1BlobBaseFee
) external override onlyWhitelistedSender {
l1BaseFee = _l1BaseFee;
l1BlobBaseFee = _l1BlobBaseFee;

emit L1BaseFeeUpdated(_l1BaseFee);
emit L1BlobBaseFeeUpdated(_l1BlobBaseFee);
}

/************************
Expand All @@ -105,7 +168,7 @@ contract L1GasPriceOracle is OwnableBase, IL1GasPriceOracle {
/// @notice Allows the owner to modify the overhead.
/// @param _overhead New overhead
function setOverhead(uint256 _overhead) external onlyOwner {
require(_overhead <= MAX_OVERHEAD, "exceed maximum overhead");
if (_overhead > MAX_OVERHEAD) revert ErrExceedMaxOverhead();

overhead = _overhead;
emit OverheadUpdated(_overhead);
Expand All @@ -114,12 +177,30 @@ contract L1GasPriceOracle is OwnableBase, IL1GasPriceOracle {
/// Allows the owner to modify the scalar.
/// @param _scalar New scalar
function setScalar(uint256 _scalar) external onlyOwner {
require(_scalar <= MAX_SCALE, "exceed maximum scale");
if (_scalar > MAX_SCALAR) revert ErrExceedMaxScalar();

scalar = _scalar;
emit ScalarUpdated(_scalar);
}

/// Allows the owner to modify the commit scalar.
/// @param _scalar New scalar
function setCommitScalar(uint256 _scalar) external onlyOwner {
if (_scalar > MAX_COMMIT_SCALAR) revert ErrExceedMaxCommitScalar();

commitScalar = _scalar;
emit CommitScalarUpdated(_scalar);
}

/// Allows the owner to modify the blob scalar.
/// @param _scalar New scalar
function setBlobScalar(uint256 _scalar) external onlyOwner {
if (_scalar > MAX_BLOB_SCALAR) revert ErrExceedMaxBlobScalar();

blobScalar = _scalar;
emit BlobScalarUpdated(_scalar);
}

/// @notice Update whitelist contract.
/// @dev This function can only called by contract owner.
/// @param _newWhitelist The address of new whitelist contract.
Expand All @@ -129,4 +210,57 @@ contract L1GasPriceOracle is OwnableBase, IL1GasPriceOracle {
whitelist = IWhitelist(_newWhitelist);
emit UpdateWhitelist(_oldWhitelist, _newWhitelist);
}

/// @notice Enable the Curie 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 enableCurie() external onlyOwner {
if (isCurie) revert ErrAlreadyInCurieFork();
isCurie = true;
}

/**********************
* Internal Functions *
**********************/

/// @dev Internal function to computes the amount of L1 gas used for a transaction before Curie fork.
/// The `_data` is the RLP-encoded transaction with signature. And we also reserve additional
/// 4 bytes in the non-zero bytes to store the number of bytes in the RLP-encoded transaction.
/// @param _data Signed fully RLP-encoded transaction to get the L1 gas for.
/// @return Amount of L1 gas used to publish the transaction.
function _getL1GasUsedBeforeCurie(bytes memory _data) private view returns (uint256) {
uint256 _total = 0;
uint256 _length = _data.length;
unchecked {
for (uint256 i = 0; i < _length; i++) {
if (_data[i] == 0) {
_total += 4;
} else {
_total += 16;
}
}
return _total + overhead + (4 * 16);
}
}

/// @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, before Curie 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 _getL1FeeBeforeCurie(bytes memory _data) private view returns (uint256) {
uint256 _l1GasUsed = _getL1GasUsedBeforeCurie(_data);
uint256 _l1Fee = _l1GasUsed * l1BaseFee;
return (_l1Fee * scalar) / 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 Curie 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 _getL1FeeCurie(bytes memory _data) private view returns (uint256) {
// We have bounded the value of `commitScalar` and `blobScalar`, the whole expression won't overflow.
return (commitScalar * l1BaseFee + blobScalar * _data.length * l1BlobBaseFee) / PRECISION;
}
}
Loading