Skip to content

Commit

Permalink
give up on programmatic registration
Browse files Browse the repository at this point in the history
  • Loading branch information
MattPereira committed Oct 25, 2023
1 parent babecfd commit b107a69
Show file tree
Hide file tree
Showing 8 changed files with 222 additions and 83 deletions.
75 changes: 47 additions & 28 deletions packages/hardhat/contracts/AutomationConsumer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pragma solidity ^0.8.7;

import "@chainlink/contracts/src/v0.8/automation/AutomationCompatible.sol";
import { LinkTokenInterface } from "@chainlink/contracts/src/v0.8/shared/interfaces/LinkTokenInterface.sol";
import "@chainlink/contracts/src/v0.8/automation/interfaces/v2_0/AutomationRegistryInterface2_0.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

error AutomationConsumer__UpkeepNotNeeded();
Expand All @@ -26,6 +27,10 @@ interface AutomationRegistrarInterface {
) external returns (uint256);
}

// interface AutomationRegistryInterface {
// function getUpkeep(uint256 id) external view returns (UpkeepInfo memory upkeepInfo);
// }

/**
* Simple counter contract using chainlink automation
*
Expand All @@ -39,40 +44,23 @@ contract AutomationConsumer is AutomationCompatibleInterface, Ownable {
event IntervalUpdated(uint256 indexed interval);

uint public s_counter = 0;
uint public s_interval = 30 seconds;
uint public s_interval = 10 seconds;
uint public s_maxCounterValue = 10;
bool public s_isCounting = false;
uint public s_lastTimestamp;
uint public s_upkeepID;
LinkTokenInterface public immutable i_link;
AutomationRegistrarInterface public immutable i_registrar;
AutomationRegistryBaseInterface public immutable i_registry;

constructor(
LinkTokenInterface link,
AutomationRegistrarInterface registrar
AutomationRegistrarInterface registrar,
AutomationRegistryBaseInterface registry
) {
i_link = link;
i_registrar = registrar;
}

function startCounting() public {
s_isCounting = true;
s_lastTimestamp = block.timestamp;
emit CounterStarted(s_counter);
}

function stopCounting() public {
s_isCounting = false;
emit CounterStopped(s_counter);
}

function resetCounter() public {
s_counter = 0;
}

function updateInterval(uint256 _interval) public {
s_interval = _interval;
emit IntervalUpdated(s_interval);
i_registry = registry;
}

/**
Expand Down Expand Up @@ -120,27 +108,58 @@ contract AutomationConsumer is AutomationCompatibleInterface, Ownable {

/**
* @param params required params for registering an upkeep
*
* DEBUG NOTES:
* - this contract successfully approves registrar to spend link
* - UNPREDICTABLE_GAS_LIMIT must be coming from the i_registrar contract
*/

function registerNewUpkeep(RegistrationParams memory params) public {
require(
i_link.balanceOf(address(this)) >= params.amount,
"Not enough LINK tokens in this contract"
);
require(
i_link.approve(address(i_registrar), params.amount),
"Failed to approve registrar contract to spend LINK"
);
// this function call on the registrar contract always fails with UNPREDICTABLE_GAS_LIMIT :(
uint256 upkeepID = i_registrar.registerUpkeep(params);
require(upkeepID != 0, "Upkeep registration failed");
s_upkeepID = upkeepID;
}

function getLinkBalance() public view returns (uint256) {
return i_link.balanceOf(address(this));
// SETTERS
function setUpkeepID(uint256 _upkeepID) public {
s_upkeepID = _upkeepID;
}

function startCounting() public {
s_isCounting = true;
s_lastTimestamp = block.timestamp;
emit CounterStarted(s_counter);
}

function stopCounting() public {
s_isCounting = false;
emit CounterStopped(s_counter);
}

function resetCounter() public {
s_counter = 0;
}

function updateInterval(uint256 _interval) public {
s_interval = _interval;
emit IntervalUpdated(s_interval);
}

// GETTERS
function withdrawLink() public onlyOwner {
i_link.transfer(msg.sender, i_link.balanceOf(address(this)));
}

function getLinkBalance() public view returns (uint256) {
return i_link.balanceOf(address(this));
}

function getUpkeepBalance() public view returns (UpkeepInfo memory) {
return i_registry.getUpkeep(s_upkeepID);
}
}
10 changes: 5 additions & 5 deletions packages/hardhat/deploy/03_VRFConsumer.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { HardhatRuntimeEnvironment } from "hardhat/types";
import { DeployFunction } from "hardhat-deploy/types";
import { networkConfig } from "../helper-hardhat-config";
import LinkTokenABI from "@chainlink/contracts/abi/v0.8/LinkToken.json";
import { approveAndTransfer } from "../scripts/approveAndTransfer";
import LinkTokenABI from "@chainlink/contracts/abi/v0.8/LinkToken.json";

/** 1. Deploy the "VRFConsumer" contract
* 2. Send 5 LINK to VRFConsumer contract
Expand All @@ -28,11 +28,11 @@ const deployVRFConsumer: DeployFunction = async function (hre: HardhatRuntimeEnv
});

if (linkTokenAddress) {
const tokenContract = new ethers.Contract(linkTokenAddress, LinkTokenABI, ethers.provider);
const rawBalance = await tokenContract.balanceOf(VRFConsumer.address);
const formattedBalance = +ethers.utils.formatUnits(rawBalance, await tokenContract.decimals());
const vrfConsumer = await ethers.getContract("VRFConsumer", deployer);
const vrfConsumerBalance = await vrfConsumer.getLinkBalance();
const minBalance = ethers.utils.parseUnits("1", 18);

if (formattedBalance < 2)
if (vrfConsumerBalance < minBalance)
await approveAndTransfer({
tokenAddress: linkTokenAddress,
tokenABI: LinkTokenABI,
Expand Down
86 changes: 51 additions & 35 deletions packages/hardhat/deploy/04_AutomationConsumer.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { HardhatRuntimeEnvironment } from "hardhat/types";
import { DeployFunction } from "hardhat-deploy/types";
import { networkConfig } from "../helper-hardhat-config";
import LinkTokenABI from "@chainlink/contracts/abi/v0.8/LinkToken.json";
import { approveAndTransfer } from "../scripts/approveAndTransfer";
// import RegistrarABI from "@chainlink/contracts/abi/v0.8/AutomationRegistrar2_1.json";
// import LinkTokenABI from "@chainlink/contracts/abi/v0.8/LinkToken.json";
// import { approveAndTransfer } from "../scripts/approveAndTransfer";

/** 1. Deploys the "AutomationConsumer" contract
* 2. Send LINK to AutomationConsumer contract
Expand All @@ -13,12 +14,12 @@ import { approveAndTransfer } from "../scripts/approveAndTransfer";
const deployAutomationConsumer: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const { deployer } = await hre.getNamedAccounts();
const { deploy, log } = hre.deployments;
const { ethers } = hre;
// const { ethers } = hre;

log("------------------------------------");
const chainId = await hre.ethers.provider.getNetwork().then(network => network.chainId);
const { linkTokenAddress, registrarAddress } = networkConfig[chainId].AutomationConsumer;
const args = [linkTokenAddress, registrarAddress];
const { linkTokenAddress, registrarAddress, registryAddress } = networkConfig[chainId].AutomationConsumer;
const args = [linkTokenAddress, registrarAddress, registryAddress];

await deploy("AutomationConsumer", {
from: deployer,
Expand All @@ -29,39 +30,54 @@ const deployAutomationConsumer: DeployFunction = async function (hre: HardhatRun

log("------------------------------------");

if (linkTokenAddress) {
const [signer] = await ethers.getSigners();
const AutomationConsumer = await ethers.getContract("AutomationConsumer", signer);
// const linkContract = new ethers.Contract(linkTokenAddress, LinkTokenABI, signer);
/** FAILED ATTEMPT: programmatic registration through AutomationConsumer.registerNewUpkeep()
*
* VERIFIED:
* - the AutomationConsumer contract does successfully approve registrar contract to spend LINK
*
* PROBLEM:
* - always fails with UNPREDICTABLE_GAS_LIMIT and I can't figure out why
*/

const fundAmount = "5";
// if (linkTokenAddress) {
// const [signer] = await ethers.getSigners();
// const AutomationConsumer = await ethers.getContract("AutomationConsumer", signer);

await approveAndTransfer({
tokenAddress: linkTokenAddress,
tokenABI: LinkTokenABI,
spenderAddress: AutomationConsumer.address,
amount: fundAmount,
});
// const fundAmount = "5";

// https://docs.chain.link/chainlink-automation/guides/register-upkeep-in-contract#register-the-upkeep
const registrationParams = {
name: "programmatic registration",
encryptedEmail: "0x",
upkeepContract: AutomationConsumer.address,
gasLimit: 500000,
adminAddress: deployer,
triggerType: 0, // 0 for conditional upkeep
checkData: "0x",
triggerConfig: "0x",
offchainConfig: "0x",
amount: ethers.utils.parseUnits(fundAmount, 18),
};
console.log("Registering upkeep...");
const registerUpkeepTx = await AutomationConsumer.registerNewUpkeep(registrationParams);
console.log("Register tx hash:", registerUpkeepTx.hash);
await registerUpkeepTx.wait();
console.log("Successfully registered upkeep with ID:", await AutomationConsumer.s_upkeepID());
}
// await approveAndTransfer({
// tokenAddress: linkTokenAddress,
// tokenABI: LinkTokenABI,
// spenderAddress: AutomationConsumer.address,
// amount: fundAmount,
// });

