Skip to content

Commit

Permalink
Merge branch 'develop' into VRF-892
Browse files Browse the repository at this point in the history
  • Loading branch information
vreff committed Mar 12, 2024
2 parents 4836247 + 2743b55 commit 8ba24cb
Show file tree
Hide file tree
Showing 47 changed files with 1,035 additions and 190 deletions.
5 changes: 5 additions & 0 deletions .changeset/dirty-weeks-shave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"chainlink": minor
---

update AutomationBase interface to check for ready only address on polygon zkEVM
5 changes: 5 additions & 0 deletions .changeset/fresh-spies-melt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"chainlink": patch
---

Update automation smoke test to use UpkeepCounter with time based counter
5 changes: 5 additions & 0 deletions .changeset/lemon-ladybugs-doubt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"chainlink": patch
---

Add kv store tied to jobs and use it for juels fee per coin cache to store persisted values for backup
5 changes: 5 additions & 0 deletions .changeset/pretty-fishes-jam.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"chainlink": minor
---

VRF V2+ Coordinator msg.data len validation
5 changes: 5 additions & 0 deletions .changeset/tasty-buckets-relate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"chainlink": patch
---

Validation for premium limits added to VRFCoordinatorV2_5 contract
5 changes: 5 additions & 0 deletions .changeset/warm-owls-act.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"chainlink": minor
---

Add support for eth_getLogs & finality tags in simulated_backend_client.go
5 changes: 5 additions & 0 deletions .changeset/wild-walls-suffer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"chainlink": minor
---

