Skip to content

Commit

Permalink
convert link/native to link/usd and native/usd (#12309)
Browse files Browse the repository at this point in the history
* convert link/native to link/usd and native/usd

* fix tests

* add gethwrappers

* update tests to skip flakey

* fix failing registrar test
  • Loading branch information
RyanRHall authored Mar 8, 2024
1 parent 1d3df02 commit c258b2e
Show file tree
Hide file tree
Showing 15 changed files with 838 additions and 647 deletions.

Large diffs are not rendered by default.

16 changes: 12 additions & 4 deletions contracts/src/v0.8/automation/dev/test/AutomationRegistry2_3.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ import {AutomationRegistryLogicA2_3} from "../v2_3/AutomationRegistryLogicA2_3.s
import {AutomationRegistryLogicB2_3} from "../v2_3/AutomationRegistryLogicB2_3.sol";
import {IAutomationRegistryMaster2_3, AutomationRegistryBase2_3} from "../interfaces/v2_3/IAutomationRegistryMaster2_3.sol";
import {ChainModuleBase} from "../../chains/ChainModuleBase.sol";
import {MockV3Aggregator} from "../../../tests/MockV3Aggregator.sol";

contract AutomationRegistry2_3_SetUp is BaseTest {
address internal constant LINK_ETH_FEED = 0x1111111111111111111111111111111111111110;
address internal constant FAST_GAS_FEED = 0x1111111111111111111111111111111111111112;
address internal LINK_USD_FEED;
address internal NATIVE_USD_FEED;
address internal FAST_GAS_FEED;
address internal constant LINK_TOKEN = 0x1111111111111111111111111111111111111113;
address internal constant ZERO_ADDRESS = address(0);

Expand All @@ -31,6 +33,10 @@ contract AutomationRegistry2_3_SetUp is BaseTest {
IAutomationRegistryMaster2_3 internal registryMaster;

function setUp() public override {
LINK_USD_FEED = address(new MockV3Aggregator(8, 2_000_000_000)); // $20
NATIVE_USD_FEED = address(new MockV3Aggregator(8, 400_000_000_000)); // $4,000
FAST_GAS_FEED = address(new MockV3Aggregator(0, 1_000_000_000)); // 1 gwei

s_valid_transmitters = new address[](4);
for (uint160 i = 0; i < 4; ++i) {
s_valid_transmitters[i] = address(4 + i);
Expand All @@ -48,7 +54,8 @@ contract AutomationRegistry2_3_SetUp is BaseTest {
AutomationForwarderLogic forwarderLogic = new AutomationForwarderLogic();
AutomationRegistryLogicB2_3 logicB2_3 = new AutomationRegistryLogicB2_3(
LINK_TOKEN,
LINK_ETH_FEED,
LINK_USD_FEED,
NATIVE_USD_FEED,
FAST_GAS_FEED,
address(forwarderLogic),
ZERO_ADDRESS
Expand Down Expand Up @@ -108,7 +115,8 @@ contract AutomationRegistry2_3_SetConfig is AutomationRegistry2_3_SetUp {
maxPerformDataSize: 5_000,
maxRevertDataSize: 5_000,
fallbackGasPrice: 20_000_000_000,
fallbackLinkPrice: 200_000_000_000,
fallbackLinkPrice: 2_000_000_000, // $20
fallbackNativePrice: 400_000_000_000, // $4,000
transcoder: 0xB1e66855FD67f6e85F0f0fA38cd6fBABdf00923c,
registrars: s_registrars,
upkeepPrivilegeManager: 0xD9c855F08A7e460691F41bBDDe6eC310bc0593D8,
Expand Down
32 changes: 18 additions & 14 deletions contracts/src/v0.8/automation/dev/v2_3/AutomationRegistry2_3.sol
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,15 @@ contract AutomationRegistry2_3 is AutomationRegistryBase2_3, OCR2Abstract, Chain
string public constant override typeAndVersion = "AutomationRegistry 2.3.0";

/**
* @param logicA the address of the first logic contract, but cast as logicB in order to call logicB functions
* @param logicA the address of the first logic contract, but cast as logicB in order to call logicB functions (via fallback)
*/
constructor(
AutomationRegistryLogicB2_3 logicA
)
AutomationRegistryBase2_3(
logicA.getLinkAddress(),
logicA.getLinkNativeFeedAddress(),
logicA.getLinkUSDFeedAddress(),
logicA.getNativeUSDFeedAddress(),
logicA.getFastGasFeedAddress(),
logicA.getAutomationForwarderLogic(),
logicA.getAllowedReadOnlyAddress()
Expand Down Expand Up @@ -168,26 +169,28 @@ contract AutomationRegistry2_3 is AutomationRegistryBase2_3, OCR2Abstract, Chain
gasOverhead = gasOverhead / transmitVars.numUpkeepsPassedChecks + ACCOUNTING_PER_UPKEEP_GAS_OVERHEAD;

{
uint96 reimbursement;
uint96 premium;
for (uint256 i = 0; i < report.upkeepIds.length; i++) {
if (upkeepTransmitInfo[i].earlyChecksPassed) {
(reimbursement, premium) = _postPerformPayment(
PaymentReceipt memory receipt = _handlePayment(
hotVars,
report.upkeepIds[i],
upkeepTransmitInfo[i].gasUsed,
report.fastGasWei,
report.linkNative,
gasOverhead,
(l1Fee * upkeepTransmitInfo[i].calldataWeight) / transmitVars.totalCalldataWeight
PaymentParams({
gasLimit: upkeepTransmitInfo[i].gasUsed,
gasOverhead: gasOverhead,
l1CostWei: (l1Fee * upkeepTransmitInfo[i].calldataWeight) / transmitVars.totalCalldataWeight,
fastGasWei: report.fastGasWei,
linkUSD: report.linkUSD,
nativeUSD: _getNativeUSD(hotVars),
isTransaction: true
}),
report.upkeepIds[i]
);
transmitVars.totalPremium += premium;
transmitVars.totalReimbursement += reimbursement;
transmitVars.totalPremium += receipt.premium;
transmitVars.totalReimbursement += receipt.reimbursement;

emit UpkeepPerformed(
report.upkeepIds[i],
upkeepTransmitInfo[i].performSuccess,
reimbursement + premium,
receipt.reimbursement + receipt.premium,
upkeepTransmitInfo[i].gasUsed,
gasOverhead,
report.triggers[i]
Expand Down Expand Up @@ -361,6 +364,7 @@ contract AutomationRegistry2_3 is AutomationRegistryBase2_3, OCR2Abstract, Chain
});
s_fallbackGasPrice = onchainConfig.fallbackGasPrice;
s_fallbackLinkPrice = onchainConfig.fallbackLinkPrice;
s_fallbackNativePrice = onchainConfig.fallbackNativePrice;

uint32 previousConfigBlockNumber = s_storage.latestConfigBlockNumber;
s_storage.latestConfigBlockNumber = uint32(onchainConfig.chainModule.blockNumber());
Expand Down
156 changes: 102 additions & 54 deletions contracts/src/v0.8/automation/dev/v2_3/AutomationRegistryBase2_3.sol
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ abstract contract AutomationRegistryBase2_3 is ConfirmedOwner {
uint256 internal constant ACCOUNTING_PER_UPKEEP_GAS_OVERHEAD = 7_000; // Overhead per upkeep performed in batch

LinkTokenInterface internal immutable i_link;
AggregatorV3Interface internal immutable i_linkNativeFeed;
AggregatorV3Interface internal immutable i_linkUSDFeed;
AggregatorV3Interface internal immutable i_nativeUSDFeed;
AggregatorV3Interface internal immutable i_fastGasFeed;
address internal immutable i_automationForwarderLogic;
address internal immutable i_allowedReadOnlyAddress;
Expand Down Expand Up @@ -98,6 +99,7 @@ abstract contract AutomationRegistryBase2_3 is ConfirmedOwner {
Storage internal s_storage; // Mixture of config and state, not used in transmit
uint256 internal s_fallbackGasPrice;
uint256 internal s_fallbackLinkPrice;
uint256 internal s_fallbackNativePrice;
uint256 internal s_expectedLinkBalance; // Used in case of erroneous LINK transfers to contract
mapping(address => MigrationPermission) internal s_peerRegistryMigrationPermission; // Permissions for migration to and fro
mapping(uint256 => bytes) internal s_upkeepTriggerConfig; // upkeep triggers
Expand All @@ -121,6 +123,7 @@ abstract contract AutomationRegistryBase2_3 is ConfirmedOwner {
error IncorrectNumberOfSigners();
error IndexOutOfRange();
error InvalidDataLength();
error InvalidFeed();
error InvalidTrigger();
error InvalidPayee();
error InvalidRecipient();
Expand Down Expand Up @@ -267,6 +270,7 @@ abstract contract AutomationRegistryBase2_3 is ConfirmedOwner {
uint32 maxRevertDataSize;
uint256 fallbackGasPrice;
uint256 fallbackLinkPrice;
uint256 fallbackNativePrice;
address transcoder;
address[] registrars;
address upkeepPrivilegeManager;
Expand Down Expand Up @@ -390,7 +394,7 @@ abstract contract AutomationRegistryBase2_3 is ConfirmedOwner {
/// @dev Report transmitted by OCR to transmit function
struct Report {
uint256 fastGasWei;
uint256 linkNative;
uint256 linkUSD;
uint256[] upkeepIds;
uint256[] gasLimits;
bytes[] triggers;
Expand Down Expand Up @@ -460,6 +464,36 @@ abstract contract AutomationRegistryBase2_3 is ConfirmedOwner {
address priceFeed;
}

/**
* @notice struct containing price & payment information used in calculating payment amount
* @member gasLimit the amount of gas used
* @member gasOverhead the amount of gas overhead
* @member l1CostWei the amount to be charged for L1 fee in wei
* @member fastGasWei the fast gas price
* @member linkUSD the exchange ratio between LINK and USD
* @member nativeUSD the exchange ratio between the chain's native token and USD
* @member isTransaction is this an eth_call or a transaction
*/
struct PaymentParams {
uint256 gasLimit;
uint256 gasOverhead;
uint256 l1CostWei;
uint256 fastGasWei;
uint256 linkUSD;
uint256 nativeUSD;
bool isTransaction;
}

/**
* @notice struct containing receipt information after a payment is made
* @member reimbursement the amount to reimburse a node for gas spent
* @member premium the premium charged to the user, shared between all nodes
*/
struct PaymentReceipt {
uint96 reimbursement;
uint96 premium;
}

event AdminPrivilegeConfigSet(address indexed admin, bytes privilegeConfig);
event CancelledUpkeepReport(uint256 indexed id, bytes trigger);
event ChainSpecificModuleUpdated(address newModule);
Expand Down Expand Up @@ -502,23 +536,29 @@ abstract contract AutomationRegistryBase2_3 is ConfirmedOwner {

/**
* @param link address of the LINK Token
* @param linkNativeFeed address of the LINK/Native price feed
* @param linkUSDFeed address of the LINK/USD price feed
* @param nativeUSDFeed address of the Native/USD price feed
* @param fastGasFeed address of the Fast Gas price feed
* @param automationForwarderLogic the address of automation forwarder logic
* @param allowedReadOnlyAddress the address of the allowed read only address
*/
constructor(
address link,
address linkNativeFeed,
address linkUSDFeed,
address nativeUSDFeed,
address fastGasFeed,
address automationForwarderLogic,
address allowedReadOnlyAddress
) ConfirmedOwner(msg.sender) {
i_link = LinkTokenInterface(link);
i_linkNativeFeed = AggregatorV3Interface(linkNativeFeed);
i_linkUSDFeed = AggregatorV3Interface(linkUSDFeed);
i_nativeUSDFeed = AggregatorV3Interface(nativeUSDFeed);
i_fastGasFeed = AggregatorV3Interface(fastGasFeed);
i_automationForwarderLogic = automationForwarderLogic;
i_allowedReadOnlyAddress = allowedReadOnlyAddress;
if (i_linkUSDFeed.decimals() != i_nativeUSDFeed.decimals()) {
revert InvalidFeed();
}
}

// ================================================================
Expand Down Expand Up @@ -587,7 +627,9 @@ abstract contract AutomationRegistryBase2_3 is ConfirmedOwner {
* for gas it takes the min of gas price in the transaction or the fast gas
* price in order to reduce costs for the upkeep clients.
*/
function _getFeedData(HotVars memory hotVars) internal view returns (uint256 gasWei, uint256 linkNative) {
function _getFeedData(
HotVars memory hotVars
) internal view returns (uint256 gasWei, uint256 linkUSD, uint256 nativeUSD) {
uint32 stalenessSeconds = hotVars.stalenessSeconds;
bool staleFallback = stalenessSeconds > 0;
uint256 timestamp;
Expand All @@ -600,43 +642,57 @@ abstract contract AutomationRegistryBase2_3 is ConfirmedOwner {
} else {
gasWei = uint256(feedValue);
}
(, feedValue, , timestamp, ) = i_linkNativeFeed.latestRoundData();
(, feedValue, , timestamp, ) = i_linkUSDFeed.latestRoundData();
if (
feedValue <= 0 || block.timestamp < timestamp || (staleFallback && stalenessSeconds < block.timestamp - timestamp)
) {
linkNative = s_fallbackLinkPrice;
linkUSD = s_fallbackLinkPrice;
} else {
linkNative = uint256(feedValue);
linkUSD = uint256(feedValue);
}
(, feedValue, , timestamp, ) = i_nativeUSDFeed.latestRoundData();
return (gasWei, linkUSD, _getNativeUSD(hotVars));
}

/**
* @dev this price has it's own getter for use in the transmit() hot path
* in the future, all price data should be included in the report instead of
* getting read during execution
*/
function _getNativeUSD(HotVars memory hotVars) internal view returns (uint256) {
(, int256 feedValue, , uint256 timestamp, ) = i_nativeUSDFeed.latestRoundData();
if (
feedValue <= 0 ||
block.timestamp < timestamp ||
(hotVars.stalenessSeconds > 0 && hotVars.stalenessSeconds < block.timestamp - timestamp)
) {
return s_fallbackNativePrice;
} else {
return uint256(feedValue);
}
return (gasWei, linkNative);
}

/**
* @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 l1CostWei the amount to be charged for L1 fee in wei
* @param fastGasWei the fast gas price
* @param linkNative the exchange ratio between LINK and Native token
* @param isExecution if this is triggered by a perform upkeep function
* @param hotVars the hot path variables
* @param paymentParams the pricing data and gas usage data
* @dev use of PaymentParams is solely to avoid stack too deep errors
*/
function _calculatePaymentAmount(
HotVars memory hotVars,
uint256 gasLimit,
uint256 gasOverhead,
uint256 l1CostWei,
uint256 fastGasWei,
uint256 linkNative,
bool isExecution
PaymentParams memory paymentParams
) internal view returns (uint96, uint96) {
uint256 gasWei = fastGasWei * hotVars.gasCeilingMultiplier;
uint256 gasWei = paymentParams.fastGasWei * hotVars.gasCeilingMultiplier;
// in case it's actual execution use actual gas price, capped by fastGasWei * gasCeilingMultiplier
if (isExecution && tx.gasprice < gasWei) {
if (paymentParams.isTransaction && tx.gasprice < gasWei) {
gasWei = tx.gasprice;
}
uint256 gasPayment = ((gasWei * (gasLimit + gasOverhead) + l1CostWei) * 1e18) / linkNative;
uint256 premium = (((gasWei * gasLimit) + l1CostWei) * 1e9 * hotVars.paymentPremiumPPB) /
linkNative +
uint256 gasPayment = ((gasWei * (paymentParams.gasLimit + paymentParams.gasOverhead) + paymentParams.l1CostWei) *
paymentParams.nativeUSD) / paymentParams.linkUSD;
uint256 premium = (((gasWei * paymentParams.gasLimit) + paymentParams.l1CostWei) *
hotVars.paymentPremiumPPB *
paymentParams.nativeUSD) /
(paymentParams.linkUSD * 1e9) +
uint256(hotVars.flatFeeMicroLink) *
1e12;
// LINK_TOTAL_SUPPLY < UINT96_MAX
Expand All @@ -653,7 +709,8 @@ abstract contract AutomationRegistryBase2_3 is ConfirmedOwner {
Trigger triggerType,
uint32 performGas,
uint256 fastGasWei,
uint256 linkNative
uint256 linkUSD,
uint256 nativeUSD
) internal view returns (uint96) {
uint256 maxGasOverhead;
if (triggerType == Trigger.CONDITION) {
Expand All @@ -676,12 +733,15 @@ abstract contract AutomationRegistryBase2_3 is ConfirmedOwner {

(uint96 reimbursement, uint96 premium) = _calculatePaymentAmount(
hotVars,
performGas,
maxGasOverhead,
maxL1Fee,
fastGasWei,
linkNative,
false //isExecution
PaymentParams({
gasLimit: performGas,
gasOverhead: maxGasOverhead,
l1CostWei: maxL1Fee,
fastGasWei: fastGasWei,
linkUSD: linkUSD,
nativeUSD: nativeUSD,
isTransaction: false
})
);

return reimbursement + premium;
Expand Down Expand Up @@ -901,27 +961,15 @@ abstract contract AutomationRegistryBase2_3 is ConfirmedOwner {
}

/**
* @dev does postPerform payment processing for an upkeep. Deducts upkeep's balance and increases
* amount spent.
* @dev handles the payment processing after an upkeep has been performed.
* Deducts an upkeep's balance and increases the amount spent.
*/
function _postPerformPayment(
function _handlePayment(
HotVars memory hotVars,
uint256 upkeepId,
uint256 gasUsed,
uint256 fastGasWei,
uint256 linkNative,
uint256 gasOverhead,
uint256 l1Fee
) internal returns (uint96 gasReimbursement, uint96 premium) {
(gasReimbursement, premium) = _calculatePaymentAmount(
hotVars,
gasUsed,
gasOverhead,
l1Fee,
fastGasWei,
linkNative,
true // isExecution
);
PaymentParams memory paymentParams,
uint256 upkeepId
) internal returns (PaymentReceipt memory) {
(uint96 gasReimbursement, uint96 premium) = _calculatePaymentAmount(hotVars, paymentParams);

uint96 balance = s_upkeep[upkeepId].balance;
uint96 payment = gasReimbursement + premium;
Expand All @@ -940,7 +988,7 @@ abstract contract AutomationRegistryBase2_3 is ConfirmedOwner {
s_upkeep[upkeepId].balance -= payment;
s_upkeep[upkeepId].amountSpent += payment;

return (gasReimbursement, premium);
return PaymentReceipt({reimbursement: gasReimbursement, premium: premium});
}

/**
Expand Down
Loading

0 comments on commit c258b2e

Please sign in to comment.