From c3cd6350b49000ecdea734f4b93945330d781d52 Mon Sep 17 00:00:00 2001 From: FelixFan1992 Date: Wed, 1 Nov 2023 07:42:48 -0400 Subject: [PATCH] initial design for automation chain expansion and FG/LN feeds removal --- .../automation/v2_1/KeeperRegistry2_1.sol | 8 +-- .../automation/v2_1/KeeperRegistryBase2_1.sol | 55 ++++++------------- .../v2_1/KeeperRegistryLogicA2_1.sol | 13 +++-- .../v2_1/KeeperRegistryLogicB2_1.sol | 12 ++-- .../ocr2keeper/evm21/encoding/encoder.go | 7 ++- .../ocr2keeper/evm21/encoding/packer.go | 2 + .../evm21/registry_check_pipeline.go | 12 +++- .../ocr2keeper/evm21/streams_lookup.go | 2 +- 8 files changed, 51 insertions(+), 60 deletions(-) diff --git a/contracts/src/v0.8/automation/v2_1/KeeperRegistry2_1.sol b/contracts/src/v0.8/automation/v2_1/KeeperRegistry2_1.sol index 2f96df6d572..c672a4180ed 100644 --- a/contracts/src/v0.8/automation/v2_1/KeeperRegistry2_1.sol +++ b/contracts/src/v0.8/automation/v2_1/KeeperRegistry2_1.sol @@ -90,11 +90,12 @@ contract KeeperRegistry2_1 is KeeperRegistryBase2_1, OCR2Abstract, Chainable, IE upkeepTransmitInfo[i].triggerType = _getTriggerType(report.upkeepIds[i]); upkeepTransmitInfo[i].maxLinkPayment = _getMaxLinkPayment( hotVars, + report.cfgs[i], upkeepTransmitInfo[i].triggerType, uint32(report.gasLimits[i]), uint32(report.performDatas[i].length), - report.fastGasWei, - report.linkNative, +// report.fastGasWei, +// report.linkNative, true ); (upkeepTransmitInfo[i].earlyChecksPassed, upkeepTransmitInfo[i].dedupID) = _prePerformChecks( @@ -152,10 +153,9 @@ contract KeeperRegistry2_1 is KeeperRegistryBase2_1, OCR2Abstract, Chainable, IE (reimbursement, premium) = _postPerformPayment( hotVars, + report.cfgs[i], report.upkeepIds[i], upkeepTransmitInfo[i], - report.fastGasWei, - report.linkNative, numUpkeepsPassedChecks ); totalPremium += premium; diff --git a/contracts/src/v0.8/automation/v2_1/KeeperRegistryBase2_1.sol b/contracts/src/v0.8/automation/v2_1/KeeperRegistryBase2_1.sol index f5d89de5467..5346f349d1d 100644 --- a/contracts/src/v0.8/automation/v2_1/KeeperRegistryBase2_1.sol +++ b/contracts/src/v0.8/automation/v2_1/KeeperRegistryBase2_1.sol @@ -341,8 +341,7 @@ abstract contract KeeperRegistryBase2_1 is ConfirmedOwner, ExecutionPrevention { /// @dev Report transmitted by OCR to transmit function struct Report { - uint256 fastGasWei; - uint256 linkNative; + ChainConfig[] cfgs; uint256[] upkeepIds; uint256[] gasLimits; bytes[] triggers; @@ -405,6 +404,12 @@ abstract contract KeeperRegistryBase2_1 is ConfirmedOwner, ExecutionPrevention { bytes32 blockHash; } + struct ChainConfig { + uint256 fastGas; + uint256 linkNative; + uint256 l1GasCost; // 0 for L1 + } + event AdminPrivilegeConfigSet(address indexed admin, bytes privilegeConfig); event CancelledUpkeepReport(uint256 indexed id, bytes trigger); event DedupKeyAdded(bytes32 indexed dedupKey); @@ -555,48 +560,24 @@ abstract contract KeeperRegistryBase2_1 is ConfirmedOwner, ExecutionPrevention { * @dev calculates LINK paid for gas spent plus a configure premium percentage * @param gasLimit the amount of gas used * @param gasOverhead the amount of gas overhead - * @param fastGasWei the fast gas price - * @param linkNative the exchange ratio between LINK and Native token * @param numBatchedUpkeeps the number of upkeeps in this batch. Used to divide the L1 cost * @param isExecution if this is triggered by a perform upkeep function */ function _calculatePaymentAmount( HotVars memory hotVars, + ChainConfig memory cfg, uint256 gasLimit, uint256 gasOverhead, - uint256 fastGasWei, - uint256 linkNative, uint16 numBatchedUpkeeps, bool isExecution ) internal view returns (uint96, uint96) { - uint256 gasWei = fastGasWei * hotVars.gasCeilingMultiplier; + uint256 gasWei = cfg.fastGas * hotVars.gasCeilingMultiplier; // in case it's actual execution use actual gas price, capped by fastGasWei * gasCeilingMultiplier if (isExecution && tx.gasprice < gasWei) { gasWei = tx.gasprice; } - uint256 l1CostWei = 0; - if (i_mode == Mode.OPTIMISM) { - bytes memory txCallData = new bytes(0); - if (isExecution) { - txCallData = bytes.concat(msg.data, L1_FEE_DATA_PADDING); - } else { - // fee is 4 per 0 byte, 16 per non-zero byte. Worst case we can have - // s_storage.maxPerformDataSize non zero-bytes. Instead of setting bytes to non-zero - // we initialize 'new bytes' of length 4*maxPerformDataSize to cover for zero bytes. - txCallData = new bytes(4 * s_storage.maxPerformDataSize); - } - l1CostWei = OPTIMISM_ORACLE.getL1Fee(txCallData); - } else if (i_mode == Mode.ARBITRUM) { - if (isExecution) { - l1CostWei = ARB_NITRO_ORACLE.getCurrentTxL1GasFees(); - } else { - // fee is 4 per 0 byte, 16 per non-zero byte - we assume all non-zero and - // max data size to calculate max payment - (, uint256 perL1CalldataUnit, , , , ) = ARB_NITRO_ORACLE.getPricesInWei(); - l1CostWei = perL1CalldataUnit * s_storage.maxPerformDataSize * 16; - } - } + uint256 l1CostWei = cfg.l1GasCost; // if it's not performing upkeeps, use gas ceiling multiplier to estimate the upper bound if (!isExecution) { l1CostWei = hotVars.gasCeilingMultiplier * l1CostWei; @@ -604,9 +585,9 @@ abstract contract KeeperRegistryBase2_1 is ConfirmedOwner, ExecutionPrevention { // Divide l1CostWei among all batched upkeeps. Spare change from division is not charged l1CostWei = l1CostWei / numBatchedUpkeeps; - uint256 gasPayment = ((gasWei * (gasLimit + gasOverhead) + l1CostWei) * 1e18) / linkNative; + uint256 gasPayment = ((gasWei * (gasLimit + gasOverhead) + l1CostWei) * 1e18) / cfg.linkNative; uint256 premium = (((gasWei * gasLimit) + l1CostWei) * 1e9 * hotVars.paymentPremiumPPB) / - linkNative + + cfg.linkNative + uint256(hotVars.flatFeeMicroLink) * 1e12; // LINK_TOTAL_SUPPLY < UINT96_MAX @@ -619,20 +600,18 @@ abstract contract KeeperRegistryBase2_1 is ConfirmedOwner, ExecutionPrevention { */ function _getMaxLinkPayment( HotVars memory hotVars, + ChainConfig memory cfg, Trigger triggerType, uint32 performGas, uint32 performDataLength, - uint256 fastGasWei, - uint256 linkNative, bool isExecution // Whether this is an actual perform execution or just a simulation ) internal view returns (uint96) { uint256 gasOverhead = _getMaxGasOverhead(triggerType, performDataLength, hotVars.f); (uint96 reimbursement, uint96 premium) = _calculatePaymentAmount( hotVars, + cfg, performGas, gasOverhead, - fastGasWei, - linkNative, 1, // Consider only 1 upkeep in batch to get maxPayment isExecution ); @@ -871,18 +850,16 @@ abstract contract KeeperRegistryBase2_1 is ConfirmedOwner, ExecutionPrevention { */ function _postPerformPayment( HotVars memory hotVars, + ChainConfig memory cfg, uint256 upkeepId, UpkeepTransmitInfo memory upkeepTransmitInfo, - uint256 fastGasWei, - uint256 linkNative, uint16 numBatchedUpkeeps ) internal returns (uint96 gasReimbursement, uint96 premium) { (gasReimbursement, premium) = _calculatePaymentAmount( hotVars, + cfg, upkeepTransmitInfo.gasUsed, upkeepTransmitInfo.gasOverhead, - fastGasWei, - linkNative, numBatchedUpkeeps, true ); diff --git a/contracts/src/v0.8/automation/v2_1/KeeperRegistryLogicA2_1.sol b/contracts/src/v0.8/automation/v2_1/KeeperRegistryLogicA2_1.sol index e3845ce63bd..3739b2a3c72 100644 --- a/contracts/src/v0.8/automation/v2_1/KeeperRegistryLogicA2_1.sol +++ b/contracts/src/v0.8/automation/v2_1/KeeperRegistryLogicA2_1.sol @@ -46,7 +46,8 @@ contract KeeperRegistryLogicA2_1 is KeeperRegistryBase2_1, Chainable { */ function checkUpkeep( uint256 id, - bytes memory triggerData + bytes memory triggerData, + ChainConfig memory cfg ) public cannotExecute @@ -72,11 +73,12 @@ contract KeeperRegistryLogicA2_1 is KeeperRegistryBase2_1, Chainable { (fastGasWei, linkNative) = _getFeedData(hotVars); uint96 maxLinkPayment = _getMaxLinkPayment( hotVars, + cfg, triggerType, upkeep.performGas, s_storage.maxPerformDataSize, - fastGasWei, - linkNative, + //fastGasWei, + //linkNative, false ); if (upkeep.balance < maxLinkPayment) { @@ -144,7 +146,8 @@ contract KeeperRegistryLogicA2_1 is KeeperRegistryBase2_1, Chainable { * @dev this function may be deprecated in a future version of chainlink automation */ function checkUpkeep( - uint256 id + uint256 id, + ChainConfig memory cfg ) external returns ( @@ -157,7 +160,7 @@ contract KeeperRegistryLogicA2_1 is KeeperRegistryBase2_1, Chainable { uint256 linkNative ) { - return checkUpkeep(id, bytes("")); + return checkUpkeep(id, bytes(""), cfg); } /** diff --git a/contracts/src/v0.8/automation/v2_1/KeeperRegistryLogicB2_1.sol b/contracts/src/v0.8/automation/v2_1/KeeperRegistryLogicB2_1.sol index b394bff4ee8..42b47ae956f 100644 --- a/contracts/src/v0.8/automation/v2_1/KeeperRegistryLogicB2_1.sol +++ b/contracts/src/v0.8/automation/v2_1/KeeperRegistryLogicB2_1.sol @@ -455,8 +455,8 @@ contract KeeperRegistryLogicB2_1 is KeeperRegistryBase2_1 { * @notice calculates the minimum balance required for an upkeep to remain eligible * @param id the upkeep id to calculate minimum balance for */ - function getMinBalance(uint256 id) external view returns (uint96) { - return getMinBalanceForUpkeep(id); + function getMinBalance(uint256 id, ChainConfig memory cfg) external view returns (uint96) { + return getMinBalanceForUpkeep(id, cfg); } /** @@ -464,19 +464,19 @@ contract KeeperRegistryLogicB2_1 is KeeperRegistryBase2_1 { * @param id the upkeep id to calculate minimum balance for * @dev this will be deprecated in a future version in favor of getMinBalance */ - function getMinBalanceForUpkeep(uint256 id) public view returns (uint96 minBalance) { - return getMaxPaymentForGas(_getTriggerType(id), s_upkeep[id].performGas); + function getMinBalanceForUpkeep(uint256 id, ChainConfig memory cfg) public view returns (uint96 minBalance) { + return getMaxPaymentForGas(_getTriggerType(id), cfg, s_upkeep[id].performGas); } /** * @notice calculates the maximum payment for a given gas limit * @param gasLimit the gas to calculate payment for */ - function getMaxPaymentForGas(Trigger triggerType, uint32 gasLimit) public view returns (uint96 maxPayment) { + function getMaxPaymentForGas(Trigger triggerType, ChainConfig memory cfg, uint32 gasLimit) public view returns (uint96 maxPayment) { HotVars memory hotVars = s_hotVars; (uint256 fastGasWei, uint256 linkNative) = _getFeedData(hotVars); return - _getMaxLinkPayment(hotVars, triggerType, gasLimit, s_storage.maxPerformDataSize, fastGasWei, linkNative, false); + _getMaxLinkPayment(hotVars, cfg, triggerType, gasLimit, s_storage.maxPerformDataSize, false); } /** diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/encoder.go b/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/encoder.go index 239de099c01..6ca663d4691 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/encoder.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/encoder.go @@ -32,8 +32,9 @@ func (e reportEncoder) Encode(results ...ocr2keepers.CheckResult) ([]byte, error } report := automation_utils_2_1.KeeperRegistryBase21Report{ - FastGasWei: big.NewInt(0), - LinkNative: big.NewInt(0), + FastGasWei: big.NewInt(0), + LinkNative: big.NewInt(0), + // Cfgs: make([]ChainConfig, len(results)), UpkeepIds: make([]*big.Int, len(results)), GasLimits: make([]*big.Int, len(results)), Triggers: make([][]byte, len(results)), @@ -55,7 +56,7 @@ func (e reportEncoder) Encode(results ...ocr2keepers.CheckResult) ([]byte, error report.LinkNative = result.LinkNative } } - + // report.Cfgs[i] = ChainConfig(result.l1GasCost, result.fastGas, result.linkNative) id := result.UpkeepID.BigInt() report.UpkeepIds[i] = id report.GasLimits[i] = big.NewInt(0).SetUint64(result.GasAllocated) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/packer.go b/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/packer.go index 9e070b2838c..34f5de8dce9 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/packer.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/packer.go @@ -150,12 +150,14 @@ func (p *abiPacker) UnpackReport(raw []byte) (automation_utils_2_1.KeeperRegistr GasLimits: make([]*big.Int, len(converted.GasLimits)), Triggers: make([][]byte, len(converted.Triggers)), PerformDatas: make([][]byte, len(converted.PerformDatas)), + // Cfgs: make([]ChainConfig, len(converted.Cfgs)), } if len(report.UpkeepIds) > 0 { copy(report.UpkeepIds, converted.UpkeepIds) copy(report.GasLimits, converted.GasLimits) copy(report.Triggers, converted.Triggers) copy(report.PerformDatas, converted.PerformDatas) + //copy(report.Cfgs, converted.Cfgs) } return report, nil diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/registry_check_pipeline.go b/core/services/ocr2/plugins/ocr2keeper/evm21/registry_check_pipeline.go index d3530994702..b4f75c029a6 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/registry_check_pipeline.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/registry_check_pipeline.go @@ -187,6 +187,14 @@ func (r *EvmRegistry) checkUpkeeps(ctx context.Context, payloads []ocr2keepers.U continue } + // call gas estimator (ge) component to get L2 gas cost + // l1GasCost = ge.getL1GasCost(chain_id, block_id, block_hash, tx_call_data) + // fast_gas = ... + // link_native = ... + // results[i].l1GasCost = l1GasCost + // results[i].fastGas = fastGas + // results[i].linkNative = linkNative + opts := r.buildCallOpts(ctx, block) var payload []byte var err error @@ -201,7 +209,7 @@ func (r *EvmRegistry) checkUpkeeps(ctx context.Context, payloads []ocr2keepers.U } // check data will include the log trigger config - payload, err = r.abi.Pack("checkUpkeep", upkeepId, p.CheckData) + payload, err = r.abi.Pack("checkUpkeep", upkeepId, p.CheckData /* ChainConfig(l1GasCost, fast_gas, link_native) */) if err != nil { // pack error, no retryable r.lggr.Warnf("failed to pack log trigger checkUpkeep data for upkeepId %s with check data %s: %s", upkeepId, hexutil.Encode(p.CheckData), err) @@ -211,7 +219,7 @@ func (r *EvmRegistry) checkUpkeeps(ctx context.Context, payloads []ocr2keepers.U default: // checkUpkeep is overloaded on the contract for conditionals and log upkeeps // Need to use the first function (checkUpkeep0) for conditionals - payload, err = r.abi.Pack("checkUpkeep0", upkeepId) + payload, err = r.abi.Pack("checkUpkeep0", upkeepId /* ChainConfig(l1GasCost, fast_gas, link_native) */) if err != nil { // pack error, no retryable r.lggr.Warnf("failed to pack conditional checkUpkeep data for upkeepId %s with check data %s: %s", upkeepId, hexutil.Encode(p.CheckData), err) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/streams_lookup.go b/core/services/ocr2/plugins/ocr2keeper/evm21/streams_lookup.go index 6f2594b6c38..28804985723 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/streams_lookup.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/streams_lookup.go @@ -533,7 +533,7 @@ func (r *EvmRegistry) multiFeedsRequest(ctx context.Context, ch chan<- MercuryDa // hence, retry in this case. retry will help when we send a very new timestamp and reports are not yet generated if len(response.Reports) != len(sl.Feeds) { // TODO: AUTO-5044: calculate what reports are missing and log a warning - lggr.Warnf("at timestamp %s upkeep %s mercury v0.3 server retruned 200 status with %d reports while we requested %d feeds, treating as 404 (not found) and retrying", sl.Time.String(), sl.upkeepId.String(), len(response.Reports), len(sl.Feeds)) + lggr.Warnf("at timestamp %s upkeep %s mercury v0.3 server returned 200 status with %d reports while we requested %d feeds, treating as 404 (not found) and retrying", sl.Time.String(), sl.upkeepId.String(), len(response.Reports), len(sl.Feeds)) retryable = true state = encoding.MercuryFlakyFailure return fmt.Errorf("%d", http.StatusNotFound)