Validate if flat fee configs are configured correctly
2 changes: 1 addition & 1 deletion contracts/src/v0.8/automation/AutomationBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ contract AutomationBase {
*/
function _preventExecution() internal view {
// solhint-disable-next-line avoid-tx-origin
if (tx.origin != address(0)) {
if (tx.origin != address(0) && tx.origin != address(0x1111111111111111111111111111111111111111)) {
revert OnlySimulatedBackend();
}
}
Expand Down
28 changes: 14 additions & 14 deletions contracts/src/v0.8/automation/testhelpers/UpkeepCounter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,25 @@ pragma solidity 0.8.16;
contract UpkeepCounter {
event PerformingUpkeep(
address indexed from,
uint256 initialBlock,
uint256 lastBlock,
uint256 initialTimestamp,
uint256 lastTimestamp,
uint256 previousBlock,
uint256 counter
);

uint256 public testRange;
uint256 public interval;
uint256 public lastBlock;
uint256 public lastTimestamp;
uint256 public previousPerformBlock;
uint256 public initialBlock;
uint256 public initialTimestamp;
uint256 public counter;

constructor(uint256 _testRange, uint256 _interval) {
testRange = _testRange;
interval = _interval;
previousPerformBlock = 0;
lastBlock = block.number;
initialBlock = 0;
lastTimestamp = block.timestamp;
initialTimestamp = 0;
counter = 0;
}

Expand All @@ -31,28 +31,28 @@ contract UpkeepCounter {
}

function performUpkeep(bytes calldata performData) external {
if (initialBlock == 0) {
initialBlock = block.number;
if (initialTimestamp == 0) {
initialTimestamp = block.timestamp;
}
lastBlock = block.number;
lastTimestamp = block.timestamp;
counter = counter + 1;
performData;
emit PerformingUpkeep(tx.origin, initialBlock, lastBlock, previousPerformBlock, counter);
previousPerformBlock = lastBlock;
emit PerformingUpkeep(tx.origin, initialTimestamp, lastTimestamp, previousPerformBlock, counter);
previousPerformBlock = lastTimestamp;
}

function eligible() public view returns (bool) {
if (initialBlock == 0) {
if (initialTimestamp == 0) {
return true;
}

return (block.number - initialBlock) < testRange && (block.number - lastBlock) >= interval;
return (block.timestamp - initialTimestamp) < testRange && (block.timestamp - lastTimestamp) >= interval;
}

function setSpread(uint256 _testRange, uint256 _interval) external {
testRange = _testRange;
interval = _interval;
initialBlock = 0;
initialTimestamp = 0;
counter = 0;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ contract LinkAvailableBalanceMonitor is AccessControl, AutomationCompatibleInter

bytes32 private constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
bytes32 private constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE");
uint96 private constant DEFAULT_TOP_UP_AMOUNT_JULES = 9000000000000000000;
uint96 private constant DEFAULT_MIN_BALANCE_JULES = 1000000000000000000;
uint96 private constant DEFAULT_TOP_UP_AMOUNT_JUELS = 9000000000000000000;
uint96 private constant DEFAULT_MIN_BALANCE_JUELS = 1000000000000000000;
IERC20 private immutable i_linkToken;

uint256 private s_minWaitPeriodSeconds;
Expand Down Expand Up @@ -142,14 +142,15 @@ contract LinkAvailableBalanceMonitor is AccessControl, AutomationCompatibleInter
}
// s_onRampAddresses is not the same length as s_watchList, so it has
// to be clean in a separate loop
for (uint256 idx = 0; idx < s_onRampAddresses.length(); idx++) {
(uint256 key, ) = s_onRampAddresses.at(idx);
for (uint256 idx = s_onRampAddresses.length(); idx > 0; idx--) {
(uint256 key, ) = s_onRampAddresses.at(idx - 1);
s_onRampAddresses.remove(key);
}
for (uint256 idx = 0; idx < addresses.length; idx++) {
address targetAddress = addresses[idx];
if (s_targets[targetAddress].isActive) revert DuplicateAddress(targetAddress);
if (targetAddress == address(0)) revert InvalidWatchList();
if (minBalances[idx] == 0) revert InvalidWatchList();
if (topUpAmounts[idx] == 0) revert InvalidWatchList();
s_targets[targetAddress] = MonitoredAddress({
isActive: true,
Expand All @@ -173,6 +174,7 @@ contract LinkAvailableBalanceMonitor is AccessControl, AutomationCompatibleInter
/// in which case it will carry the proper dstChainSelector along with the 0x0 address
function addToWatchListOrDecomission(address targetAddress, uint64 dstChainSelector) public onlyAdminOrExecutor {
if (s_targets[targetAddress].isActive) revert DuplicateAddress(targetAddress);
if (targetAddress == address(0) && dstChainSelector == 0) revert InvalidAddress(targetAddress);
bool onRampExists = s_onRampAddresses.contains(dstChainSelector);
// if targetAddress is an existing onRamp, there's a need of cleaning the previous onRamp associated to this dstChainSelector
// there's no need to remove any other address that's not an onRamp
Expand All @@ -182,16 +184,19 @@ contract LinkAvailableBalanceMonitor is AccessControl, AutomationCompatibleInter
}
// only add the new address if it's not 0x0
if (targetAddress != address(0)) {
s_onRampAddresses.set(dstChainSelector, targetAddress);
s_targets[targetAddress] = MonitoredAddress({
isActive: true,
minBalance: DEFAULT_MIN_BALANCE_JULES,
topUpAmount: DEFAULT_TOP_UP_AMOUNT_JULES,
minBalance: DEFAULT_MIN_BALANCE_JUELS,
topUpAmount: DEFAULT_TOP_UP_AMOUNT_JUELS,
lastTopUpTimestamp: 0
});
s_watchList.add(targetAddress);
} else {
// if the address is 0x0, it means the onRamp has ben decomissioned and has to be cleaned
// add the contract to onRampAddresses if it carries a valid dstChainSelector
if (dstChainSelector > 0) {
s_onRampAddresses.set(dstChainSelector, targetAddress);
}
// else if is refundant as this is the only corner case left, maintaining it for legibility
} else if (targetAddress == address(0) && dstChainSelector > 0) {
s_onRampAddresses.remove(dstChainSelector);
}
}
Expand Down Expand Up @@ -219,16 +224,24 @@ contract LinkAvailableBalanceMonitor is AccessControl, AutomationCompatibleInter
uint256 idx = uint256(blockhash(block.number - (block.number % s_upkeepInterval) - 1)) % numTargets;
uint256 numToCheck = numTargets < maxCheck ? numTargets : maxCheck;
uint256 numFound = 0;
uint256 minWaitPeriod = s_minWaitPeriodSeconds;
address[] memory targetsToFund = new address[](maxPerform);
MonitoredAddress memory target;
MonitoredAddress memory contractToFund;
for (
uint256 numChecked = 0;
numChecked < numToCheck;
(idx, numChecked) = ((idx + 1) % numTargets, numChecked + 1)
) {
address targetAddress = s_watchList.at(idx);
target = s_targets[targetAddress];
if (_needsFunding(targetAddress, target.minBalance)) {
contractToFund = s_targets[targetAddress];
if (
_needsFunding(
targetAddress,
contractToFund.lastTopUpTimestamp + minWaitPeriod,
contractToFund.minBalance,
contractToFund.isActive
)
) {
targetsToFund[numFound] = targetAddress;
numFound++;
if (numFound == maxPerform) {
Expand All @@ -247,15 +260,24 @@ contract LinkAvailableBalanceMonitor is AccessControl, AutomationCompatibleInter
/// @notice tries to fund an array of target addresses, checking if they're underfunded in the process
/// @param targetAddresses is an array of contract addresses to be funded in case they're underfunded
function topUp(address[] memory targetAddresses) public whenNotPaused {
MonitoredAddress memory target;
MonitoredAddress memory contractToFund;
uint256 minWaitPeriod = s_minWaitPeriodSeconds;
uint256 localBalance = i_linkToken.balanceOf(address(this));
for (uint256 idx = 0; idx < targetAddresses.length; idx++) {
address targetAddress = targetAddresses[idx];
target = s_targets[targetAddress];
if (localBalance >= target.topUpAmount && _needsFunding(targetAddress, target.minBalance)) {
bool success = i_linkToken.transfer(targetAddress, target.topUpAmount);
contractToFund = s_targets[targetAddress];
if (
localBalance >= contractToFund.topUpAmount &&
_needsFunding(
targetAddress,
contractToFund.lastTopUpTimestamp + minWaitPeriod,
contractToFund.minBalance,
contractToFund.isActive
)
) {
bool success = i_linkToken.transfer(targetAddress, contractToFund.topUpAmount);
if (success) {
localBalance -= target.topUpAmount;
localBalance -= contractToFund.topUpAmount;
s_targets[targetAddress].lastTopUpTimestamp = uint56(block.timestamp);
emit TopUpSucceeded(targetAddress);
} else {
Expand All @@ -271,30 +293,32 @@ contract LinkAvailableBalanceMonitor is AccessControl, AutomationCompatibleInter
/// if it is elligible for funding
/// @param targetAddress the target to check
/// @param minBalance minimum balance required for the target
/// @param minWaitPeriodPassed the minimum wait period (target lastTopUpTimestamp + minWaitPeriod)
/// @return bool whether the target needs funding or not
function _needsFunding(address targetAddress, uint256 minBalance) private view returns (bool) {
function _needsFunding(
address targetAddress,
uint256 minWaitPeriodPassed,
uint256 minBalance,
bool contractIsActive
) private view returns (bool) {
// Explicitly check if the targetAddress is the zero address
// or if it's not a contract. In both cases return with false,
// to prevent target.linkAvailableForPayment from running,
// which would revert the operation.
if (targetAddress == address(0) || targetAddress.code.length == 0) {
return false;
}
MonitoredAddress memory addressToCheck = s_targets[targetAddress];
ILinkAvailable target;
IAggregatorProxy proxy = IAggregatorProxy(targetAddress);
try proxy.aggregator() returns (address aggregatorAddress) {
// proxy.aggregator() can return a 0 address if the address is not an aggregator
if (aggregatorAddress == address(0)) return false;
target = ILinkAvailable(aggregatorAddress);
} catch {
target = ILinkAvailable(targetAddress);
}
try target.linkAvailableForPayment() returns (int256 balance) {
if (
balance < int256(minBalance) &&
addressToCheck.lastTopUpTimestamp + s_minWaitPeriodSeconds <= block.timestamp &&
addressToCheck.isActive
) {
if (balance < int256(minBalance) && minWaitPeriodPassed <= block.timestamp && contractIsActive) {
return true;
}
} catch {}
Expand Down Expand Up @@ -408,9 +432,9 @@ contract LinkAvailableBalanceMonitor is AccessControl, AutomationCompatibleInter
/// @notice Gets configuration information for an address on the watchlist
function getAccountInfo(
address targetAddress
) external view returns (bool isActive, uint256 minBalance, uint256 topUpAmount) {
) external view returns (bool isActive, uint96 minBalance, uint96 topUpAmount, uint56 lastTopUpTimestamp) {
MonitoredAddress memory target = s_targets[targetAddress];
return (target.isActive, target.minBalance, target.topUpAmount);
return (target.isActive, target.minBalance, target.topUpAmount, target.lastTopUpTimestamp);
}

/// @dev Modifier to make a function callable only by executor role or the
Expand Down
38 changes: 37 additions & 1 deletion contracts/src/v0.8/vrf/dev/VRFCoordinatorV2_5.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,17 @@ contract VRFCoordinatorV2_5 is VRF, SubscriptionAPI, IVRFCoordinatorV2Plus {
// 5k is plenty for an EXTCODESIZE call (2600) + warm CALL (100)
// and some arithmetic operations.
uint256 private constant GAS_FOR_CALL_EXACT_CHECK = 5_000;
// upper bound limit for premium percentages to make sure fee calculations don't overflow
uint8 private constant PREMIUM_PERCENTAGE_MAX = 155;
error InvalidRequestConfirmations(uint16 have, uint16 min, uint16 max);
error GasLimitTooBig(uint32 have, uint32 want);
error NumWordsTooBig(uint32 have, uint32 want);
error MsgDataTooBig(uint256 have, uint32 max);
error ProvingKeyAlreadyRegistered(bytes32 keyHash);
error NoSuchProvingKey(bytes32 keyHash);
error InvalidLinkWeiPrice(int256 linkWei);
error LinkDiscountTooHigh(uint32 flatFeeLinkDiscountPPM, uint32 flatFeeNativePPM);
error InvalidPremiumPercentage(uint8 premiumPercentage, uint8 max);
error InsufficientGasForConsumer(uint256 have, uint256 want);
error NoCorrespondingRequest();
error IncorrectCommitment();
Expand Down Expand Up @@ -176,9 +180,15 @@ contract VRFCoordinatorV2_5 is VRF, SubscriptionAPI, IVRFCoordinatorV2Plus {
if (fallbackWeiPerUnitLink <= 0) {
revert InvalidLinkWeiPrice(fallbackWeiPerUnitLink);
}
if (fulfillmentFlatFeeNativePPM > 0 && fulfillmentFlatFeeLinkDiscountPPM >= fulfillmentFlatFeeNativePPM) {
if (fulfillmentFlatFeeLinkDiscountPPM > fulfillmentFlatFeeNativePPM) {
revert LinkDiscountTooHigh(fulfillmentFlatFeeLinkDiscountPPM, fulfillmentFlatFeeNativePPM);
}
if (nativePremiumPercentage > PREMIUM_PERCENTAGE_MAX) {
revert InvalidPremiumPercentage(nativePremiumPercentage, PREMIUM_PERCENTAGE_MAX);
}
if (linkPremiumPercentage > PREMIUM_PERCENTAGE_MAX) {
revert InvalidPremiumPercentage(linkPremiumPercentage, PREMIUM_PERCENTAGE_MAX);
}
s_config = Config({
minimumRequestConfirmations: minimumRequestConfirmations,
maxGasLimit: maxGasLimit,
Expand Down Expand Up @@ -444,6 +454,32 @@ contract VRFCoordinatorV2_5 is VRF, SubscriptionAPI, IVRFCoordinatorV2Plus {
bool onlyPremium
) external nonReentrant returns (uint96 payment) {
uint256 startGas = gasleft();
// fulfillRandomWords msg.data has 772 bytes and with an additional
// buffer of 32 bytes, we get 804 bytes.
/* Data size split:
* fulfillRandomWords function signature - 4 bytes
* proof - 416 bytes
* pk - 64 bytes
* gamma - 64 bytes
* c - 32 bytes
* s - 32 bytes
* seed - 32 bytes
* uWitness - 32 bytes
* cGammaWitness - 64 bytes
* sHashWitness - 64 bytes
* zInv - 32 bytes
* requestCommitment - 320 bytes
* blockNum - 32 bytes
* subId - 32 bytes
* callbackGasLimit - 32 bytes
* numWords - 32 bytes
* sender - 32 bytes
* extraArgs - 128 bytes
* onlyPremium - 32 bytes
*/
if (msg.data.length > 804) {
revert MsgDataTooBig(msg.data.length, 804);
}
Output memory output = _getRandomnessFromProof(proof, rc);
uint256 gasPrice = _getValidatedGasPrice(onlyPremium, output.provingKey.maxGas);

Expand Down
11 changes: 3 additions & 8 deletions contracts/src/v0.8/vrf/dev/VRFV2PlusWrapper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -143,22 +143,17 @@ contract VRFV2PlusWrapper is ConfirmedOwner, TypeAndVersionInterface, VRFConsume
}

/**
* @notice set the link token to be used by this wrapper
* @notice set the link token and link native feed to be used by this wrapper
* @param link address of the link token
* @param linkNativeFeed address of the link native feed
*/
function setLINK(address link) external onlyOwner {
function setLinkAndLinkNativeFeed(address link, address linkNativeFeed) external onlyOwner {
// Disallow re-setting link token because the logic wouldn't really make sense
if (address(s_link) != address(0)) {
revert LinkAlreadySet();
}
s_link = LinkTokenInterface(link);
}

/**
* @notice set the link native feed to be used by this wrapper
* @param linkNativeFeed address of the link native feed
*/
function setLinkNativeFeed(address linkNativeFeed) external onlyOwner {
s_linkNativeFeed = AggregatorV3Interface(linkNativeFeed);
}

Expand Down
2 changes: 1 addition & 1 deletion contracts/test/v0.8/Cron.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ describe('Cron', () => {
})

describe('calculateNextTick() / calculateLastTick()', () => {
it('correctly identifies the next & last ticks for cron jobs', async () => {
it('correctly identifies the next & last ticks for cron jobs [ @skip-coverage ]', async () => {
await setTimestamp(timeStamp)
const now = () => moment.unix(timeStamp)
const tests = [
Expand Down
Loading

0 comments on commit 8ba24cb

Please sign in to comment.