Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

convert link/native to link/usd and native/usd #12309

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

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
});
shileiwill marked this conversation as resolved.
Show resolved Hide resolved
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();
RyanRHall marked this conversation as resolved.
Show resolved Hide resolved
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
Loading