Skip to content

Commit

Permalink
Merge branch 'develop' into BCI-3658-Implement-the-dynamic-gas-limit-…
Browse files Browse the repository at this point in the history
…estimation-feature
  • Loading branch information
amit-momin committed Aug 21, 2024
2 parents 4dc5cb5 + 633eb41 commit 03db68e
Show file tree
Hide file tree
Showing 20 changed files with 1,062 additions and 83 deletions.
5 changes: 5 additions & 0 deletions .changeset/hot-laws-deny.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"chainlink": patch
---

#internal log info on missed finalized head instead of returning an error
2 changes: 1 addition & 1 deletion contracts/scripts/native_solc_compile_all_automation
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ compileContract automation/v2_2/AutomationUtils2_2.sol
compileContract automation/interfaces/v2_2/IAutomationRegistryMaster.sol
compileContract automation/chains/ArbitrumModule.sol
compileContract automation/chains/ChainModuleBase.sol
compileContract automation/chains/OptimismModule.sol
compileContract automation/chains/OptimismModuleV2.sol
compileContract automation/chains/ScrollModule.sol
compileContract automation/interfaces/IChainModule.sol
compileContract automation/interfaces/IAutomationV21PlusCommon.sol
Expand Down
4 changes: 2 additions & 2 deletions contracts/src/v0.8/automation/chains/ChainModuleBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ contract ChainModuleBase is IChainModule {
return blockhash(n);
}