// // https://docs.chain.link/chainlink-automation/guides/register-upkeep-in-contract#register-the-upkeep
// const registrationParams = {
// name: "AutomationConsumer",
// encryptedEmail: "0x",
// upkeepContract: AutomationConsumer.address,
// gasLimit: "500000",
// adminAddress: deployer,
// triggerType: 0, // 0 for conditional upkeep
// checkData: "0x",
// triggerConfig: "0x",
// offchainConfig: "0x",
// amount: ethers.utils.parseUnits(fundAmount, 18),
// };
// console.log("registrationParams", registrationParams);

// try {
// console.log("Registering upkeep...");
// const registerUpkeepTx = await AutomationConsumer.registerNewUpkeep(registrationParams);
// console.log("Register tx hash:", registerUpkeepTx.hash);
// await registerUpkeepTx.wait();
// console.log("Successfully registered upkeep with ID:", await AutomationConsumer.s_upkeepID());
// } catch (e) {
// console.log(e);
// console.log("Failed to register upkeep");
// }
// }
};

export default deployAutomationConsumer;
Expand Down
2 changes: 2 additions & 0 deletions packages/hardhat/helper-hardhat-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ interface NetworkConfigEntryTypes {
AutomationConsumer: {
registrarAddress?: string;
linkTokenAddress?: string;
registryAddress?: string;
};
}

Expand All @@ -22,6 +23,7 @@ const networkConfig: { [key: number]: NetworkConfigEntryTypes } = {
AutomationConsumer: {
linkTokenAddress: "0x779877A7B0D9E8603169DdbD7836e478b4624789",
registrarAddress: "0x9a811502d843E5a03913d5A2cfb646c11463467A",
registryAddress: "0x86EFBD0b6736Bed994962f9797049422A3A8E8Ad",
},
VRFConsumer: {
VRFV2WrapperAddress: "0xab18414CD93297B0d12ac29E63Ca20f515b3DB46",
Expand Down
6 changes: 3 additions & 3 deletions packages/hardhat/scripts/approveAndTransfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ export async function approveAndTransfer({ tokenAddress, tokenABI, spenderAddres
const parsedAmount = ethers.utils.parseUnits(amount, await tokenContract.decimals());
const approveTx = await tokenContract.approve(spenderAddress, parsedAmount);
const approveTxReceipt = await approveTx.wait();
console.log("Approve tx hash:", approveTxReceipt.transactionHash);
console.log("approveTx hash:", approveTxReceipt.transactionHash);

console.log(`Transferring ${spenderAddress} 5 LINK...`);
console.log(`Transferring ${spenderAddress} ${amount} LINK...`);
const transferTx = await tokenContract.transfer(spenderAddress, parsedAmount);
const transferTxReceipt = await transferTx.wait();
console.log("Transfer tx hash:", transferTxReceipt.transactionHash);
console.log("transferTx hash:", transferTxReceipt.transactionHash);

const contractTokenBalance = await tokenContract.balanceOf(spenderAddress);
console.log(`${spenderAddress} new token balance: ${contractTokenBalance.toString()}`);
Expand Down
6 changes: 2 additions & 4 deletions packages/nextjs/components/automation/Events.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,8 @@ export const Events = () => {
console.log("events", events);

return (
<section>
<h4 className="text-center font-medium text-xl">UpkeepPerformed Events</h4>

<div className="bg-base-200 h-[310px] rounded-xl">
<section className="mb-5 grow bg-base-200 rounded-xl">
<div className="">
{!eventsData || isLoadingEvents ? (
<div className="w-full h-full flex flex-col justify-center items-center">
<Spinner width="75" height="75" />
Expand Down
22 changes: 15 additions & 7 deletions packages/nextjs/components/automation/Showcase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,22 +57,30 @@ export const Showcase = () => {
</div>

<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
<div className="flex flex-col">
<Events />
</div>

<div>
<p className="text-xl">
Since every transaction costs gas and the cost must be computed before a transaction can be processed, smart
contracts cannot be programmed to call functions triggered by indefinite time intervals or indeterministic
conditional logic.
Since smart contracts cannot initiate functions or update their state by themselves, they require externally
owned accounts to trigger their execution. Chainlink automation is a reliable way to outsource smart
contract operations.
</p>

<p className="text-xl">
Click start to update boolean state variable integrated with the <InlineCode text="checkUpkeep" />{" "}
function&apos;s return value that controls if chainlink nodes should call the{" "}
<InlineCode text="performUpkeep" /> function.
</p>

<p className="text-xl">
Modify the <InlineCode text="s_interval" /> to get a sense of how often the chainlink keeper nodes will
trigger the <InlineCode text="performUpkeep" /> function.
</p>
</div>

<div className="flex flex-col">
<h4 className="text-center font-medium text-xl">UpkeepPerformed Events</h4>

<Events />

<div className="bg-base-200 rounded-xl flex flex-wrap justify-around items-center">
<div className="stats">
<div className="stat bg-base-200">
Expand Down
Loading

0 comments on commit b107a69

Please sign in to comment.