Skip to content

Commit

Permalink
basic automation showcase finished
Browse files Browse the repository at this point in the history
  • Loading branch information
MattPereira committed Oct 22, 2023
1 parent 0541db5 commit 6050df6
Show file tree
Hide file tree
Showing 11 changed files with 318 additions and 135 deletions.
23 changes: 16 additions & 7 deletions packages/hardhat/contracts/AutomationConsumer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@ error AutomationConsumer__UpkeepNotNeeded();
* - try to figure out how to use performData
*/
contract AutomationConsumer is AutomationCompatibleInterface, Ownable {
event UpkeepPerformed(uint256 counter);
event CounterStarted(uint256 counter);
event CounterStopped(uint256 counter);
event UpkeepPerformed(uint256 indexed timestamp, uint256 indexed counter);
event CounterStarted(uint256 indexed counter);
event CounterStopped(uint256 indexed counter);
event IntervalUpdated(uint256 indexed interval);

uint public counter = 0;
uint public interval = 5 seconds;
uint public interval = 1 minutes;
uint public maxCounterValue = 10;
bool public isCounting = false;
uint public lastTimeStamp;
Expand All @@ -38,6 +39,15 @@ contract AutomationConsumer is AutomationCompatibleInterface, Ownable {
emit CounterStopped(counter);
}

function resetCounter() public {
counter = 0;
}

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

/**
* - checkData param is not used in this example, but it can be set when the Upkeep is registered
* @return upkeepNeeded - if true, performUpkeep() will be called
Expand Down Expand Up @@ -74,10 +84,9 @@ contract AutomationConsumer is AutomationCompatibleInterface, Ownable {
}

uint incrementValue = abi.decode(performData, (uint));
lastTimeStamp = block.timestamp;
counter += incrementValue;

emit UpkeepPerformed(counter);
lastTimeStamp = block.timestamp;
emit UpkeepPerformed(block.timestamp, counter);
}

// Utilitiy functions
Expand Down
74 changes: 74 additions & 0 deletions packages/nextjs/components/automation/Events.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { useEffect, useState } from "react";
import { Spinner } from "~~/components/assets/Spinner";
import { useScaffoldEventHistory, useScaffoldEventSubscriber } from "~~/hooks/scaffold-eth/";

type Event = {
timestamp: bigint | undefined;
counter: bigint | undefined;
};

export const Events = () => {
const [events, setEvents] = useState<Event[]>([]);

useScaffoldEventSubscriber({
contractName: "AutomationConsumer",
eventName: "UpkeepPerformed",
listener: logs => {
logs.forEach(log => {
const { timestamp, counter } = log.args;
console.log("📡 UpkeepPerformed event", timestamp, counter);
if (timestamp && counter) {
setEvents(events => [...events, { timestamp, counter }]);
}
});
},
});

const { data: eventsData, isLoading: isLoadingEvents } = useScaffoldEventHistory({
contractName: "AutomationConsumer",
eventName: "UpkeepPerformed",
fromBlock: 31231n,
});

useEffect(() => {
if (!events?.length && !!eventsData?.length && !isLoadingEvents) {
setEvents(
eventsData.map(({ args }) => ({
timestamp: args.timestamp,
counter: args.counter,
})) || [],
);
}
}, [events.length, eventsData, isLoadingEvents]);

console.log("events", events);

return (
<div className="bg-base-200 h-[350px] rounded-xl">
{!eventsData || isLoadingEvents ? (
<div className="w-full h-full flex flex-col justify-center items-center">
<Spinner width="75" height="75" />
</div>
) : (
<div className="overflow-x-auto overflow-y-auto h-full hide-scrollbar ">
<table className="table">
<thead className="text-lg">
<tr className="border-b border-neutral-500">
<th>Timestamp</th>
<th>Count</th>
</tr>
</thead>
<tbody className="text-lg">
{events.map((event, idx) => (
<tr key={idx} className="border-b border-neutral-500">
<td>{new Date(Number(event.timestamp) * 1000).toLocaleTimeString()}</td>
<td>{event?.counter?.toString()}</td>
</tr>
))}
</tbody>
</table>
</div>
)}
</div>
);
};
89 changes: 49 additions & 40 deletions packages/nextjs/components/automation/Showcase.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { ExternalLinkButton } from "../common";
import { InlineCode } from "../common";
import { Events } from "./Events";
import { Address } from "~~/components/scaffold-eth";
import { useScaffoldContract, useScaffoldContractRead, useScaffoldContractWrite } from "~~/hooks/scaffold-eth";

export const Showcase = () => {
const { data: vrfConsumerContract } = useScaffoldContract({ contractName: "AutomationConsumer" });

const { data: linkBalance } = useScaffoldContractRead({
contractName: "AutomationConsumer",
functionName: "getLinkBalance",
});
// const { data: linkBalance } = useScaffoldContractRead({
// contractName: "AutomationConsumer",
// functionName: "getLinkBalance",
// });

const { data: currentCount } = useScaffoldContractRead({
contractName: "AutomationConsumer",
Expand All @@ -21,6 +22,11 @@ export const Showcase = () => {
functionName: "isCounting",
});

const { data: interval } = useScaffoldContractRead({
contractName: "AutomationConsumer",
functionName: "interval",
});

const { writeAsync: startCounting } = useScaffoldContractWrite({
contractName: "AutomationConsumer",
functionName: "startCounting",
Expand All @@ -39,62 +45,65 @@ export const Showcase = () => {

return (
<div className="bg-base-100 rounded-xl mb-10 p-10">
<div className="flex flex-col justify-center gap-2 items-center mb-10">
<div className="flex flex-wrap justify-between gap-2 items-center mb-10">
<div className="flex items-center gap-4">
<h3 className="text-2xl md:text-3xl mb-0 font-bold">AutomationConsumer</h3>
<ExternalLinkButton href="https://github.com/MattPereira/speedrun-chainlink/blob/main/packages/hardhat/contracts/AutomationConsumer.sol" />
</div>

<div className="badge badge-warning">{linkBalance?.toString()} LINK</div>

{/* <div className="badge badge-warning">{linkBalance?.toString()} LINK</div> */}
<div>
<Address size="xl" address={vrfConsumerContract?.address} />
</div>
</div>

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

