Skip to content
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

EIP4844 smart contract updated #8712

Closed
Closed
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
95 changes: 82 additions & 13 deletions packages/contracts-bedrock/src/L2/GasPriceOracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,30 @@ contract GasPriceOracle is ISemver {
uint256 public constant DECIMALS = 6;

/// @notice Semantic version.
/// @custom:semver 1.1.0
string public constant version = "1.1.0";
/// @custom:semver 1.2.0
string public constant version = "1.2.0";

/// @notice Indicates whether the network has gone through the Ecotone upgrade.
bool public isEcotone;

/// @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.
/// @return L1 fee that should be paid for the tx
function getL1Fee(bytes memory _data) external view returns (uint256) {
uint256 l1GasUsed = getL1GasUsed(_data);
uint256 l1Fee = l1GasUsed * l1BaseFee();
uint256 divisor = 10 ** DECIMALS;
uint256 unscaled = l1Fee * scalar();
uint256 scaled = unscaled / divisor;
return scaled;
if (isEcotone) {
return _getL1FeeEcotone(_data);
}
return _getL1FeeBedrock(_data);
}

/// @notice Set chain to be Ecotone chain (callable by depositor account)
function setEcotone() external {
require(
msg.sender == L1Block(Predeploys.L1_BLOCK_ATTRIBUTES).DEPOSITOR_ACCOUNT(),
"GasPriceOracle: only the depositor account can set isEcotone flag"
);
isEcotone = true;
}

/// @notice Retrieves the current gas price (base fee).
Expand All @@ -52,15 +62,23 @@ contract GasPriceOracle is ISemver {
return block.basefee;
}

/// @custom:legacy
/// @notice Retrieves the current fee overhead.
/// @return Current fee overhead.
function overhead() public view returns (uint256) {
if (isEcotone) {
revert("GasPriceOracle: overhead() is deprecated");
}
return L1Block(Predeploys.L1_BLOCK_ATTRIBUTES).l1FeeOverhead();
}

/// @custom:legacy
/// @notice Retrieves the current fee scalar.
/// @return Current fee scalar.
function scalar() public view returns (uint256) {
if (isEcotone) {
revert("GasPriceOracle: scalar() is deprecated");
}
return L1Block(Predeploys.L1_BLOCK_ATTRIBUTES).l1FeeScalar();
}

Expand All @@ -70,20 +88,68 @@ contract GasPriceOracle is ISemver {
return L1Block(Predeploys.L1_BLOCK_ATTRIBUTES).basefee();
}

/// @notice Retrieves the current blob base fee.
/// @return Current blob base fee.
function blobBasefee() public view returns (uint256) {
return L1Block(Predeploys.L1_BLOCK_ATTRIBUTES).blobBasefee();
}

/// @notice Retrieves the current base fee scalar.
/// @return Current base fee scalar.
function basefeeScalar() public view returns (uint32) {
return L1Block(Predeploys.L1_BLOCK_ATTRIBUTES).basefeeScalar();
}

/// @notice Retrieves the current blob base fee scalar.
/// @return Current blob base fee scalar.
function blobBasefeeScalar() public view returns (uint32) {
return L1Block(Predeploys.L1_BLOCK_ATTRIBUTES).blobBasefeeScalar();
}

/// @custom:legacy
/// @notice Retrieves the number of decimals used in the scalar.
/// @return Number of decimals used in the scalar.
function decimals() public pure returns (uint256) {
return DECIMALS;
}

/// @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 68 bytes of padding to account for the fact that the input does
/// not have a signature.
/// @notice Computes the amount of L1 gas used for a transaction. Adds 68 bytes
/// of padding to account for the fact that the input does not have a signature.
anikaraghu marked this conversation as resolved.
Show resolved Hide resolved
/// @param _data Unsigned 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) public view returns (uint256) {
return _getL1GasUsed(_data);
}

/// @notice Computation of the L1 portion of the fee for Bedrock.
/// @param _data Unsigned fully RLP-encoded transaction to get the L1 fee for.
/// @return L1 fee that should be paid for the tx
function _getL1FeeBedrock(bytes memory _data) internal view returns (uint256) {
uint256 l1GasUsed = _getL1GasUsed(_data);
uint256 l1Fee = l1GasUsed * l1BaseFee();
uint256 unscaled = l1Fee * L1Block(Predeploys.L1_BLOCK_ATTRIBUTES).l1FeeScalar();
uint256 divisor = 10 ** DECIMALS;
uint256 scaled = unscaled / divisor;
return scaled;
}

/// @notice L1 portion of the fee after Ecotone.
/// @param _data Unsigned fully RLP-encoded transaction to get the L1 fee for.
/// @return L1 fee that should be paid for the tx
function _getL1FeeEcotone(bytes memory _data) internal view returns (uint256) {
uint256 l1GasUsed = _getL1GasUsed(_data);
uint256 scaledBasefee = basefeeScalar() * 16 * l1BaseFee();
uint256 scaledBlobBasefee = blobBasefeeScalar() * blobBasefee();
uint256 unscaled = l1GasUsed * (scaledBasefee + scaledBlobBasefee);
uint256 divisor = 16 * 10 ** DECIMALS;
uint256 scaled = unscaled / divisor;
return scaled;
}