function getCurrentL1Fee(uint256) external view virtual returns (uint256) {
function getCurrentL1Fee(uint256) external view virtual returns (uint256 l1Fee) {
return 0;
}

function getMaxL1Fee(uint256) external view virtual returns (uint256) {
function getMaxL1Fee(uint256) external view virtual returns (uint256 maxL1Fee) {
return 0;
}

Expand Down
11 changes: 10 additions & 1 deletion contracts/src/v0.8/automation/chains/OptimismModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ pragma solidity 0.8.19;
import {OVM_GasPriceOracle} from "../../vendor/@eth-optimism/contracts/v0.8.9/contracts/L2/predeploys/OVM_GasPriceOracle.sol";
import {ChainModuleBase} from "./ChainModuleBase.sol";

/**
* @notice This contract is deprecated. Please use OptimismModuleV2 which utilizes the most recent offerings from OP
* and can estimate L1 fee with much lower cost.
*/
contract OptimismModule is ChainModuleBase {
/// @dev OP_L1_DATA_FEE_PADDING includes 80 bytes for L1 data padding for Optimism and BASE
bytes private constant OP_L1_DATA_FEE_PADDING =
Expand All @@ -25,10 +29,15 @@ contract OptimismModule is ChainModuleBase {
return _getL1Fee(dataSize);
}

/* @notice this function provides an estimation for L1 fee incurred by calldata of a certain size
* @dev this function uses the getL1Fee function in OP gas price oracle. it estimates the exact L1 fee but it costs
* a lot of gas to call.
* @param dataSize the size of calldata
* @return l1Fee the L1 fee
*/
function _getL1Fee(uint256 dataSize) internal view returns (uint256) {
// fee is 4 per 0 byte, 16 per non-zero byte. Worst case we can have all non zero-bytes.
// Instead of setting bytes to non-zero, we initialize 'new bytes' of length 4*dataSize to cover for zero bytes.
// this is the same as OP.
bytes memory txCallData = new bytes(4 * dataSize);
return OVM_GASPRICEORACLE.getL1Fee(bytes.concat(txCallData, OP_L1_DATA_FEE_PADDING));
}
Expand Down
78 changes: 78 additions & 0 deletions contracts/src/v0.8/automation/chains/OptimismModuleV2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

import {GasPriceOracle as OVM_GasPriceOracle} from "../../vendor/@eth-optimism/contracts-bedrock/v0.17.3/src/L2/GasPriceOracle.sol";
import {ChainModuleBase} from "./ChainModuleBase.sol";
import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol";

/**
* @notice OptimismModuleV2 provides a cost-efficient way to get L1 fee on OP stack.
* After EIP-4844 is implemented in OP stack, the new OP upgrade includes a new function getL1FeeUpperBound to estimate
* the upper bound of current transaction's L1 fee.
*/
contract OptimismModuleV2 is ChainModuleBase, ConfirmedOwner {
error InvalidL1FeeCoefficient(uint8 coefficient);
event L1FeeCoefficientSet(uint8 coefficient);

/// @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);

/// @dev L1 fee coefficient is used to account for the impact of data compression on the l1 fee
/// getL1FeeUpperBound returns the upper bound of l1 fee so this configurable coefficient will help
/// charge a predefined percentage of the upper bound.
uint8 private s_l1FeeCoefficient = 100;
uint256 private constant FIXED_GAS_OVERHEAD = 28_000;
uint256 private constant PER_CALLDATA_BYTE_GAS_OVERHEAD = 0;

constructor() ConfirmedOwner(msg.sender) {}

function getCurrentL1Fee(uint256 dataSize) external view override returns (uint256) {
return (s_l1FeeCoefficient * _getL1Fee(dataSize)) / 100;
}

function getMaxL1Fee(uint256 dataSize) external view override returns (uint256) {
return _getL1Fee(dataSize);
}

/* @notice this function provides an estimation for L1 fee incurred by calldata of a certain size
* @dev this function uses the newly provided getL1FeeUpperBound function in OP gas price oracle. this helps
* estimate L1 fee with much lower cost
* @param dataSize the size of calldata
* @return l1Fee the L1 fee
*/
function _getL1Fee(uint256 dataSize) internal view returns (uint256) {
return OVM_GASPRICEORACLE.getL1FeeUpperBound(dataSize);
}

function getGasOverhead()
external
pure
override
returns (uint256 chainModuleFixedOverhead, uint256 chainModulePerByteOverhead)
{
return (FIXED_GAS_OVERHEAD, PER_CALLDATA_BYTE_GAS_OVERHEAD);
}

/* @notice this function sets a new coefficient for L1 fee estimation.
* @dev this function can only be invoked by contract owner
* @param coefficient the new coefficient
*/
function setL1FeeCalculation(uint8 coefficient) external onlyOwner {
if (coefficient > 100) {
revert InvalidL1FeeCoefficient(coefficient);
}

s_l1FeeCoefficient = coefficient;

emit L1FeeCoefficientSet(coefficient);
}

/* @notice this function returns the s_l1FeeCoefficient
* @return coefficient the current s_l1FeeCoefficient in effect
*/
function getL1FeeCoefficient() public view returns (uint256 coefficient) {
return s_l1FeeCoefficient;
}
}
19 changes: 17 additions & 2 deletions contracts/src/v0.8/automation/chains/ScrollModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ contract ScrollModule is ChainModuleBase, ConfirmedOwner {
IScrollL1GasPriceOracle private constant SCROLL_ORACLE = IScrollL1GasPriceOracle(SCROLL_ORACLE_ADDR);

/// @dev L1 fee coefficient can be applied to reduce possibly inflated gas cost
uint8 public s_l1FeeCoefficient = 100;
uint8 private s_l1FeeCoefficient = 100;
uint256 private constant FIXED_GAS_OVERHEAD = 45_000;
uint256 private constant PER_CALLDATA_BYTE_GAS_OVERHEAD = 170;

Expand All @@ -34,7 +34,11 @@ contract ScrollModule is ChainModuleBase, ConfirmedOwner {
return _getL1Fee(dataSize);
}

function _getL1Fee(uint256 dataSize) internal view returns (uint256) {
/* @notice this function provides an estimation for L1 fee incurred by calldata of a certain size
* @param dataSize the size of calldata
* @return l1Fee the L1 fee
*/
function _getL1Fee(uint256 dataSize) internal view returns (uint256 l1Fee) {
// fee is 4 per 0 byte, 16 per non-zero byte. Worst case we can have all non zero-bytes.
// Instead of setting bytes to non-zero, we initialize 'new bytes' of length 4*dataSize to cover for zero bytes.
// this is the same as OP.
Expand All @@ -51,6 +55,10 @@ contract ScrollModule is ChainModuleBase, ConfirmedOwner {
return (FIXED_GAS_OVERHEAD, PER_CALLDATA_BYTE_GAS_OVERHEAD);
}

/* @notice this function sets a new coefficient for L1 fee estimation.
* @dev this function can only be invoked by contract owner
* @param coefficient the new coefficient
*/
function setL1FeeCalculation(uint8 coefficient) external onlyOwner {
if (coefficient > 100) {
revert InvalidL1FeeCoefficient(coefficient);
Expand All @@ -60,4 +68,11 @@ contract ScrollModule is ChainModuleBase, ConfirmedOwner {

emit L1FeeCoefficientSet(coefficient);
}

/* @notice this function returns the s_l1FeeCoefficient
* @return coefficient the current s_l1FeeCoefficient in effect
*/
function getL1FeeCoefficient() public view returns (uint256 coefficient) {
return s_l1FeeCoefficient;
}
}
45 changes: 29 additions & 16 deletions contracts/src/v0.8/automation/interfaces/IChainModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,39 @@
pragma solidity ^0.8.0;

interface IChainModule {
// retrieve the native block number of a chain. e.g. L2 block number on Arbitrum
function blockNumber() external view returns (uint256);
/* @notice this function provides the block number of current chain.
* @dev certain chains have its own function to retrieve block number, e.g. Arbitrum
* @return blockNumber the block number of the current chain.
*/
function blockNumber() external view returns (uint256 blockNumber);

// retrieve the native block hash of a chain.
function blockHash(uint256) external view returns (bytes32);
/* @notice this function provides the block hash of a block number.
* @dev this function can usually look back 256 blocks at most, unless otherwise specified
* @param blockNumber the block number
* @return blockHash the block hash of the input block number
*/
function blockHash(uint256 blockNumber) external view returns (bytes32 blockHash);

// retrieve the L1 data fee for a L2 transaction. it should return 0 for L1 chains and
// L2 chains which don't have L1 fee component. it uses msg.data to estimate L1 data so
// it must be used with a transaction. Return value in wei.
function getCurrentL1Fee(uint256 dataSize) external view returns (uint256);
/* @notice this function provides the L1 fee of current transaction.
* @dev retrieve the L1 data fee for a L2 transaction. it should return 0 for L1 chains. it should
* return 0 for L2 chains if they don't have L1 fee component.
* @param dataSize the calldata size of the current transaction
* @return l1Fee the L1 fee in wei incurred by calldata of this data size
*/
function getCurrentL1Fee(uint256 dataSize) external view returns (uint256 l1Fee);

// retrieve the L1 data fee for a L2 simulation. it should return 0 for L1 chains and
// L2 chains which don't have L1 fee component. Return value in wei.
function getMaxL1Fee(uint256 dataSize) external view returns (uint256);
/* @notice this function provides the max possible L1 fee of current transaction.
* @dev retrieve the max possible L1 data fee for a L2 transaction. it should return 0 for L1 chains. it should
* return 0 for L2 chains if they don't have L1 fee component.
* @param dataSize the calldata size of the current transaction
* @return maxL1Fee the max possible L1 fee in wei incurred by calldata of this data size
*/
function getMaxL1Fee(uint256 dataSize) external view returns (uint256 maxL1Fee);

// Returns an upper bound on execution gas cost for one invocation of blockNumber(),
// one invocation of blockHash() and one invocation of getCurrentL1Fee().
// Returns two values, first value indicates a fixed cost and the second value is
// the cost per msg.data byte (As some chain module's getCurrentL1Fee execution cost
// scales with calldata size)
/* @notice this function provides the overheads of calling this chain module.
* @return chainModuleFixedOverhead the fixed overhead incurred by calling this chain module
* @return chainModulePerByteOverhead the fixed overhead per byte incurred by calling this chain module with calldata
*/
function getGasOverhead()
external
view
Expand Down
7 changes: 6 additions & 1 deletion contracts/src/v0.8/tests/MockOVMGasPriceOracle.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.6;

contract MockOVMGasPriceOracle {
function getL1Fee(bytes memory _data) public view returns (uint256) {
function getL1Fee(bytes memory) public pure returns (uint256) {
return 2000000;
}

function getL1FeeUpperBound(uint256) public pure returns (uint256) {
return 2000000;
}
}
13 changes: 7 additions & 6 deletions contracts/test/v0.8/automation/AutomationRegistry2_2.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { MockArbGasInfo__factory as MockArbGasInfoFactory } from '../../../typec
import { MockOVMGasPriceOracle__factory as MockOVMGasPriceOracleFactory } from '../../../typechain/factories/MockOVMGasPriceOracle__factory'
import { ChainModuleBase__factory as ChainModuleBaseFactory } from '../../../typechain/factories/ChainModuleBase__factory'
import { ArbitrumModule__factory as ArbitrumModuleFactory } from '../../../typechain/factories/ArbitrumModule__factory'
import { OptimismModule__factory as OptimismModuleFactory } from '../../../typechain/factories/OptimismModule__factory'
import { OptimismModuleV2__factory as OptimismModuleV2Factory } from '../../../typechain/factories/OptimismModuleV2__factory'
import { ILogAutomation__factory as ILogAutomationactory } from '../../../typechain/factories/ILogAutomation__factory'
import { IAutomationForwarder__factory as IAutomationForwarderFactory } from '../../../typechain/factories/IAutomationForwarder__factory'
import { MockArbSys__factory as MockArbSysFactory } from '../../../typechain/factories/MockArbSys__factory'
Expand All @@ -35,7 +35,7 @@ import { MockV3Aggregator } from '../../../typechain/MockV3Aggregator'
import { UpkeepMock } from '../../../typechain/UpkeepMock'
import { ChainModuleBase } from '../../../typechain/ChainModuleBase'
import { ArbitrumModule } from '../../../typechain/ArbitrumModule'
import { OptimismModule } from '../../../typechain/OptimismModule'
import { OptimismModuleV2 } from '../../../typechain/OptimismModuleV2'
import { UpkeepTranscoder } from '../../../typechain/UpkeepTranscoder'
import { IChainModule, UpkeepAutoFunder } from '../../../typechain'
import {
Expand Down Expand Up @@ -148,7 +148,7 @@ let upkeepMockFactory: UpkeepMockFactory
let upkeepAutoFunderFactory: UpkeepAutoFunderFactory
let chainModuleBaseFactory: ChainModuleBaseFactory
let arbitrumModuleFactory: ArbitrumModuleFactory
let optimismModuleFactory: OptimismModuleFactory
let optimismModuleV2Factory: OptimismModuleV2Factory
let streamsLookupUpkeepFactory: StreamsLookupUpkeepFactory
let personas: Personas

Expand All @@ -169,7 +169,7 @@ let ltUpkeep: MockContract
let transcoder: UpkeepTranscoder
let chainModuleBase: ChainModuleBase
let arbitrumModule: ArbitrumModule
let optimismModule: OptimismModule
let optimismModule: OptimismModuleV2
let streamsLookupUpkeep: StreamsLookupUpkeep
let automationUtils: AutomationCompatibleUtils

Expand Down Expand Up @@ -430,7 +430,8 @@ describe('AutomationRegistry2_2', () => {
await ethers.getContractFactory('UpkeepAutoFunder')
chainModuleBaseFactory = await ethers.getContractFactory('ChainModuleBase')
arbitrumModuleFactory = await ethers.getContractFactory('ArbitrumModule')
optimismModuleFactory = await ethers.getContractFactory('OptimismModule')
optimismModuleV2Factory =
await ethers.getContractFactory('OptimismModuleV2')
streamsLookupUpkeepFactory = await ethers.getContractFactory(
'StreamsLookupUpkeep',
)
Expand Down Expand Up @@ -844,7 +845,7 @@ describe('AutomationRegistry2_2', () => {
.deploy()
chainModuleBase = await chainModuleBaseFactory.connect(owner).deploy()
arbitrumModule = await arbitrumModuleFactory.connect(owner).deploy()
optimismModule = await optimismModuleFactory.connect(owner).deploy()
optimismModule = await optimismModuleV2Factory.connect(owner).deploy()
streamsLookupUpkeep = await streamsLookupUpkeepFactory
.connect(owner)
.deploy(
Expand Down
10 changes: 5 additions & 5 deletions contracts/test/v0.8/automation/AutomationRegistry2_3.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { MockArbGasInfo__factory as MockArbGasInfoFactory } from '../../../typec
import { MockOVMGasPriceOracle__factory as MockOVMGasPriceOracleFactory } from '../../../typechain/factories/MockOVMGasPriceOracle__factory'
import { ChainModuleBase__factory as ChainModuleBaseFactory } from '../../../typechain/factories/ChainModuleBase__factory'
import { ArbitrumModule__factory as ArbitrumModuleFactory } from '../../../typechain/factories/ArbitrumModule__factory'
import { OptimismModule__factory as OptimismModuleFactory } from '../../../typechain/factories/OptimismModule__factory'
import { OptimismModuleV2__factory as OptimismModuleV2Factory } from '../../../typechain/factories/OptimismModuleV2__factory'
import { ILogAutomation__factory as ILogAutomationactory } from '../../../typechain/factories/ILogAutomation__factory'
import { MockArbSys__factory as MockArbSysFactory } from '../../../typechain/factories/MockArbSys__factory'
import { AutomationCompatibleUtils } from '../../../typechain/AutomationCompatibleUtils'
Expand All @@ -34,7 +34,7 @@ import { MockV3Aggregator } from '../../../typechain/MockV3Aggregator'
import { UpkeepMock } from '../../../typechain/UpkeepMock'
import { ChainModuleBase } from '../../../typechain/ChainModuleBase'
import { ArbitrumModule } from '../../../typechain/ArbitrumModule'
import { OptimismModule } from '../../../typechain/OptimismModule'
import { OptimismModuleV2 } from '../../../typechain/OptimismModuleV2'
import { UpkeepTranscoder } from '../../../typechain/UpkeepTranscoder'
import { IChainModule, UpkeepAutoFunder } from '../../../typechain'
import {
Expand Down Expand Up @@ -152,7 +152,7 @@ let upkeepMockFactory: UpkeepMockFactory
let upkeepAutoFunderFactory: UpkeepAutoFunderFactory
let chainModuleBaseFactory: ChainModuleBaseFactory
let arbitrumModuleFactory: ArbitrumModuleFactory
let optimismModuleFactory: OptimismModuleFactory
let optimismModuleFactory: OptimismModuleV2Factory
let streamsLookupUpkeepFactory: StreamsLookupUpkeepFactory
let personas: Personas

Expand All @@ -174,7 +174,7 @@ let ltUpkeep: MockContract
let transcoder: UpkeepTranscoder
let chainModuleBase: ChainModuleBase
let arbitrumModule: ArbitrumModule
let optimismModule: OptimismModule
let optimismModule: OptimismModuleV2
let streamsLookupUpkeep: StreamsLookupUpkeep
let automationUtils: AutomationCompatibleUtils
let automationUtils2_3: AutomationUtils2_3
Expand Down Expand Up @@ -442,7 +442,7 @@ describe('AutomationRegistry2_3', () => {
await ethers.getContractFactory('UpkeepAutoFunder')
chainModuleBaseFactory = await ethers.getContractFactory('ChainModuleBase')
arbitrumModuleFactory = await ethers.getContractFactory('ArbitrumModule')
optimismModuleFactory = await ethers.getContractFactory('OptimismModule')
optimismModuleFactory = await ethers.getContractFactory('OptimismModuleV2')
streamsLookupUpkeepFactory = await ethers.getContractFactory(
'StreamsLookupUpkeep',
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package ccipreader
import (
"context"
"math/big"
"sort"
"testing"
"time"

Expand Down Expand Up @@ -110,7 +111,7 @@ func TestCCIPReader_CommitReportsGTETimestamp(t *testing.T) {
)
require.NoError(t, err)
return len(reports) == numReports-1
}, testutils.WaitTimeout(t), 50*time.Millisecond)
}, 10*time.Second, 50*time.Millisecond)

assert.Len(t, reports[0].Report.MerkleRoots, 1)
assert.Equal(t, chainS1, reports[0].Report.MerkleRoots[0].ChainSel)
Expand Down Expand Up @@ -250,6 +251,10 @@ func TestCCIPReader_MsgsBetweenSeqNums(t *testing.T) {

s.sb.Commit()

// Need to replay as sometimes the logs are not picked up by the log poller (?)
// Maybe another situation where chain reader doesn't register filters as expected.
require.NoError(t, s.lp.Replay(ctx, 1))

var msgs []cciptypes.Message
require.Eventually(t, func() bool {
msgs, err = s.reader.MsgsBetweenSeqNums(
Expand All @@ -262,6 +267,10 @@ func TestCCIPReader_MsgsBetweenSeqNums(t *testing.T) {
}, 10*time.Second, 100*time.Millisecond)

require.Len(t, msgs, 2)
// sort to ensure ascending order of sequence numbers.
sort.Slice(msgs, func(i, j int) bool {
return msgs[i].Header.SequenceNumber < msgs[j].Header.SequenceNumber
})
require.Equal(t, cciptypes.SeqNum(10), msgs[0].Header.SequenceNumber)
require.Equal(t, cciptypes.SeqNum(15), msgs[1].Header.SequenceNumber)
for _, msg := range msgs {
Expand Down
Loading

0 comments on commit 03db68e

Please sign in to comment.