Skip to content

Commit 773798a

Browse files
authored
GasPriceOracle: remove saturating math from getOperatorFee (jovian) (#17913)
* GasPriceOracle: remove saturating math fro getOperatorFee (isthmus and jovian) Extend test coverage to edge cases * restore Isthmys formula * fix test * use library * lint * semver bump * modify semver and regenerate lock file
1 parent dc43a31 commit 773798a

File tree

3 files changed

+84
-11
lines changed

3 files changed

+84
-11
lines changed

packages/contracts-bedrock/snapshots/semver-lock.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@
6060
"sourceCodeHash": "0x6d137fef431d75a8bf818444915fc39c8b1d93434a9af9971d96fb3170bc72b7"
6161
},
6262
"src/L2/GasPriceOracle.sol:GasPriceOracle": {
63-
"initCodeHash": "0xedc721584d43025c515186d4d5879d2b8e4abaf3a69181b99b6bf90c684df442",
64-
"sourceCodeHash": "0x0074761fc0f893a2418b4e1197c7f29ee59f407df31b99c420bf2fd82d14d583"
63+
"initCodeHash": "0xf72c23d9c3775afd7b645fde429d09800622d329116feb5ff9829634655123ca",
64+
"sourceCodeHash": "0xb4d1bf3669ba87bbeaf4373145c7e1490478c4a05ba4838a524aa6f0ce7348a6"
6565
},
6666
"src/L2/L1Block.sol:L1Block": {
6767
"initCodeHash": "0x1f054ff228ecad7f51772dd25084469192f7a33c522b87cd46ec5558d3c46aec",

packages/contracts-bedrock/src/L2/GasPriceOracle.sol

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ contract GasPriceOracle is ISemver {
3030
uint256 public constant DECIMALS = 6;
3131

3232
/// @notice Semantic version.
33-
/// @custom:semver 1.5.0
34-
string public constant version = "1.5.0";
33+
/// @custom:semver 1.6.0
34+
string public constant version = "1.6.0";
3535

3636
/// @notice This is the intercept value for the linear regression used to estimate the final size of the
3737
/// compressed transaction.
@@ -224,7 +224,7 @@ contract GasPriceOracle is ISemver {
224224
uint256 operatorConstant = IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES).operatorFeeConstant();
225225

226226
if (isJovian) {
227-
return Arithmetic.saturatingAdd(Arithmetic.saturatingMul(_gasUsed, operatorScalar) * 100, operatorConstant);
227+
return _gasUsed * operatorScalar * 100 + operatorConstant;
228228
} else {
229229
return Arithmetic.saturatingAdd(Arithmetic.saturatingMul(_gasUsed, operatorScalar) / 1e6, operatorConstant);
230230
}

packages/contracts-bedrock/test/L2/GasPriceOracle.t.sol

Lines changed: 79 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { Fork } from "scripts/libraries/Config.sol";
77

88
// Libraries
99
import { Encoding } from "src/libraries/Encoding.sol";
10+
import { stdError } from "forge-std/Test.sol";
1011

1112
contract GasPriceOracle_Test is CommonTest {
1213
address depositor;
@@ -26,6 +27,10 @@ contract GasPriceOracle_Test is CommonTest {
2627
uint32 constant operatorFeeScalar = 4_000_000;
2728
uint64 constant operatorFeeConstant = 300;
2829

30+
uint256 constant MAX_UINT256 = type(uint256).max;
31+
uint64 constant MAX_UINT64 = type(uint64).max;
32+
uint32 constant MAX_UINT32 = type(uint32).max;
33+
2934
/// @dev Sets up the test suite.
3035
function setUp() public virtual override {
3136
super.setUp();
@@ -447,6 +452,26 @@ contract GasPriceOracleJovian_Test is GasPriceOracle_Test {
447452
assertEq(gasPriceOracle.isJovian(), true, "Jovian activation failed");
448453
}
449454

455+
function _setOperatorFeeParams(uint32 _operatorFeeScalar, uint64 _operatorFeeConstant) internal {
456+
vm.prank(depositor);
457+
(bool success,) = address(l1Block).call(
458+
Encoding.encodeSetL1BlockValuesIsthmus(
459+
baseFeeScalar,
460+
blobBaseFeeScalar,
461+
sequenceNumber,
462+
timestamp,
463+
number,
464+
baseFee,
465+
blobBaseFee,
466+
hash,
467+
batcherHash,
468+
_operatorFeeScalar,
469+
_operatorFeeConstant
470+
)
471+
);
472+
require(success, "GasPriceOracleJovian_Test: L1Block setup failed");
473+
}
474+
450475
/// @dev Tests that `operatorFee` is set correctly using the new Jovian formula (multiply by 100).
451476
function test_getOperatorFee_succeeds() external {
452477
_activateJovian();
@@ -468,19 +493,67 @@ contract GasPriceOracleJovian_Test is GasPriceOracle_Test {
468493
}
469494

470495
/// @dev Tests the transition from Isthmus formula to Jovian formula.
471-
function test_formulaTransition_succeeds() external {
472-
// Check Isthmus formula (divide by 1e6)
496+
function test_formulaTransition_edgeCases_works() external {
497+
// Check Isthmus formula with a low gasUsed value (divide by 1e6)
498+
_setOperatorFeeParams(operatorFeeScalar, operatorFeeConstant);
473499
uint256 isthmusFee = gasPriceOracle.getOperatorFee(10);
474-
assertEq(isthmusFee, 10 * operatorFeeScalar / 1e6 + operatorFeeConstant);
500+
assertEq(
501+
isthmusFee,
502+
uint256(10) * operatorFeeScalar / 1e6 + operatorFeeConstant,
503+
"Isthmus operator fee incorrect with 10 gas used"
504+
);
505+
506+
// Use maximum values permitted by data types for scalars.
507+
// Use maximum value for gasUsed according to client implementations.
508+
// Assert that the fee is as expected (no overflow).
509+
_setOperatorFeeParams(MAX_UINT32, MAX_UINT64);
510+
isthmusFee = gasPriceOracle.getOperatorFee(MAX_UINT64);
511+
assertEq(
512+
isthmusFee,
513+
uint256(MAX_UINT64) * MAX_UINT32 / 1e6 + MAX_UINT64,
514+
"Isthmus operator fee incorrect with max uint64 gas used"
515+
);
516+
517+
// Show that the math saturates if the maximum
518+
// value for gasUsed (according to data type) is used.
519+
_setOperatorFeeParams(1e6, 1);
520+
uint256 saturatedIsthmusFee = gasPriceOracle.getOperatorFee(MAX_UINT256);
521+
assertEq(
522+
saturatedIsthmusFee,
523+
115792089237316195423570985008687907853269984665640564039457584007913130,
524+
"Incorrect value for fee under Isthmus (saturating arithmetic triggered)"
525+
);
475526

476527
// Activate Jovian
477528
_activateJovian();
478529

479-
// Check Jovian formula (multiply by 100)
530+
// Check Jovian formula with a low gasUsed value (multiply by 100)
531+
_setOperatorFeeParams(operatorFeeScalar, operatorFeeConstant);
480532
uint256 jovianFee = gasPriceOracle.getOperatorFee(10);
481-
assertEq(jovianFee, 10 * operatorFeeScalar * 100 + operatorFeeConstant);
533+
assertEq(
534+
jovianFee,
535+
uint256(10) * operatorFeeScalar * 100 + operatorFeeConstant,
536+
"Jovian operator fee incorrect with 10 gas used"
537+
);
538+
539+
// Use maximum values permitted by data types for scalars.
540+
// Use maximum value for gasUsed according to client implementations.
541+
// Assert that the fee is as expected (no overflow).
542+
_setOperatorFeeParams(MAX_UINT32, MAX_UINT64);
543+
jovianFee = gasPriceOracle.getOperatorFee(MAX_UINT64);
544+
assertEq(
545+
jovianFee,
546+
uint256(MAX_UINT64) * MAX_UINT32 * 100 + MAX_UINT64,
547+
"Jovian operator fee incorrect with max uint64 gas used"
548+
);
549+
550+
// Show that a revert is possible if the maximum
551+
// value for gasUsed (according to data type) is used.
552+
_setOperatorFeeParams(1, 1);
553+
vm.expectRevert(stdError.arithmeticError);
554+
gasPriceOracle.getOperatorFee(MAX_UINT256);
482555

483556
// Verify the fee increased significantly
484-
assertGt(jovianFee, isthmusFee);
557+
assertGt(jovianFee, isthmusFee, "Jovian formula fee should be greater than Isthmus formula fee");
485558
}
486559
}

0 commit comments

Comments
 (0)