/// @notice L1 gas estimation calculation.
/// @param _data Unsigned 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) internal view returns (uint256) {
uint256 total = 0;
uint256 length = _data.length;
for (uint256 i = 0; i < length; i++) {
Expand All @@ -93,7 +159,10 @@ contract GasPriceOracle is ISemver {
total += 16;
}
}
uint256 unsigned = total + overhead();
uint256 unsigned = total;
if (!isEcotone) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that with this approach we check the isEcotone flag twice when we call this from _getL1FeeBedrock/Ecotone. Thoughts on this tradeoff(alternative being have a separate internal function)

total += L1Block(Predeploys.L1_BLOCK_ATTRIBUTES).l1FeeOverhead();
}
return unsigned + (68 * 16);
}
}
74 changes: 72 additions & 2 deletions packages/contracts-bedrock/src/L2/L1Block.sol
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,26 @@ contract L1Block is ISemver {
bytes32 public batcherHash;

/// @notice The overhead value applied to the L1 portion of the transaction fee.
/// @custom:legacy
uint256 public l1FeeOverhead;

/// @notice The scalar value applied to the L1 portion of the transaction fee.
/// @custom:legacy
uint256 public l1FeeScalar;

/// @custom:semver 1.1.0
string public constant version = "1.1.0";
/// @notice The latest L1 blob basefee.
uint256 public blobBasefee;

/// @notice The scalar value applied to the L1 base fee portion of the blob-capable L1 cost func
uint32 public basefeeScalar;

/// @notice The scalar value applied to the L1 blob base fee portion of the blob-capable L1 cost func
uint32 public blobBasefeeScalar;

/// @custom:semver 1.2.0
string public constant version = "1.2.0";

/// @custom:legacy
/// @notice Updates the L1 block values.
/// @param _number L1 blocknumber.
/// @param _timestamp L1 timestamp.
Expand Down Expand Up @@ -73,4 +85,62 @@ contract L1Block is ISemver {
l1FeeOverhead = _l1FeeOverhead;
l1FeeScalar = _l1FeeScalar;
}

/// @notice Updates the L1 block values for a post-blob activated chain.
/// Params are passed in as part of msg.data in order to compress the calldata.
/// Params should be passed in in the following order:
/// 1. _basefeeScalar L1 base fee scalar
/// 2. _blobBasefeeScalar L1 blob base fee scalar
/// 3. _sequenceNumber Number of L2 blocks since epoch start.
/// 4. _timestamp L1 timestamp.
/// 5. _number L1 blocknumber.
/// 6. _basefee L1 basefee.
/// 7. _blobBasefee L1 blobBasefee.
/// 8. _hash L1 blockhash.
/// 9. _batcherHash Versioned hash to authenticate batcher by.
function setL1BlockValuesEcotone() external {
require(msg.sender == DEPOSITOR_ACCOUNT, "L1Block: only the depositor account can set L1 block values");

uint256 _basefeeScalar;
uint256 _blobBasefeeScalar;
uint256 _sequenceNumber;
uint256 _timestamp;
uint256 _number;
uint256 _basefee;
uint256 _blobBasefee;
bytes32 _hash;
bytes32 _batcherHash;

assembly {
let offset := 0x4
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think having an offset for first pass to get passing tests is fine but ideally we don't need to have the intermediate adds and we just hardcode in the values. We should also be able to sstore directly in the yul but I am fine with that as a nice to have

_basefeeScalar := shr(224, calldataload(offset)) // uint32
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the basefeescalar and the blobbasefeescalar we technically do not need to even parse them into their own values on the stack, since they are next to each other in the calldata and in storage we can sstore(slot, shr(160, calldataload(4)) or something like that

offset := add(offset, 0x4)
_blobBasefeeScalar := shr(224, calldataload(offset)) // uint32
offset := add(offset, 0x4)
_sequenceNumber := shr(192, calldataload(offset)) // uint64
offset := add(offset, 0x8)
_timestamp := shr(192, calldataload(offset)) // uint64
offset := add(offset, 0x8)
_number := shr(192, calldataload(offset)) // uint64
offset := add(offset, 0x8)
_basefee := calldataload(offset) // uint256
offset := add(offset, 0x20)
_blobBasefee := calldataload(offset) // uint256
offset := add(offset, 0x20)
_hash := calldataload(offset) // bytes32
offset := add(offset, 0x20)
_batcherHash := calldataload(offset) // bytes32
offset := add(offset, 0x20)
}

number = uint64(_number);
timestamp = uint64(_timestamp);
basefee = _basefee;
blobBasefee = _blobBasefee;
hash = _hash;
sequenceNumber = uint64(_sequenceNumber);
batcherHash = _batcherHash;
basefeeScalar = uint32(_basefeeScalar);
blobBasefeeScalar = uint32(_blobBasefeeScalar);
}
}
119 changes: 118 additions & 1 deletion packages/contracts-bedrock/test/L2/GasPriceOracle.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,32 @@ contract GasPriceOracle_Test is CommonTest {
event OverheadUpdated(uint256);
event ScalarUpdated(uint256);
event DecimalsUpdated(uint256);

address depositor;

// The initial L1 context values
uint64 constant number = 10;
uint64 constant timestamp = 11;
uint256 constant basefee = 100;
uint256 constant blobBasefee = 101;
bytes32 constant hash = bytes32(uint256(64));
uint64 constant sequenceNumber = 0;
bytes32 constant batcherHash = bytes32(uint256(777));
uint256 constant l1FeeOverhead = 310;
uint256 constant l1FeeScalar = 10;
uint32 constant blobBasefeeScalar = 15;
uint32 constant basefeeScalar = 20;

/// @dev Sets up the test suite.
function setUp() public virtual override {
super.setUp();
depositor = l1Block.DEPOSITOR_ACCOUNT();
}
}

contract GasPriceOracleBedrock_Test is GasPriceOracle_Test {
/// @dev Sets up the test suite.
function setUp() public virtual override {
super.setUp();
depositor = l1Block.DEPOSITOR_ACCOUNT();

vm.prank(depositor);
Expand Down Expand Up @@ -93,3 +102,111 @@ contract GasPriceOracle_Test is CommonTest {
assertEq(returndata, hex"");
}
}

contract GasPriceOracleEcotone_Test is GasPriceOracle_Test {
/// @dev Sets up the test suite.
function setUp() public virtual override {
super.setUp();

// Define the function signature
bytes4 functionSignature = bytes4(keccak256("setL1BlockValuesEcotone()"));

// Encode the function signature and extra data
bytes memory callDataPacked = abi.encodePacked(
basefeeScalar,
blobBasefeeScalar,
sequenceNumber,
timestamp,
number,
basefee,
blobBasefee,
hash,
batcherHash
);
bytes memory functionCallData = abi.encodePacked(functionSignature, callDataPacked);

// Execute the function call
vm.prank(depositor);
(bool success, ) = address(l1Block).call(functionCallData);
require(success, "Function call failed");

vm.prank(depositor);
gasPriceOracle.setEcotone();
}

/// @dev Tests that `setEcotone` is only callable by the depositor.
function test_setEcotone_wrongCaller_reverts() external {
vm.expectRevert("GasPriceOracle: only the depositor account can set isEcotone flag");
gasPriceOracle.setEcotone();
}

/// @dev Tests that `gasPrice` is set correctly.
function test_gasPrice_succeeds() external {
vm.fee(100);
uint256 gasPrice = gasPriceOracle.gasPrice();
assertEq(gasPrice, 100);
}

/// @dev Tests that `baseFee` is set correctly.
function test_baseFee_succeeds() external {
vm.fee(64);
uint256 gasPrice = gasPriceOracle.baseFee();
assertEq(gasPrice, 64);
}

/// @dev Tests that `overhead` reverts since it was removed in ecotone.
function test_overhead_legacyFunction_reverts() external {
vm.expectRevert("GasPriceOracle: overhead() is deprecated");
gasPriceOracle.overhead();
}

/// @dev Tests that `scalar` reverts since it was removed in ecotone.
function test_scalar_legacyFunction_reverts() external {
vm.expectRevert("GasPriceOracle: scalar() is deprecated");
gasPriceOracle.scalar();
}

/// @dev Tests that `l1BaseFee` is set correctly.
function test_l1BaseFee_succeeds() external {
assertEq(gasPriceOracle.l1BaseFee(), basefee);
}

/// @dev Tests that `blobBasefee` is set correctly.
function test_blobBasefee_succeeds() external {
assertEq(gasPriceOracle.blobBasefee(), blobBasefee);
}

/// @dev Tests that `basefeeScalar` is set correctly.
function test_basefeeScalar_succeeds() external {
assertEq(gasPriceOracle.basefeeScalar(), basefeeScalar);
}

/// @dev Tests that `blobBasefeeScalar` is set correctly.
function test_blobBasefeeScalar_succeeds() external {
assertEq(gasPriceOracle.blobBasefeeScalar(), blobBasefeeScalar);
}

/// @dev Tests that `decimals` is set correctly.
function test_decimals_succeeds() external {
assertEq(gasPriceOracle.decimals(), 6);
assertEq(gasPriceOracle.DECIMALS(), 6);
}

/// @dev Tests that `setGasPrice` reverts since it was removed in bedrock.
function test_setGasPrice_doesNotExist_reverts() external {
(bool success, bytes memory returndata) =
address(gasPriceOracle).call(abi.encodeWithSignature("setGasPrice(uint256)", 1));

assertEq(success, false);
assertEq(returndata, hex"");
}

/// @dev Tests that `setL1BaseFee` reverts since it was removed in bedrock.
function test_setL1BaseFee_doesNotExist_reverts() external {
(bool success, bytes memory returndata) =
address(gasPriceOracle).call(abi.encodeWithSignature("setL1BaseFee(uint256)", 1));

assertEq(success, false);
assertEq(returndata, hex"");
}
}
Loading