Commit 52abfb50 authored by Danyal Prout's avatar Danyal Prout Committed by GitHub

fjord: Fastlz GasPriceOracle (#10534)

* GasPriceOracle fjord fastlz update

* Review feedback
parent 6e98522f
...@@ -239,15 +239,18 @@ jobs: ...@@ -239,15 +239,18 @@ jobs:
- ".devnet/allocs-l1.json" - ".devnet/allocs-l1.json"
- ".devnet/allocs-l2.json" - ".devnet/allocs-l2.json"
- ".devnet/allocs-l2-delta.json" - ".devnet/allocs-l2-delta.json"
- ".devnet/allocs-l2-ecotone.json"
- ".devnet/addresses.json" - ".devnet/addresses.json"
- ".devnet-fault-proofs/allocs-l1.json" - ".devnet-fault-proofs/allocs-l1.json"
- ".devnet-fault-proofs/addresses.json" - ".devnet-fault-proofs/addresses.json"
- ".devnet-fault-proofs/allocs-l2.json" - ".devnet-fault-proofs/allocs-l2.json"
- ".devnet-fault-proofs/allocs-l2-delta.json" - ".devnet-fault-proofs/allocs-l2-delta.json"
- ".devnet-fault-proofs/allocs-l2-ecotone.json"
- ".devnet-plasma/allocs-l1.json" - ".devnet-plasma/allocs-l1.json"
- ".devnet-plasma/addresses.json" - ".devnet-plasma/addresses.json"
- ".devnet-plasma/allocs-l2.json" - ".devnet-plasma/allocs-l2.json"
- ".devnet-plasma/allocs-l2-delta.json" - ".devnet-plasma/allocs-l2-delta.json"
- ".devnet-plasma/allocs-l2-ecotone.json"
- "packages/contracts-bedrock/deploy-config/devnetL1.json" - "packages/contracts-bedrock/deploy-config/devnetL1.json"
- "packages/contracts-bedrock/deployments/devnetL1" - "packages/contracts-bedrock/deployments/devnetL1"
- notify-failures-on-develop - notify-failures-on-develop
...@@ -1018,6 +1021,7 @@ jobs: ...@@ -1018,6 +1021,7 @@ jobs:
mkdir -p .devnet mkdir -p .devnet
cp /tmp/workspace/.devnet<<parameters.fpac>>/allocs-l2.json .devnet/allocs-l2.json cp /tmp/workspace/.devnet<<parameters.fpac>>/allocs-l2.json .devnet/allocs-l2.json
cp /tmp/workspace/.devnet<<parameters.fpac>>/allocs-l2-delta.json .devnet/allocs-l2-delta.json cp /tmp/workspace/.devnet<<parameters.fpac>>/allocs-l2-delta.json .devnet/allocs-l2-delta.json
cp /tmp/workspace/.devnet<<parameters.fpac>>/allocs-l2-ecotone.json .devnet/allocs-l2-ecotone.json
cp /tmp/workspace/.devnet<<parameters.fpac>>/allocs-l1.json .devnet/allocs-l1.json cp /tmp/workspace/.devnet<<parameters.fpac>>/allocs-l1.json .devnet/allocs-l1.json
cp /tmp/workspace/.devnet<<parameters.fpac>>/addresses.json .devnet/addresses.json cp /tmp/workspace/.devnet<<parameters.fpac>>/addresses.json .devnet/addresses.json
cp /tmp/workspace/packages/contracts-bedrock/deploy-config/devnetL1.json packages/contracts-bedrock/deploy-config/devnetL1.json cp /tmp/workspace/packages/contracts-bedrock/deploy-config/devnetL1.json packages/contracts-bedrock/deploy-config/devnetL1.json
...@@ -1260,6 +1264,7 @@ jobs: ...@@ -1260,6 +1264,7 @@ jobs:
paths: paths:
- ".devnet/allocs-l2.json" - ".devnet/allocs-l2.json"
- ".devnet/allocs-l2-delta.json" - ".devnet/allocs-l2-delta.json"
- ".devnet/allocs-l2-ecotone.json"
- ".devnet/allocs-l1.json" - ".devnet/allocs-l1.json"
- ".devnet/addresses.json" - ".devnet/addresses.json"
- "packages/contracts-bedrock/deploy-config/devnetL1.json" - "packages/contracts-bedrock/deploy-config/devnetL1.json"
......
...@@ -170,7 +170,7 @@ def devnet_l2_allocs(paths): ...@@ -170,7 +170,7 @@ def devnet_l2_allocs(paths):
# For the previous forks, and the latest fork (default, thus empty prefix), # For the previous forks, and the latest fork (default, thus empty prefix),
# move the forge-dumps into place as .devnet allocs. # move the forge-dumps into place as .devnet allocs.
for suffix in ["-delta", ""]: for suffix in ["-delta", "-ecotone", ""]:
input_path = pjoin(paths.contracts_bedrock_dir, f"state-dump-901{suffix}.json") input_path = pjoin(paths.contracts_bedrock_dir, f"state-dump-901{suffix}.json")
output_path = pjoin(paths.devnet_dir, f'allocs-l2{suffix}.json') output_path = pjoin(paths.devnet_dir, f'allocs-l2{suffix}.json')
shutil.move(src=input_path, dst=output_path) shutil.move(src=input_path, dst=output_path)
......
...@@ -23,7 +23,8 @@ type L2AllocsMode string ...@@ -23,7 +23,8 @@ type L2AllocsMode string
const ( const (
L2AllocsDelta L2AllocsMode = "delta" L2AllocsDelta L2AllocsMode = "delta"
L2AllocsEcotone L2AllocsMode = "" // the default in solidity scripting / testing L2AllocsEcotone L2AllocsMode = "ecotone"
L2AllocsFjord L2AllocsMode = "" // the default in solidity scripting / testing
) )
var ( var (
......
...@@ -123,6 +123,7 @@ func init() { ...@@ -123,6 +123,7 @@ func init() {
} }
l2Allocs[mode] = allocs l2Allocs[mode] = allocs
} }
mustL2Allocs(genesis.L2AllocsFjord)
mustL2Allocs(genesis.L2AllocsEcotone) mustL2Allocs(genesis.L2AllocsEcotone)
mustL2Allocs(genesis.L2AllocsDelta) mustL2Allocs(genesis.L2AllocsDelta)
L1Deployments, err = genesis.NewL1Deployments(l1DeploymentsPath) L1Deployments, err = genesis.NewL1Deployments(l1DeploymentsPath)
......
...@@ -60,7 +60,9 @@ func NewOpGeth(t *testing.T, ctx context.Context, cfg *SystemConfig) (*OpGeth, e ...@@ -60,7 +60,9 @@ func NewOpGeth(t *testing.T, ctx context.Context, cfg *SystemConfig) (*OpGeth, e
var allocsMode genesis.L2AllocsMode var allocsMode genesis.L2AllocsMode
allocsMode = genesis.L2AllocsDelta allocsMode = genesis.L2AllocsDelta
if ecotoneTime := cfg.DeployConfig.EcotoneTime(l1Block.Time()); ecotoneTime != nil && *ecotoneTime <= 0 { if fjordTime := cfg.DeployConfig.FjordTime(l1Block.Time()); fjordTime != nil && *fjordTime <= 0 {
allocsMode = genesis.L2AllocsFjord
} else if ecotoneTime := cfg.DeployConfig.EcotoneTime(l1Block.Time()); ecotoneTime != nil && *ecotoneTime <= 0 {
allocsMode = genesis.L2AllocsEcotone allocsMode = genesis.L2AllocsEcotone
} }
l2Allocs := config.L2Allocs(allocsMode) l2Allocs := config.L2Allocs(allocsMode)
......
...@@ -482,7 +482,9 @@ func (cfg SystemConfig) Start(t *testing.T, _opts ...SystemConfigOption) (*Syste ...@@ -482,7 +482,9 @@ func (cfg SystemConfig) Start(t *testing.T, _opts ...SystemConfigOption) (*Syste
l1Block := l1Genesis.ToBlock() l1Block := l1Genesis.ToBlock()
var allocsMode genesis.L2AllocsMode var allocsMode genesis.L2AllocsMode
allocsMode = genesis.L2AllocsDelta allocsMode = genesis.L2AllocsDelta
if ecotoneTime := cfg.DeployConfig.EcotoneTime(l1Block.Time()); ecotoneTime != nil && *ecotoneTime <= 0 { if fjordTime := cfg.DeployConfig.FjordTime(l1Block.Time()); fjordTime != nil && *fjordTime <= 0 {
allocsMode = genesis.L2AllocsFjord
} else if ecotoneTime := cfg.DeployConfig.EcotoneTime(l1Block.Time()); ecotoneTime != nil && *ecotoneTime <= 0 {
allocsMode = genesis.L2AllocsEcotone allocsMode = genesis.L2AllocsEcotone
} }
t.Log("Generating L2 genesis", "l2_allocs_mode", string(allocsMode)) t.Log("Generating L2 genesis", "l2_allocs_mode", string(allocsMode))
......
...@@ -39,11 +39,13 @@ struct L1Dependencies { ...@@ -39,11 +39,13 @@ struct L1Dependencies {
/// @notice Enum representing different ways of outputting genesis allocs. /// @notice Enum representing different ways of outputting genesis allocs.
/// @custom:value DEFAULT_LATEST Represents only latest L2 allocs, written to output path. /// @custom:value DEFAULT_LATEST Represents only latest L2 allocs, written to output path.
/// @custom:value LOCAL_LATEST Represents latest L2 allocs, not output anywhere, but kept in-process. /// @custom:value LOCAL_LATEST Represents latest L2 allocs, not output anywhere, but kept in-process.
/// @custom:value LOCAL_ECOTONE Represents Ecotone-upgrade L2 allocs, not output anywhere, but kept in-process.
/// @custom:value LOCAL_DELTA Represents Delta-upgrade L2 allocs, not output anywhere, but kept in-process. /// @custom:value LOCAL_DELTA Represents Delta-upgrade L2 allocs, not output anywhere, but kept in-process.
/// @custom:value OUTPUT_ALL Represents creation of one L2 allocs file for every upgrade. /// @custom:value OUTPUT_ALL Represents creation of one L2 allocs file for every upgrade.
enum OutputMode { enum OutputMode {
DEFAULT_LATEST, DEFAULT_LATEST,
LOCAL_LATEST, LOCAL_LATEST,
LOCAL_ECOTONE,
LOCAL_DELTA, LOCAL_DELTA,
OUTPUT_ALL OUTPUT_ALL
} }
...@@ -154,6 +156,16 @@ contract L2Genesis is Deployer { ...@@ -154,6 +156,16 @@ contract L2Genesis is Deployer {
} }
activateEcotone(); activateEcotone();
if (_mode == OutputMode.LOCAL_ECOTONE) {
return;
}
if (_mode == OutputMode.OUTPUT_ALL) {
writeGenesisAllocs(Config.stateDumpPath("-ecotone"));
}
activateFjord();
if (_mode == OutputMode.OUTPUT_ALL || _mode == OutputMode.DEFAULT_LATEST) { if (_mode == OutputMode.OUTPUT_ALL || _mode == OutputMode.DEFAULT_LATEST) {
writeGenesisAllocs(Config.stateDumpPath("")); writeGenesisAllocs(Config.stateDumpPath(""));
} }
...@@ -490,6 +502,12 @@ contract L2Genesis is Deployer { ...@@ -490,6 +502,12 @@ contract L2Genesis is Deployer {
GasPriceOracle(Predeploys.GAS_PRICE_ORACLE).setEcotone(); GasPriceOracle(Predeploys.GAS_PRICE_ORACLE).setEcotone();
} }
function activateFjord() public {
console.log("Activating fjord in GasPriceOracle contract");
vm.prank(L1Block(Predeploys.L1_BLOCK_ATTRIBUTES).DEPOSITOR_ACCOUNT());
GasPriceOracle(Predeploys.GAS_PRICE_ORACLE).setFjord();
}
/// @notice Sets the bytecode in state /// @notice Sets the bytecode in state
function _setImplementationCode(address _addr) internal returns (address) { function _setImplementationCode(address _addr) internal returns (address) {
string memory cname = Predeploys.getName(_addr); string memory cname = Predeploys.getName(_addr);
......
...@@ -60,8 +60,8 @@ ...@@ -60,8 +60,8 @@
"sourceCodeHash": "0x864d244dd0b01cb01e5f50425bedce46e72005bdb8bead198ac885481547f41d" "sourceCodeHash": "0x864d244dd0b01cb01e5f50425bedce46e72005bdb8bead198ac885481547f41d"
}, },
"src/L2/GasPriceOracle.sol": { "src/L2/GasPriceOracle.sol": {
"initCodeHash": "0xfd456e91d8c9714590a4f0a2c1046ba70e102f1c629ead886c4eebc3f921c3c3", "initCodeHash": "0xb16f1e370e58c7693fd113a21a1b1e7ccebc03d4f1e5a76786fc27847ef51ead",
"sourceCodeHash": "0xde06becce9514f46ba78b4cb0732c7a714d49ba8f131258d56a5f5b22b51be7e" "sourceCodeHash": "0x5529ee28aae94904a1c08a8b188f51a39a0f51fbd3b43f1abd4fee7bba57998c"
}, },
"src/L2/L1Block.sol": { "src/L2/L1Block.sol": {
"initCodeHash": "0x00961e82f3ed7f7755115c897304063e283bff0bed1200d50e0abe5c59424069", "initCodeHash": "0x00961e82f3ed7f7755115c897304063e283bff0bed1200d50e0abe5c59424069",
......
...@@ -109,6 +109,25 @@ ...@@ -109,6 +109,25 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"inputs": [
{
"internalType": "uint256",
"name": "_unsignedTxSize",
"type": "uint256"
}
],
"name": "getL1FeeUpperBound",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{ {
"inputs": [ "inputs": [
{ {
...@@ -141,6 +160,19 @@ ...@@ -141,6 +160,19 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"inputs": [],
"name": "isFjord",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{ {
"inputs": [], "inputs": [],
"name": "l1BaseFee", "name": "l1BaseFee",
...@@ -187,6 +219,13 @@ ...@@ -187,6 +219,13 @@
"stateMutability": "nonpayable", "stateMutability": "nonpayable",
"type": "function" "type": "function"
}, },
{
"inputs": [],
"name": "setFjord",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{ {
"inputs": [], "inputs": [],
"name": "version", "name": "version",
......
...@@ -5,5 +5,12 @@ ...@@ -5,5 +5,12 @@
"offset": 0, "offset": 0,
"slot": "0", "slot": "0",
"type": "bool" "type": "bool"
},
{
"bytes": "1",
"label": "isFjord",
"offset": 1,
"slot": "0",
"type": "bool"
} }
] ]
\ No newline at end of file
...@@ -4,6 +4,8 @@ pragma solidity 0.8.15; ...@@ -4,6 +4,8 @@ pragma solidity 0.8.15;
import { ISemver } from "src/universal/ISemver.sol"; import { ISemver } from "src/universal/ISemver.sol";
import { Predeploys } from "src/libraries/Predeploys.sol"; import { Predeploys } from "src/libraries/Predeploys.sol";
import { L1Block } from "src/L2/L1Block.sol"; import { L1Block } from "src/L2/L1Block.sol";
import { Constants } from "src/libraries/Constants.sol";
import { LibZip } from "@solady/utils/LibZip.sol";
/// @custom:proxied /// @custom:proxied
/// @custom:predeploy 0x420000000000000000000000000000000000000F /// @custom:predeploy 0x420000000000000000000000000000000000000F
...@@ -24,33 +26,77 @@ contract GasPriceOracle is ISemver { ...@@ -24,33 +26,77 @@ contract GasPriceOracle is ISemver {
uint256 public constant DECIMALS = 6; uint256 public constant DECIMALS = 6;
/// @notice Semantic version. /// @notice Semantic version.
/// @custom:semver 1.2.0 /// @custom:semver 1.3.0
string public constant version = "1.2.0"; string public constant version = "1.3.0";
/// @notice This is the intercept value for the linear regression used to estimate the final size of the
/// compressed transaction.
int32 private constant COST_INTERCEPT = -42_585_600;
/// @notice This is the coefficient value for the linear regression used to estimate the final size of the
/// compressed transaction.
uint32 private constant COST_FASTLZ_COEF = 836_500;
/// @notice This is the minimum bound for the fastlz to brotli size estimation. Any estimations below this
/// are set to this value.
uint256 private constant MIN_TRANSACTION_SIZE = 100;
/// @notice Indicates whether the network has gone through the Ecotone upgrade. /// @notice Indicates whether the network has gone through the Ecotone upgrade.
bool public isEcotone; bool public isEcotone;
/// @notice Indicates whether the network has gone through the Fjord upgrade.
bool public isFjord;
/// @notice Computes the L1 portion of the fee based on the size of the rlp encoded input /// @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. /// 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 Unsigned fully RLP-encoded transaction to get the L1 fee for.
/// @return L1 fee that should be paid for the tx /// @return L1 fee that should be paid for the tx
function getL1Fee(bytes memory _data) external view returns (uint256) { function getL1Fee(bytes memory _data) external view returns (uint256) {
if (isEcotone) { if (isFjord) {
return _getL1FeeFjord(_data);
} else if (isEcotone) {
return _getL1FeeEcotone(_data); return _getL1FeeEcotone(_data);
} }
return _getL1FeeBedrock(_data); return _getL1FeeBedrock(_data);
} }
/// @notice returns an upper bound for the L1 fee for a given transaction size.
/// It is provided for callers who wish to estimate L1 transaction costs in the
/// write path, and is much more gas efficient than `getL1Fee`.
/// It assumes the worst case of fastlz upper-bound which covers %99.99 txs.
/// @param _unsignedTxSize Unsigned fully RLP-encoded transaction size to get the L1 fee for.
/// @return L1 estimated upper-bound fee that should be paid for the tx
function getL1FeeUpperBound(uint256 _unsignedTxSize) external view returns (uint256) {
require(isFjord, "GasPriceOracle: getL1FeeUpperBound only supports Fjord");
// Add 68 to the size to account for unsigned tx:
uint256 txSize = _unsignedTxSize + 68;
// txSize / 255 + 16 is the pratical fastlz upper-bound covers %99.99 txs.
uint256 flzUpperBound = txSize + txSize / 255 + 16;
return _fjordL1Cost(flzUpperBound);
}
/// @notice Set chain to be Ecotone chain (callable by depositor account) /// @notice Set chain to be Ecotone chain (callable by depositor account)
function setEcotone() external { function setEcotone() external {
require( require(
msg.sender == L1Block(Predeploys.L1_BLOCK_ATTRIBUTES).DEPOSITOR_ACCOUNT(), msg.sender == Constants.DEPOSITOR_ACCOUNT,
"GasPriceOracle: only the depositor account can set isEcotone flag" "GasPriceOracle: only the depositor account can set isEcotone flag"
); );
require(isEcotone == false, "GasPriceOracle: Ecotone already active"); require(isEcotone == false, "GasPriceOracle: Ecotone already active");
isEcotone = true; isEcotone = true;
} }
/// @notice Set chain to be Fjord chain (callable by depositor account)
function setFjord() external {
require(
msg.sender == Constants.DEPOSITOR_ACCOUNT, "GasPriceOracle: only the depositor account can set isFjord flag"
);
require(isEcotone, "GasPriceOracle: Fjord can only be activated after Ecotone");
require(isFjord == false, "GasPriceOracle: Fjord already active");
isFjord = true;
}
/// @notice Retrieves the current gas price (base fee). /// @notice Retrieves the current gas price (base fee).
/// @return Current L2 gas price (base fee). /// @return Current L2 gas price (base fee).
function gasPrice() public view returns (uint256) { function gasPrice() public view returns (uint256) {
...@@ -114,7 +160,15 @@ contract GasPriceOracle is ISemver { ...@@ -114,7 +160,15 @@ contract GasPriceOracle is ISemver {
/// of padding to account for the fact that the input does not have a signature. /// 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 Unsigned fully RLP-encoded transaction to get the L1 gas for.
/// @return Amount of L1 gas used to publish the transaction. /// @return Amount of L1 gas used to publish the transaction.
/// @custom:deprecated This method does not accurately estimate the gas used for a transaction.
/// If you are calculating fees use getL1Fee or getL1FeeUpperBound.
function getL1GasUsed(bytes memory _data) public view returns (uint256) { function getL1GasUsed(bytes memory _data) public view returns (uint256) {
if (isFjord) {
// Add 68 to the size to account for unsigned tx
// Assume the compressed data is mostly non-zero, and would pay 16 gas per calldata byte
// Divide by 1e6 due to the scaling factor of the linear regression
return _fjordLinearRegression(LibZip.flzCompress(_data).length + 68) * 16 / 1e6;
}
uint256 l1GasUsed = _getCalldataGas(_data); uint256 l1GasUsed = _getCalldataGas(_data);
if (isEcotone) { if (isEcotone) {
return l1GasUsed; return l1GasUsed;
...@@ -143,6 +197,13 @@ contract GasPriceOracle is ISemver { ...@@ -143,6 +197,13 @@ contract GasPriceOracle is ISemver {
return fee / (16 * 10 ** DECIMALS); return fee / (16 * 10 ** DECIMALS);
} }
/// @notice L1 portion of the fee after Fjord.
/// @param _data Unsigned fully RLP-encoded transaction to get the L1 fee for.
/// @return L1 fee that should be paid for the tx
function _getL1FeeFjord(bytes memory _data) internal view returns (uint256) {
return _fjordL1Cost(LibZip.flzCompress(_data).length + 68);
}
/// @notice L1 gas estimation calculation. /// @notice L1 gas estimation calculation.
/// @param _data Unsigned fully RLP-encoded transaction to get the L1 gas for. /// @param _data Unsigned fully RLP-encoded transaction to get the L1 gas for.
/// @return Amount of L1 gas used to publish the transaction. /// @return Amount of L1 gas used to publish the transaction.
...@@ -158,4 +219,25 @@ contract GasPriceOracle is ISemver { ...@@ -158,4 +219,25 @@ contract GasPriceOracle is ISemver {
} }
return total + (68 * 16); return total + (68 * 16);
} }
/// @notice Fjord L1 cost based on the compressed and original tx size.
/// @param _fastLzSize estimated compressed tx size.
/// @return Fjord L1 fee that should be paid for the tx
function _fjordL1Cost(uint256 _fastLzSize) internal view returns (uint256) {
// Apply the linear regression to estimate the Brotli 10 size
uint256 estimatedSize = _fjordLinearRegression(_fastLzSize);
uint256 feeScaled = baseFeeScalar() * 16 * l1BaseFee() + blobBaseFeeScalar() * blobBaseFee();
return estimatedSize * feeScaled / (10 ** (DECIMALS * 2));
}
/// @notice Takes the fastLz size compression and returns the estimated Brotli
/// @param _fastLzSize fastlz compressed tx size.
/// @return Number of bytes in the compressed transaction
function _fjordLinearRegression(uint256 _fastLzSize) internal pure returns (uint256) {
int256 estimatedSize = COST_INTERCEPT + int256(COST_FASTLZ_COEF * _fastLzSize);
if (estimatedSize < int256(MIN_TRANSACTION_SIZE) * 1e6) {
estimatedSize = int256(MIN_TRANSACTION_SIZE) * 1e6;
}
return uint256(estimatedSize);
}
} }
...@@ -108,12 +108,19 @@ contract GasPriceOracleBedrock_Test is GasPriceOracle_Test { ...@@ -108,12 +108,19 @@ contract GasPriceOracleBedrock_Test is GasPriceOracle_Test {
assertEq(success, false); assertEq(success, false);
assertEq(returndata, hex""); assertEq(returndata, hex"");
} }
/// @dev Tests that Fjord cannot be activated without activating Ecotone
function test_setFjord_withoutEcotone_reverts() external {
vm.prank(depositor);
vm.expectRevert("GasPriceOracle: Fjord can only be activated after Ecotone");
gasPriceOracle.setFjord();
}
} }
contract GasPriceOracleEcotone_Test is GasPriceOracle_Test { contract GasPriceOracleEcotone_Test is GasPriceOracle_Test {
/// @dev Sets up the test suite. /// @dev Sets up the test suite.
function setUp() public virtual override { function setUp() public virtual override {
l2OutputMode = OutputMode.LOCAL_LATEST; // activate ecotone l2OutputMode = OutputMode.LOCAL_ECOTONE; // activate ecotone
super.setUp(); super.setUp();
assertEq(gasPriceOracle.isEcotone(), true); assertEq(gasPriceOracle.isEcotone(), true);
...@@ -195,4 +202,131 @@ contract GasPriceOracleEcotone_Test is GasPriceOracle_Test { ...@@ -195,4 +202,131 @@ contract GasPriceOracleEcotone_Test is GasPriceOracle_Test {
// gas * (2M*16*20 + 3M*15) / 16M == 48977.5 // gas * (2M*16*20 + 3M*15) / 16M == 48977.5
assertEq(price, 48977); assertEq(price, 48977);
} }
/// @dev Tests that `setFjord` is only callable by the depositor.
function test_setFjord_wrongCaller_reverts() external {
vm.expectRevert("GasPriceOracle: only the depositor account can set isFjord flag");
gasPriceOracle.setFjord();
}
}
contract GasPriceOracleFjordActive_Test is GasPriceOracle_Test {
/// @dev Sets up the test suite.
function setUp() public virtual override {
l2OutputMode = OutputMode.LOCAL_LATEST; // activate fjord
super.setUp();
bytes memory calldataPacked = Encoding.encodeSetL1BlockValuesEcotone(
baseFeeScalar, blobBaseFeeScalar, sequenceNumber, timestamp, number, baseFee, blobBaseFee, hash, batcherHash
);
vm.prank(depositor);
(bool success,) = address(l1Block).call(calldataPacked);
require(success, "Function call failed");
}
/// @dev Tests that `setFjord` cannot be called when Fjord is already activate
function test_setFjord_whenFjordActive_reverts() external {
vm.expectRevert("GasPriceOracle: Fjord already active");
vm.prank(depositor);
gasPriceOracle.setFjord();
}
/// @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 view {
assertEq(gasPriceOracle.l1BaseFee(), baseFee);
}
/// @dev Tests that `blobBaseFee` is set correctly.
function test_blobBaseFee_succeeds() external view {
assertEq(gasPriceOracle.blobBaseFee(), blobBaseFee);
}
/// @dev Tests that `baseFeeScalar` is set correctly.
function test_baseFeeScalar_succeeds() external view {
assertEq(gasPriceOracle.baseFeeScalar(), baseFeeScalar);
}
/// @dev Tests that `blobBaseFeeScalar` is set correctly.
function test_blobBaseFeeScalar_succeeds() external view {
assertEq(gasPriceOracle.blobBaseFeeScalar(), blobBaseFeeScalar);
}
/// @dev Tests that `decimals` is set correctly.
function test_decimals_succeeds() external view {
assertEq(gasPriceOracle.decimals(), 6);
assertEq(gasPriceOracle.DECIMALS(), 6);
}
/// @dev Tests that `getL1GasUsed`, `getL1Fee` and `getL1FeeUpperBound` return expected values
/// for the minimum bound of the linear regression
function test_getL1FeeMinimumBound_succeeds() external view {
bytes memory data = hex"0000010203"; // fastlzSize: 74, inc signature
uint256 gas = gasPriceOracle.getL1GasUsed(data);
assertEq(gas, 1600); // 100 (minimum size) * 16
uint256 price = gasPriceOracle.getL1Fee(data);
// linearRegression = -42.5856 + 74 * 0.8365 = 19.3154
// under the minTxSize of 100, so linear regression output is ignored
// 100_000_000 * (20 * 16 * 2 * 1e6 + 3 * 1e6 * 15) / 1e12
assertEq(price, 68500);
assertEq(data.length, 5);
// flzUpperBound = (5 + 68) + ((5 + 68) / 255) + 16 = 89
// linearRegression = -42.5856 + 89 * 0.8365 = 31.8629
// under the minTxSize of 100, so output is ignored
// 100_000_000 * (20 * 16 * 2 * 1e6 + 3 * 1e6 * 15) / 1e12
uint256 upperBound = gasPriceOracle.getL1FeeUpperBound(data.length);
assertEq(upperBound, 68500);
}
/// @dev Tests that `getL1GasUsed`, `getL1Fee` and `getL1FeeUpperBound` return expected values
/// for a specific test transaction
function test_getL1FeeRegression_succeeds() external view {
// fastlzSize: 235, inc signature
bytes memory data =
hex"1d2c3ec4f5a9b3f3cd2c024e455c1143a74bbd637c324adcbd4f74e346786ac44e23e78f47d932abedd8d1"
hex"06daadcea350be16478461046273101034601364012364701331dfad43729dc486abd134bcad61b34d6ca1"
hex"f2eb31655b7d61ca33ba6d172cdf7d8b5b0ef389a314ca7a9a831c09fc2ca9090d059b4dd25194f3de297b"
hex"dba6d6d796e4f80be94f8a9151d685607826e7ba25177b40cb127ea9f1438470";
uint256 gas = gasPriceOracle.getL1GasUsed(data);
assertEq(gas, 2463); // 235 * 16
uint256 price = gasPriceOracle.getL1Fee(data);
// linearRegression = -42.5856 + 235 * 0.8365 = 153.9919
// 153_991_900 * (20 * 16 * 2 * 1e6 + 3 * 1e6 * 15) / 1e12
assertEq(price, 105484);
assertEq(data.length, 161);
// flzUpperBound = (161 + 68) + ((161 + 68) / 255) + 16 = 245
// linearRegression = -42.5856 + 245 * 0.8365 = 162.3569
// 162_356_900 * (20 * 16 * 2 * 1e6 + 3 * 1e6 * 15) / 1e12 == 111,214.4765
uint256 upperBound = gasPriceOracle.getL1FeeUpperBound(data.length);
assertEq(upperBound, 111214);
}
} }
...@@ -25,7 +25,7 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode { ...@@ -25,7 +25,7 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode {
address internal constant l1ERC721BridgeProxyAddress = 0xD31598c909d9C935a9e35bA70d9a3DD47d4D5865; address internal constant l1ERC721BridgeProxyAddress = 0xD31598c909d9C935a9e35bA70d9a3DD47d4D5865;
address internal constant l1StandardBridgeAddress = 0x04c50B398Cd4182428E79f7186b7C919cF17e86F; address internal constant l1StandardBridgeAddress = 0x04c50B398Cd4182428E79f7186b7C919cF17e86F;
address internal constant l1StandardBridgeProxyAddress = 0xDeF3bca8c80064589E6787477FFa7Dd616B5574F; address internal constant l1StandardBridgeProxyAddress = 0xDeF3bca8c80064589E6787477FFa7Dd616B5574F;
address internal constant mipsAddress = 0x4edC7dF83e441Cc0399642Cef7Bd9437f021cE46; address internal constant mipsAddress = 0xF698388BFCDbd3f9f2F13ebC3E01471B3cc7cE83;
address internal constant optimismPortal2Address = 0xfcbb237388CaF5b08175C9927a37aB6450acd535; address internal constant optimismPortal2Address = 0xfcbb237388CaF5b08175C9927a37aB6450acd535;
address internal constant optimismPortalProxyAddress = 0x978e3286EB805934215a88694d80b09aDed68D90; address internal constant optimismPortalProxyAddress = 0x978e3286EB805934215a88694d80b09aDed68D90;
address internal constant preimageOracleAddress = 0x3bd7E801E51d48c5d94Ea68e8B801DFFC275De75; address internal constant preimageOracleAddress = 0x3bd7E801E51d48c5d94Ea68e8B801DFFC275De75;
...@@ -36,7 +36,7 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode { ...@@ -36,7 +36,7 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode {
address internal constant safeSingletonAddress = 0x90193C961A926261B756D1E5bb255e67ff9498A1; address internal constant safeSingletonAddress = 0x90193C961A926261B756D1E5bb255e67ff9498A1;
address internal constant superchainConfigAddress = 0x068E44eB31e111028c41598E4535be7468674D0A; address internal constant superchainConfigAddress = 0x068E44eB31e111028c41598E4535be7468674D0A;
address internal constant superchainConfigProxyAddress = 0xDEb1E9a6Be7Baf84208BB6E10aC9F9bbE1D70809; address internal constant superchainConfigProxyAddress = 0xDEb1E9a6Be7Baf84208BB6E10aC9F9bbE1D70809;
address internal constant systemConfigAddress = 0x809abd1c13738dE7a76C85839779FCaa59FA32CE; address internal constant systemConfigAddress = 0x1Fa4ABc046c3B6e20e072df7F869D67566974301;
address internal constant systemConfigProxyAddress = 0x20A42a5a785622c6Ba2576B2D6e924aA82BFA11D; address internal constant systemConfigProxyAddress = 0x20A42a5a785622c6Ba2576B2D6e924aA82BFA11D;
address internal constant systemOwnerSafeAddress = 0x7d039be7F9b5190147621b02e82B250e1D748e02; address internal constant systemOwnerSafeAddress = 0x7d039be7F9b5190147621b02e82B250e1D748e02;
address internal constant acc27Address = 0x12e721c390F5728200a26BBEf206A5F4F7E991f3; address internal constant acc27Address = 0x12e721c390F5728200a26BBEf206A5F4F7E991f3;
...@@ -373,7 +373,7 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode { ...@@ -373,7 +373,7 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode {
value = hex"0000000000000000000000000000000000000000000000000000000000000003"; value = hex"0000000000000000000000000000000000000000000000000000000000000003";
vm.store(systemOwnerSafeAddress, slot, value); vm.store(systemOwnerSafeAddress, slot, value);
slot = hex"360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc"; slot = hex"360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
value = hex"000000000000000000000000809abd1c13738de7a76c85839779fcaa59fa32ce"; value = hex"0000000000000000000000001fa4abc046c3b6e20e072df7f869d67566974301";
vm.store(systemConfigProxyAddress, slot, value); vm.store(systemConfigProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000000"; slot = hex"0000000000000000000000000000000000000000000000000000000000000000";
value = hex"0000000000000000000000000000000000000000000000000000000000000001"; value = hex"0000000000000000000000000000000000000000000000000000000000000001";
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment