Skip to content

Commit

Permalink
Merge branch 'main' into dynamic-nft-quickstart
Browse files Browse the repository at this point in the history
  • Loading branch information
dwightjl authored Sep 15, 2023
2 parents f3a3203 + e5f84a8 commit 105082a
Show file tree
Hide file tree
Showing 38 changed files with 1,821 additions and 618 deletions.
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2023 SmartContract
Copyright (c) 2023 SmartContract Chainlink Limited SEZC

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 23 additions & 0 deletions public/samples/Automation/Counter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;

/**
* THIS IS AN EXAMPLE CONTRACT THAT USES HARDCODED VALUES FOR CLARITY.
* THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE.
* DO NOT USE THIS CODE IN PRODUCTION.
*/

contract Counter {
/**
* Public counter variable
*/
uint public counter;

constructor() {
counter = 0;
}

function updateCounter(uint updateAmount) external {
counter += updateAmount;
}
}
336 changes: 336 additions & 0 deletions public/samples/Automation/tutorials/VRFSubscriptionBalanceMonitor.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,336 @@
// SPDX-License-Identifier: MIT

pragma solidity 0.8.6;

import "@chainlink/contracts/src/v0.8/ConfirmedOwner.sol";
import "@chainlink/contracts/src/v0.8/interfaces/KeeperCompatibleInterface.sol";
import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";
import "@chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol";
import "@openzeppelin/contracts/security/Pausable.sol";

