-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Functions: ARB+OP cost estimation tweaks (#11102)
* Functions: ARB+OP gas tweaks * make wrappers-all * Updated gas snapshot * Single line comments * (refactor): rework ChainSpecificUtil usage to be an additional flat fee, rather than gas price * (test): Add ChainSpecificUtil foundry tests * Regenerate geth wrappers * Regenerate gas snapshot * Amend L1Fee as gas units, not in wei * Prettier * Revert "Amend L1Fee as gas units, not in wei" This reverts commit 75e47cadbe4a7a58fc731bc57b9dfaa7eb8a7898. * Denote that _getCurrentTxL1GasFees's return is in Wei * (refactor) rework FunctionsBilling unit conversion helper to be juels from wei * Changes from review * Regenerate gas snapshot --------- Co-authored-by: Justin Kaseman <justinkaseman@live.com>
- Loading branch information
1 parent
c2a1b26
commit 0128650
Showing
11 changed files
with
434 additions
and
82 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
78 changes: 78 additions & 0 deletions
78
contracts/src/v0.8/functions/dev/v1_X/ChainSpecificUtil.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.19; | ||
|
||
import {ArbGasInfo} from "../../../vendor/@arbitrum/nitro-contracts/src/precompiles/ArbGasInfo.sol"; | ||
import {OVM_GasPriceOracle} from "../../../vendor/@eth-optimism/contracts/v0.8.9/contracts/L2/predeploys/OVM_GasPriceOracle.sol"; | ||
|
||
/// @dev A library that abstracts out opcodes that behave differently across chains. | ||
/// @dev The methods below return values that are pertinent to the given chain. | ||
library ChainSpecificUtil { | ||
// ------------ Start Arbitrum Constants ------------ | ||
|
||
/// @dev ARBGAS_ADDR is the address of the ArbGasInfo precompile on Arbitrum. | ||
/// @dev reference: https://github.com/OffchainLabs/nitro/blob/v2.0.14/contracts/src/precompiles/ArbGasInfo.sol#L10 | ||
address private constant ARBGAS_ADDR = address(0x000000000000000000000000000000000000006C); | ||
ArbGasInfo private constant ARBGAS = ArbGasInfo(ARBGAS_ADDR); | ||
|
||
uint256 private constant ARB_MAINNET_CHAIN_ID = 42161; | ||
uint256 private constant ARB_GOERLI_TESTNET_CHAIN_ID = 421613; | ||
uint256 private constant ARB_SEPOLIA_TESTNET_CHAIN_ID = 421614; | ||
|
||
// ------------ End Arbitrum Constants ------------ | ||
|
||
// ------------ Start Optimism Constants ------------ | ||
/// @dev L1_FEE_DATA_PADDING includes 35 bytes for L1 data padding for Optimism | ||
bytes internal constant L1_FEE_DATA_PADDING = | ||
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; | ||
/// @dev OVM_GASPRICEORACLE_ADDR is the address of the OVM_GasPriceOracle precompile on Optimism. | ||
/// @dev reference: https://community.optimism.io/docs/developers/build/transaction-fees/#estimating-the-l1-data-fee | ||
address private constant OVM_GASPRICEORACLE_ADDR = address(0x420000000000000000000000000000000000000F); | ||
OVM_GasPriceOracle private constant OVM_GASPRICEORACLE = OVM_GasPriceOracle(OVM_GASPRICEORACLE_ADDR); | ||
|
||
uint256 private constant OP_MAINNET_CHAIN_ID = 10; | ||
uint256 private constant OP_GOERLI_CHAIN_ID = 420; | ||
uint256 private constant OP_SEPOLIA_CHAIN_ID = 11155420; | ||
|
||
/// @dev Base is a OP stack based rollup and follows the same L1 pricing logic as Optimism. | ||
uint256 private constant BASE_MAINNET_CHAIN_ID = 8453; | ||
uint256 private constant BASE_GOERLI_CHAIN_ID = 84531; | ||
uint256 private constant BASE_SEPOLIA_CHAIN_ID = 84532; | ||
|
||
// ------------ End Optimism Constants ------------ | ||
|
||
/// @notice Returns the L1 fees in wei that will be paid for the current transaction, given any calldata | ||
/// @notice for the current transaction. | ||
/// @notice When on a known Arbitrum chain, it uses ArbGas.getCurrentTxL1GasFees to get the fees. | ||
/// @notice On Arbitrum, the provided calldata is not used to calculate the fees. | ||
/// @notice On Optimism, the provided calldata is passed to the OVM_GasPriceOracle predeploy | ||
/// @notice and getL1Fee is called to get the fees. | ||
function _getCurrentTxL1GasFees(bytes memory txCallData) internal view returns (uint256 l1FeeWei) { | ||
uint256 chainid = block.chainid; | ||
if (_isArbitrumChainId(chainid)) { | ||
return ARBGAS.getCurrentTxL1GasFees(); | ||
} else if (_isOptimismChainId(chainid)) { | ||
return OVM_GASPRICEORACLE.getL1Fee(bytes.concat(txCallData, L1_FEE_DATA_PADDING)); | ||
} | ||
return 0; | ||
} | ||
|
||
/// @notice Return true if and only if the provided chain ID is an Arbitrum chain ID. | ||
function _isArbitrumChainId(uint256 chainId) internal pure returns (bool) { | ||
return | ||
chainId == ARB_MAINNET_CHAIN_ID || | ||
chainId == ARB_GOERLI_TESTNET_CHAIN_ID || | ||
chainId == ARB_SEPOLIA_TESTNET_CHAIN_ID; | ||
} | ||
|
||
/// @notice Return true if and only if the provided chain ID is an Optimism (or Base) chain ID. | ||
/// @notice Note that optimism chain id's are also OP stack chain id's. | ||
function _isOptimismChainId(uint256 chainId) internal pure returns (bool) { | ||
return | ||
chainId == OP_MAINNET_CHAIN_ID || | ||
chainId == OP_GOERLI_CHAIN_ID || | ||
chainId == OP_SEPOLIA_CHAIN_ID || | ||
chainId == BASE_MAINNET_CHAIN_ID || | ||
chainId == BASE_GOERLI_CHAIN_ID || | ||
chainId == BASE_SEPOLIA_CHAIN_ID; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
217 changes: 217 additions & 0 deletions
217
contracts/src/v0.8/functions/tests/v1_X/ChainSpecificUtil.t.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,217 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.19; | ||
|
||
import {BaseTest} from "./BaseTest.t.sol"; | ||
import {FunctionsClient} from "../../dev/v1_X/FunctionsClient.sol"; | ||
import {FunctionsRouter} from "../../dev/v1_X/FunctionsRouter.sol"; | ||
import {FunctionsSubscriptions} from "../../dev/v1_X/FunctionsSubscriptions.sol"; | ||
import {FunctionsRequest} from "../../dev/v1_X/libraries/FunctionsRequest.sol"; | ||
import {FunctionsResponse} from "../../dev/v1_X/libraries/FunctionsResponse.sol"; | ||
|
||
import {FunctionsFulfillmentSetup} from "./Setup.t.sol"; | ||
|
||
import {ArbGasInfo} from "../../../vendor/@arbitrum/nitro-contracts/src/precompiles/ArbGasInfo.sol"; | ||
import {OVM_GasPriceOracle} from "../../../vendor/@eth-optimism/contracts/v0.8.9/contracts/L2/predeploys/OVM_GasPriceOracle.sol"; | ||
|
||
/// @notice #_getCurrentTxL1GasFees Arbitrum | ||
/// @dev Arbitrum gas formula = L2 Gas Price * (Gas used on L2 + Extra Buffer for L1 cost) | ||
/// @dev where Extra Buffer for L1 cost = (L1 Estimated Cost / L2 Gas Price) | ||
contract ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum is FunctionsFulfillmentSetup { | ||
address private constant ARBGAS_ADDR = address(0x000000000000000000000000000000000000006C); | ||
uint256 private constant L1_FEE_WEI = 15_818_209_764_247; | ||
|
||
uint96 l1FeeJuels = uint96((1e18 * L1_FEE_WEI) / uint256(LINK_ETH_RATE)); | ||
|
||
function setUp() public virtual override { | ||
vm.mockCall(ARBGAS_ADDR, abi.encodeWithSelector(ArbGasInfo.getCurrentTxL1GasFees.selector), abi.encode(L1_FEE_WEI)); | ||
} | ||
|
||
function test__getCurrentTxL1GasFees_SuccessWhenArbitrumMainnet() public { | ||
// Set the chainID | ||
vm.chainId(42161); | ||
|
||
// Setup sends and fulfills request #1 | ||
FunctionsFulfillmentSetup.setUp(); | ||
|
||
// Check request cost estimate | ||
uint96 expectedEstimatedTotalCostJuels = _getExpectedCostEstimate(s_requests[1].requestData.callbackGasLimit) + | ||
l1FeeJuels; | ||
assertEq(s_requests[1].commitment.estimatedTotalCostJuels, expectedEstimatedTotalCostJuels); | ||
|
||
// Check response actual cost | ||
uint96 expectedTotalCostJuels = _getExpectedCost(5416) + l1FeeJuels; | ||
assertEq(s_responses[1].totalCostJuels, expectedTotalCostJuels); | ||
} | ||
|
||
function test__getCurrentTxL1GasFees_SuccessWhenArbitrumGoerli() public { | ||
// Set the chainID | ||
vm.chainId(421613); | ||
|
||
// Setup sends and fulfills request #1 | ||
FunctionsFulfillmentSetup.setUp(); | ||
|
||
// Check request cost estimate | ||
uint96 expectedEstimatedTotalCostJuels = _getExpectedCostEstimate(s_requests[1].requestData.callbackGasLimit) + | ||
l1FeeJuels; | ||
assertEq(s_requests[1].commitment.estimatedTotalCostJuels, expectedEstimatedTotalCostJuels); | ||
|
||
// Check response actual cost | ||
uint96 expectedTotalCostJuels = _getExpectedCost(5416) + l1FeeJuels; | ||
assertEq(s_responses[1].totalCostJuels, expectedTotalCostJuels); | ||
} | ||
|
||
function test__getCurrentTxL1GasFees_SuccessWhenArbitrumSepolia() public { | ||
// Set the chainID | ||
vm.chainId(421614); | ||
|
||
// Setup sends and fulfills request #1 | ||
FunctionsFulfillmentSetup.setUp(); | ||
|
||
// Check request cost estimate | ||
uint96 expectedEstimatedTotalCostJuels = _getExpectedCostEstimate(s_requests[1].requestData.callbackGasLimit) + | ||
l1FeeJuels; | ||
assertEq(s_requests[1].commitment.estimatedTotalCostJuels, expectedEstimatedTotalCostJuels); | ||
|
||
// Check response actual cost | ||
uint96 expectedTotalCostJuels = _getExpectedCost(5416) + l1FeeJuels; | ||
assertEq(s_responses[1].totalCostJuels, expectedTotalCostJuels); | ||
} | ||
} | ||
|
||
/// @notice #_getCurrentTxL1GasFees Optimism | ||
/// @dev Optimism gas formula = ((l2_base_fee + l2_priority_fee) * l2_gas_used) + L1 data fee | ||
/// @dev where L1 data fee = l1_gas_price * ((count_zero_bytes(tx_data) * 4 + count_non_zero_bytes(tx_data) * 16) + fixed_overhead + noncalldata_gas) * dynamic_overhead | ||
contract ChainSpecificUtil__getCurrentTxL1GasFees_Optimism is FunctionsFulfillmentSetup { | ||
address private constant OVM_GASPRICEORACLE_ADDR = address(0x420000000000000000000000000000000000000F); | ||
uint256 private constant L1_FEE_WEI = 15_818_209_764_247; | ||
|
||
uint96 l1FeeJuels = uint96((1e18 * L1_FEE_WEI) / uint256(LINK_ETH_RATE)); | ||
|
||
function setUp() public virtual override { | ||
vm.mockCall( | ||
OVM_GASPRICEORACLE_ADDR, | ||
abi.encodeWithSelector(OVM_GasPriceOracle.getL1Fee.selector), | ||
abi.encode(L1_FEE_WEI) | ||
); | ||
} | ||
|
||
function test__getCurrentTxL1GasFees_SuccessWhenOptimismMainnet() public { | ||
// Set the chainID | ||
vm.chainId(10); | ||
|
||
// Setup sends and fulfills request #1 | ||
FunctionsFulfillmentSetup.setUp(); | ||
|
||
// Check request cost estimate | ||
uint96 expectedEstimatedTotalCostJuels = _getExpectedCostEstimate(s_requests[1].requestData.callbackGasLimit) + | ||
l1FeeJuels; | ||
assertEq(s_requests[1].commitment.estimatedTotalCostJuels, expectedEstimatedTotalCostJuels); | ||
|
||
// Check response actual cost | ||
uint96 expectedTotalCostJuels = _getExpectedCost(5416) + l1FeeJuels; | ||
assertEq(s_responses[1].totalCostJuels, expectedTotalCostJuels); | ||
} | ||
|
||
function test__getCurrentTxL1GasFees_SuccessWhenOptimismGoerli() public { | ||
// Set the chainID | ||
vm.chainId(420); | ||
|
||
// Setup sends and fulfills request #1 | ||
FunctionsFulfillmentSetup.setUp(); | ||
|
||
// Check request cost estimate | ||
uint96 expectedEstimatedTotalCostJuels = _getExpectedCostEstimate(s_requests[1].requestData.callbackGasLimit) + | ||
l1FeeJuels; | ||
assertEq(s_requests[1].commitment.estimatedTotalCostJuels, expectedEstimatedTotalCostJuels); | ||
|
||
// Check response actual cost | ||
uint96 expectedTotalCostJuels = _getExpectedCost(5416) + l1FeeJuels; | ||
assertEq(s_responses[1].totalCostJuels, expectedTotalCostJuels); | ||
} | ||
|
||
function test__getCurrentTxL1GasFees_SuccessWhenOptimismSepolia() public { | ||
// Set the chainID | ||
vm.chainId(11155420); | ||
|
||
// Setup sends and fulfills request #1 | ||
FunctionsFulfillmentSetup.setUp(); | ||
|
||
// Check request cost estimate | ||
uint96 expectedEstimatedTotalCostJuels = _getExpectedCostEstimate(s_requests[1].requestData.callbackGasLimit) + | ||
l1FeeJuels; | ||
assertEq(s_requests[1].commitment.estimatedTotalCostJuels, expectedEstimatedTotalCostJuels); | ||
|
||
// Check response actual cost | ||
uint96 expectedTotalCostJuels = _getExpectedCost(5416) + l1FeeJuels; | ||
assertEq(s_responses[1].totalCostJuels, expectedTotalCostJuels); | ||
} | ||
} | ||
|
||
/// @notice #_getCurrentTxL1GasFees Base | ||
/// @dev Base gas formula uses Optimism formula = ((l2_base_fee + l2_priority_fee) * l2_gas_used) + L1 data fee | ||
/// @dev where L1 data fee = l1_gas_price * ((count_zero_bytes(tx_data) * 4 + count_non_zero_bytes(tx_data) * 16) + fixed_overhead + noncalldata_gas) * dynamic_overhead | ||
contract ChainSpecificUtil__getCurrentTxL1GasFees_Base is FunctionsFulfillmentSetup { | ||
address private constant OVM_GASPRICEORACLE_ADDR = address(0x420000000000000000000000000000000000000F); | ||
uint256 private constant L1_FEE_WEI = 15_818_209_764_247; | ||
|
||
uint96 l1FeeJuels = uint96((1e18 * L1_FEE_WEI) / uint256(LINK_ETH_RATE)); | ||
|
||
function setUp() public virtual override { | ||
vm.mockCall( | ||
OVM_GASPRICEORACLE_ADDR, | ||
abi.encodeWithSelector(OVM_GasPriceOracle.getL1Fee.selector), | ||
abi.encode(L1_FEE_WEI) | ||
); | ||
} | ||
|
||
function test__getCurrentTxL1GasFees_SuccessWhenBaseMainnet() public { | ||
// Set the chainID | ||
vm.chainId(8453); | ||
|
||
// Setup sends and fulfills request #1 | ||
FunctionsFulfillmentSetup.setUp(); | ||
|
||
// Check request cost estimate | ||
uint96 expectedEstimatedTotalCostJuels = _getExpectedCostEstimate(s_requests[1].requestData.callbackGasLimit) + | ||
l1FeeJuels; | ||
assertEq(s_requests[1].commitment.estimatedTotalCostJuels, expectedEstimatedTotalCostJuels); | ||
|
||
// Check response actual cost | ||
uint96 expectedTotalCostJuels = _getExpectedCost(5416) + l1FeeJuels; | ||
assertEq(s_responses[1].totalCostJuels, expectedTotalCostJuels); | ||
} | ||
|
||
function test__getCurrentTxL1GasFees_SuccessWhenBaseGoerli() public { | ||
// Set the chainID | ||
vm.chainId(84531); | ||
|
||
// Setup sends and fulfills request #1 | ||
FunctionsFulfillmentSetup.setUp(); | ||
|
||
// Check request cost estimate | ||
uint96 expectedEstimatedTotalCostJuels = _getExpectedCostEstimate(s_requests[1].requestData.callbackGasLimit) + | ||
l1FeeJuels; | ||
assertEq(s_requests[1].commitment.estimatedTotalCostJuels, expectedEstimatedTotalCostJuels); | ||
|
||
// Check response actual cost | ||
uint96 expectedTotalCostJuels = _getExpectedCost(5416) + l1FeeJuels; | ||
assertEq(s_responses[1].totalCostJuels, expectedTotalCostJuels); | ||
} | ||
|
||
function test__getCurrentTxL1GasFees_SuccessWhenBaseSepolia() public { | ||
// Set the chainID | ||
vm.chainId(84532); | ||
|
||
// Setup sends and fulfills request #1 | ||
FunctionsFulfillmentSetup.setUp(); | ||
|
||
// Check request cost estimate | ||
uint96 expectedEstimatedTotalCostJuels = _getExpectedCostEstimate(s_requests[1].requestData.callbackGasLimit) + | ||
l1FeeJuels; | ||
assertEq(s_requests[1].commitment.estimatedTotalCostJuels, expectedEstimatedTotalCostJuels); | ||
|
||
// Check response actual cost | ||
uint96 expectedTotalCostJuels = _getExpectedCost(5416) + l1FeeJuels; | ||
assertEq(s_responses[1].totalCostJuels, expectedTotalCostJuels); | ||
} | ||
} |
Oops, something went wrong.