<div>
<h3 className="text-2xl font-bold">EVM Limitations</h3>
<p className="text-xl">
The design of the EVM does not allow for smart contracts to call functions on time based or future
conditional logic based triggers. Since every transaction costs gas and the cost must be computed before a
transaction can be processed, it is not possible to create a function that increments indefinitely or is
triggered by some conditional logic that has not occurred yet.
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.
</p>
<div>
<h3 className="text-2xl font-bold">Explanation</h3>
<p className="text-xl">
Click start button 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>
</div>
</div>
<div className="flex flex-col">
<h3 className="text-2xl font-bold">Simple Counter </h3>

<div className="grid grid-flow-col gap-5 text-center auto-cols-max mb-5 justify-center items-center">
<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>
<div className="bg-base-200 rounded-xl flex flex-wrap justify-around items-center">
<div className="stats">
<div className="stat bg-base-200">
<div className="stat-value">{interval?.toString()}</div>
<div className="stat-title">interval</div>
</div>
</div>
<div className="stats">
<div className="stat bg-base-200">
<div className="stat-value">{isCounting?.toString()}</div>
<div className={`stat-value ${isCounting ? "text-green-500" : "text-red-500"}`}>
{isCounting?.toString()}
</div>
<div className="stat-title">isCounting</div>
</div>
</div>
<div className="flex flex-col p-2 bg-base-200 rounded-box text-white">
<span className="countdown font-mono text-5xl">
<span style={{ "--value": Number(currentCount) } as React.CSSProperties}></span>
</span>
counter
<div className="stats">
<div className="stat bg-base-200">
<div className="stat-value">{Number(currentCount)}</div>
<div className="stat-title">counter</div>
</div>
</div>
<button onClick={() => startCounting()} className="btn btn-success">
Start
</button>
<button onClick={() => stopCounting()} className="btn btn-error">
Stop
</button>
</div>
<div className="bg-base-200 grow rounded-xl">
<h4 className="text-xl font-bold text-center mt-3">Events</h4>

{isCounting ? (
<button onClick={() => stopCounting()} className="btn btn-error">
Stop
</button>
) : (
<button onClick={() => startCounting()} className="btn btn-success">
Start
</button>
)}
</div>
</div>
</div>
Expand Down
54 changes: 29 additions & 25 deletions packages/nextjs/components/common/InformationSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,33 +8,37 @@ interface InformationSectionProps {

export const InformationSection: React.FC<InformationSectionProps> = ({ summary, details, gettingStarted }) => {
return (
<section>
<div className="mb-10">
<h3 className="text-3xl font-bold">Summary</h3>
<p className="text-xl">{summary}</p>
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-14">
<div>
<h3 className="text-3xl font-bold">Getting Started</h3>
<ol className="text-xl list-decimal list-inside">
{gettingStarted.map((step, index) => (
<li key={index} className="mb-1">
{step}
</li>
))}
</ol>
<div className="collapse collapse-plus bg-base-100 p-4">
<input type="radio" name="my-accordion-1" />
<div className="collapse-title text-3xl font-bold">Intro Guide</div>
<div className="collapse-content">
<div className="mb-10">
{/* <h3 className="text-2xl font-bold">Summary</h3> */}
<p className="text-xl">{summary}</p>
</div>
<div>
<h3 className="text-3xl font-bold">Details</h3>
<ul className="text-xl list-disc list-inside">
{details.map((detail, index) => (
<li key={index} className="mb-1 text-xl">
{detail}
</li>
))}
</ul>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-14">
<div>
<h3 className="text-2xl font-bold">Getting Started</h3>
<ol className="text-xl list-decimal list-inside">
{gettingStarted.map((step, index) => (
<li key={index} className="mb-1">
{step}
</li>
))}
</ol>
</div>
<div>
<h3 className="text-2xl font-bold">Details</h3>
<ul className="text-xl list-disc list-inside">
{details.map((detail, index) => (
<li key={index} className="mb-1 text-xl">
{detail}
</li>
))}
</ul>
</div>
</div>
</div>
</section>
</div>
);
};
2 changes: 1 addition & 1 deletion packages/nextjs/components/common/InlineCode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ type Props = {
};

export const InlineCode: React.FC<Props> = ({ text }) => {
return <code className="bg-base-100 text-neutral-100 border border-base-300 py-0.5 px-1.5 rounded-md">{text}</code>;
return <code className="bg-[#273F66] text-neutral-100 border border-base-200 py-0.5 px-1 rounded-md">{text}</code>;
};
Loading

0 comments on commit 6050df6

Please sign in to comment.