/**
* @title The VRFSubscriptionBalanceMonitor contract.
* @notice A keeper-compatible contract that monitors and funds VRF subscriptions.
*/
contract VRFSubscriptionBalanceMonitor is
ConfirmedOwner,
Pausable,
KeeperCompatibleInterface
{
VRFCoordinatorV2Interface public COORDINATOR;
LinkTokenInterface public LINKTOKEN;

uint256 private constant MIN_GAS_FOR_TRANSFER = 55_000;

event FundsAdded(uint256 amountAdded, uint256 newBalance, address sender);
event FundsWithdrawn(uint256 amountWithdrawn, address payee);
event TopUpSucceeded(uint64 indexed subscriptionId);
event TopUpFailed(uint64 indexed subscriptionId);
event KeeperRegistryAddressUpdated(address oldAddress, address newAddress);
event VRFCoordinatorV2AddressUpdated(
address oldAddress,
address newAddress
);
event LinkTokenAddressUpdated(address oldAddress, address newAddress);
event MinWaitPeriodUpdated(
uint256 oldMinWaitPeriod,
uint256 newMinWaitPeriod
);
event OutOfGas(uint256 lastId);

error InvalidWatchList();
error OnlyKeeperRegistry();
error DuplicateSubcriptionId(uint64 duplicate);

struct Target {
bool isActive;
uint96 minBalanceJuels;
uint96 topUpAmountJuels;
uint56 lastTopUpTimestamp;
}

address public s_keeperRegistryAddress; // the address of the keeper registry
uint256 public s_minWaitPeriodSeconds; // minimum time to wait between top-ups
uint64[] public s_watchList; // the watchlist on which subscriptions are stored
mapping(uint64 => Target) internal s_targets;

/**
* @param linkTokenAddress the Link token address
* @param coordinatorAddress the address of the vrf coordinator contract
* @param keeperRegistryAddress the address of the keeper registry contract
* @param minWaitPeriodSeconds the minimum wait period for addresses between funding
*/
constructor(
address linkTokenAddress,
address coordinatorAddress,
address keeperRegistryAddress,
uint256 minWaitPeriodSeconds
) ConfirmedOwner(msg.sender) {
setLinkTokenAddress(linkTokenAddress);
setVRFCoordinatorV2Address(coordinatorAddress);
setKeeperRegistryAddress(keeperRegistryAddress);
setMinWaitPeriodSeconds(minWaitPeriodSeconds);
}

/**
* @notice Sets the list of subscriptions to watch and their funding parameters.
* @param subscriptionIds the list of subscription ids to watch
* @param minBalancesJuels the minimum balances for each subscription
* @param topUpAmountsJuels the amount to top up each subscription
*/
function setWatchList(
uint64[] calldata subscriptionIds,
uint96[] calldata minBalancesJuels,
uint96[] calldata topUpAmountsJuels
) external onlyOwner {
if (
subscriptionIds.length != minBalancesJuels.length ||
subscriptionIds.length != topUpAmountsJuels.length
) {
revert InvalidWatchList();
}
uint64[] memory oldWatchList = s_watchList;
for (uint256 idx = 0; idx < oldWatchList.length; idx++) {
s_targets[oldWatchList[idx]].isActive = false;
}
for (uint256 idx = 0; idx < subscriptionIds.length; idx++) {
if (s_targets[subscriptionIds[idx]].isActive) {
revert DuplicateSubcriptionId(subscriptionIds[idx]);
}
if (subscriptionIds[idx] == 0) {
revert InvalidWatchList();
}
if (topUpAmountsJuels[idx] <= minBalancesJuels[idx]) {
revert InvalidWatchList();
}
s_targets[subscriptionIds[idx]] = Target({
isActive: true,
minBalanceJuels: minBalancesJuels[idx],
topUpAmountJuels: topUpAmountsJuels[idx],
lastTopUpTimestamp: 0
});
}
s_watchList = subscriptionIds;
}

/**
* @notice Gets a list of subscriptions that are underfunded.
* @return list of subscriptions that are underfunded
*/
function getUnderfundedSubscriptions()
public
view
returns (uint64[] memory)
{
uint64[] memory watchList = s_watchList;
uint64[] memory needsFunding = new uint64[](watchList.length);
uint256 count = 0;
uint256 minWaitPeriod = s_minWaitPeriodSeconds;
uint256 contractBalance = LINKTOKEN.balanceOf(address(this));
Target memory target;
for (uint256 idx = 0; idx < watchList.length; idx++) {
target = s_targets[watchList[idx]];
(uint96 subscriptionBalance, , , ) = COORDINATOR.getSubscription(
watchList[idx]
);
if (
target.lastTopUpTimestamp + minWaitPeriod <= block.timestamp &&
contractBalance >= target.topUpAmountJuels &&
subscriptionBalance < target.minBalanceJuels
) {
needsFunding[count] = watchList[idx];
count++;
contractBalance -= target.topUpAmountJuels;
}
}
if (count < watchList.length) {
assembly {
mstore(needsFunding, count)
}
}
return needsFunding;
}

/**
* @notice Send funds to the subscriptions provided.
* @param needsFunding the list of subscriptions to fund
*/
function topUp(uint64[] memory needsFunding) public whenNotPaused {
uint256 minWaitPeriodSeconds = s_minWaitPeriodSeconds;
uint256 contractBalance = LINKTOKEN.balanceOf(address(this));
Target memory target;
for (uint256 idx = 0; idx < needsFunding.length; idx++) {
target = s_targets[needsFunding[idx]];
(uint96 subscriptionBalance, , , ) = COORDINATOR.getSubscription(
needsFunding[idx]
);
if (
target.isActive &&
target.lastTopUpTimestamp + minWaitPeriodSeconds <=
block.timestamp &&
subscriptionBalance < target.minBalanceJuels &&
contractBalance >= target.topUpAmountJuels
) {
bool success = LINKTOKEN.transferAndCall(
address(COORDINATOR),
target.topUpAmountJuels,
abi.encode(needsFunding[idx])
);
if (success) {
s_targets[needsFunding[idx]].lastTopUpTimestamp = uint56(
block.timestamp
);
contractBalance -= target.topUpAmountJuels;
emit TopUpSucceeded(needsFunding[idx]);
} else {
emit TopUpFailed(needsFunding[idx]);
}
}
if (gasleft() < MIN_GAS_FOR_TRANSFER) {
emit OutOfGas(idx);
return;
}
}
}

/**
* @notice Gets list of subscription ids that are underfunded and returns a keeper-compatible payload.
* @return upkeepNeeded signals if upkeep is needed, performData is an abi encoded list of subscription ids that need funds
*/
function checkUpkeep(
bytes calldata
)
external
view
override
whenNotPaused
returns (bool upkeepNeeded, bytes memory performData)
{
uint64[] memory needsFunding = getUnderfundedSubscriptions();
upkeepNeeded = needsFunding.length > 0;
performData = abi.encode(needsFunding);
return (upkeepNeeded, performData);
}

/**
* @notice Called by the keeper to send funds to underfunded addresses.
* @param performData the abi encoded list of addresses to fund
*/
function performUpkeep(
bytes calldata performData
) external override onlyKeeperRegistry whenNotPaused {
uint64[] memory needsFunding = abi.decode(performData, (uint64[]));
topUp(needsFunding);
}

/**
* @notice Withdraws the contract balance in LINK.
* @param amount the amount of LINK (in juels) to withdraw
* @param payee the address to pay
*/
function withdraw(
uint256 amount,
address payable payee
) external onlyOwner {
require(payee != address(0));
emit FundsWithdrawn(amount, payee);
LINKTOKEN.transfer(payee, amount);
}

/**
* @notice Sets the LINK token address.
*/
function setLinkTokenAddress(address linkTokenAddress) public onlyOwner {
require(linkTokenAddress != address(0));
emit LinkTokenAddressUpdated(address(LINKTOKEN), linkTokenAddress);
LINKTOKEN = LinkTokenInterface(linkTokenAddress);
}

/**
* @notice Sets the VRF coordinator address.
*/
function setVRFCoordinatorV2Address(
address coordinatorAddress
) public onlyOwner {
require(coordinatorAddress != address(0));
emit VRFCoordinatorV2AddressUpdated(
address(COORDINATOR),
coordinatorAddress
);
COORDINATOR = VRFCoordinatorV2Interface(coordinatorAddress);
}

/**
* @notice Sets the keeper registry address.
*/
function setKeeperRegistryAddress(
address keeperRegistryAddress
) public onlyOwner {
require(keeperRegistryAddress != address(0));
emit KeeperRegistryAddressUpdated(
s_keeperRegistryAddress,
keeperRegistryAddress
);
s_keeperRegistryAddress = keeperRegistryAddress;
}

/**
* @notice Sets the minimum wait period (in seconds) for subscription ids between funding.
*/
function setMinWaitPeriodSeconds(uint256 period) public onlyOwner {
emit MinWaitPeriodUpdated(s_minWaitPeriodSeconds, period);
s_minWaitPeriodSeconds = period;
}

/**
* @notice Gets configuration information for a subscription on the watchlist.
*/
function getSubscriptionInfo(
uint64 subscriptionId
)
external
view
returns (
bool isActive,
uint96 minBalanceJuels,
uint96 topUpAmountJuels,
uint56 lastTopUpTimestamp
)
{
Target memory target = s_targets[subscriptionId];
return (
target.isActive,
target.minBalanceJuels,
target.topUpAmountJuels,
target.lastTopUpTimestamp
);
}

/**
* @notice Gets the list of subscription ids being watched.
*/
function getWatchList() external view returns (uint64[] memory) {
return s_watchList;
}

/**
* @notice Pause the contract, which prevents executing performUpkeep.
*/
function pause() external onlyOwner {
_pause();
}

/**
* @notice Unpause the contract.
*/
function unpause() external onlyOwner {
_unpause();
}

modifier onlyKeeperRegistry() {
if (msg.sender != s_keeperRegistryAddress) {
revert OnlyKeeperRegistry();
}
_;
}
}
2 changes: 1 addition & 1 deletion public/samples/DataFeeds/DataConsumerV3.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ contract DataConsumerV3 {
/**
* Returns the latest answer.
*/
function getLatestData() public view returns (int) {
function getChainlinkDataFeedLatestAnswer() public view returns (int) {
// prettier-ignore
(
/* uint80 roundID */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ contract DataConsumerWithSequencerCheck {
}

// Check the sequencer status and return the latest data
function getLatestData() public view returns (int) {
function getChainlinkDataFeedLatestAnswer() public view returns (int) {
// prettier-ignore
(
/*uint80 roundID*/,
Expand Down
Loading

0 comments on commit 105082a

Please sign in to comment.