Skip to content

Commit

Permalink
improve price feed showcase
Browse files Browse the repository at this point in the history
  • Loading branch information
MattPereira committed Oct 25, 2023
1 parent 3e91408 commit de49003
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 60 deletions.
23 changes: 9 additions & 14 deletions packages/nextjs/components/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React, { useCallback, useRef, useState } from "react";
import Image from "next/image";
import Link from "next/link";
import { useRouter } from "next/router";
import { BanknotesIcon, Bars3Icon, BugAntIcon, LinkIcon, MagnifyingGlassIcon } from "@heroicons/react/24/outline";
Expand Down Expand Up @@ -91,9 +90,9 @@ export const Header = () => {
);

return (
<div className="sticky lg:static top-0 navbar bg-base-100 min-h-0 flex-shrink-0 justify-between z-20 shadow-md shadow-secondary px-0 sm:px-2">
<div className="navbar-start w-auto lg:w-1/2">
<div className="lg:hidden dropdown" ref={burgerMenuRef}>
<div className="sticky xl:static top-0 navbar bg-base-100 min-h-0 flex-shrink-0 justify-between z-20 shadow-md shadow-secondary px-0 sm:px-2">
<div className="navbar-start w-auto">
<div className="xl:hidden dropdown" ref={burgerMenuRef}>
<label
tabIndex={0}
className={`ml-1 btn btn-ghost ${isDrawerOpen ? "hover:bg-secondary" : "hover:bg-transparent"}`}
Expand All @@ -115,18 +114,14 @@ export const Header = () => {
</ul>
)}
</div>
<Link href="/" passHref className="hidden lg:flex items-center gap-2 ml-4 mr-6 shrink-0">
<div className="flex relative w-10 h-10">
<Image alt="SE2 logo" className="cursor-pointer" fill src="/logo.svg" />
</div>
<div className="flex flex-col">
<span className="font-bold leading-tight">Speedrun Chainlink</span>
<span className="text-xs">with scaffold eth 2</span>
</div>
<Link href="/" passHref className="hidden xl:flex items-center gap-2 ml-4 mr-6 shrink-0 font-bold text-xl">
<span className="text-2xl">🏃💨</span> Speedrun Chainlink
</Link>
<ul className="hidden lg:flex lg:flex-nowrap menu menu-horizontal px-1 gap-2">{navLinks}</ul>
</div>
<div className="navbar-end flex-grow mr-4">
<div>
<ul className="hidden xl:flex xl:flex-nowrap menu menu-horizontal px-1 gap-2">{navLinks}</ul>
</div>
<div className=" mr-4">
<RainbowKitCustomConnectButton />
<FaucetButton />
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ export const AggregatorV3Consumer = () => {
<Address size="xl" address={aggregatorV3Consumer?.address} />
</div>

<p className="text-xl">The latest price quote returned by ETH/USD price feed contract on sepolia</p>

{!latestPrice || !decimals || !description ? (
<p>Loading...</p>
) : (
Expand Down
105 changes: 72 additions & 33 deletions packages/nextjs/components/price-feeds/FeedRegistry.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,46 @@
import { useState } from "react";
import { useEffect, useState } from "react";
import { ExternalLinkButton } from "../common";
import { StatDisplay } from "./AggregatorV3Consumer";
import { formatUnits } from "viem";
import { useContractRead } from "wagmi";
import { Spinner } from "~~/components/assets/Spinner";
import { Address } from "~~/components/scaffold-eth";
import { AddressInput } from "~~/components/scaffold-eth/Input";
import FeedRegistry from "~~/utils/external-contracts/FeedRegistry";

/**
* @dev FeedRegistry contract is only available on mainnet
*/
export const FeedRegistryDisplay = () => {
const [base, setBase] = useState(BASE_OPTIONS.BTC);
const [baseAssetAddress, setBaseAssetAddress] = useState("");

useEffect(() => {
setBaseAssetAddress(BASE_OPTIONS[0].address);
}, []);

const { data: description } = useContractRead({
address: FeedRegistry.address,
abi: FeedRegistry.abi,
functionName: "description",
args: [base, QUOTE],
args: [baseAssetAddress, QUOTE],
chainId: 1, // force request on mainnet
});

const { data: roundData } = useContractRead({
const { data: roundData, isLoading } = useContractRead({
address: FeedRegistry.address,
abi: FeedRegistry.abi,
functionName: "latestRoundData",
args: [base, QUOTE],
args: [baseAssetAddress, QUOTE],
chainId: 1, // force request on mainnet
});

console.log(roundData);

const { data: decimals } = useContractRead({
address: FeedRegistry.address,
abi: FeedRegistry.abi,
functionName: "decimals",
args: [base, QUOTE],
args: [baseAssetAddress, QUOTE],
chainId: 1, // force request on mainnet
});

Expand All @@ -47,8 +55,6 @@ export const FeedRegistryDisplay = () => {

const items = [
{ title: "description()", value: description?.toString(), type: "string" },
{ title: "latestRoundData().answer", value: price?.toString(), type: "int" },
{ title: "decimals()", value: decimals?.toString(), type: "uint8" },
{
title: "formatUnits(price, decimals)",
value: price && decimals ? "$" + parseFloat(formatUnits(price, Number(decimals))).toFixed(2) : "N/A",
Expand All @@ -65,9 +71,14 @@ export const FeedRegistryDisplay = () => {
</div>
<Address size="xl" address={"0x47Fb2585D2C56Fe188D0E6ec628a38b74fCeeeDf"} />
</div>
<p className="text-xl">Enter a supported mainnet token address to see the latest price quote in USD</p>

{!price || !description || !decimals ? (
<p>loading...</p>
{isLoading ? (
<div className="stats bg-base-200 shadow-lg">
<div className="stat">
<Spinner />
</div>
</div>
) : (
<div className="mb-8 flex flex-wrap gap-4">
{items.map(item => (
Expand All @@ -76,35 +87,63 @@ export const FeedRegistryDisplay = () => {
</div>
)}

<div className="flex items-center justify-end">
<div className="mr-4">
<label className="text-xl">Base Asset</label>
</div>
<div>
<select
className="select select-bordered w-full text-center bg-base-200 text-2xl"
value={base}
onChange={e => setBase(e.target.value)}
>
{Object.entries(BASE_OPTIONS).map(([key, value]) => (
<option key={key} value={value} className="font-bold flex space-between">
{key} : {value.slice(0, 5) + "..." + value.slice(-3)}
</option>
))}
</select>
<div className="ml-3 mb-1">
<label className="text-xl">Base Asset</label>
</div>
<div className="flex items-center justify-end gap-4">
<div className="grow">
<AddressInput value={baseAssetAddress} onChange={val => setBaseAssetAddress(val)} />
</div>
<button
className="btn btn-accent text-primary"
onClick={() => {
const modal = document?.getElementById("my_modal_2") as HTMLDialogElement;
modal?.showModal();
}}
>
Addresses
</button>
<dialog id="my_modal_2" className="modal">
<div className="modal-box">
<h3 className="font-bold text-lg">Token Addresses</h3>
<p>Copy and paste a token address into the address input field</p>
<div className="overflow-x-auto">
<table className="table">
<thead>
<tr>
<th>Symbol</th>
<th>Address</th>
</tr>
</thead>
<tbody>
{BASE_OPTIONS.map((option, i) => (
<tr key={i}>
<td>{option.symbol}</td>
<td>{option.address}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
<form method="dialog" className="modal-backdrop">
<button>close</button>
</form>
</dialog>
</div>
</div>
);
};

const BASE_OPTIONS = {
BTC: "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB",
ETH: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
LINK: "0x514910771AF9Ca656af840dff83E8264EcF986CA",
AAVE: "0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9",
YFI: "0x0bc529c00c6401aef6d220be8c6ea1667f6ad93e",
};
const BASE_OPTIONS = [
{ symbol: "BTC", address: "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" },
{ symbol: "ETH", address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" },
{ symbol: "MATIC", address: "0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0" },
{ symbol: "LINK", address: "0x514910771AF9Ca656af840dff83E8264EcF986CA" },
{ symbol: "AAVE", address: "0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9" },
{ symbol: "YFI", address: "0x0bc529c00c6401aef6d220be8c6ea1667f6ad93e" },
{ symbol: "CRV", address: "0xD533a949740bb3306d119CC777fa900bA034cd52" },
];

// address for USD
const QUOTE = "0x0000000000000000000000000000000000000348";
18 changes: 10 additions & 8 deletions packages/nextjs/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ const Home: NextPage = () => {
<div className="">
<MetaHeader />
<div className="container mx-auto py-20 px-5">
<h1 className="text-center text-5xl font-bold mb-14">🏃Speedrun Chainlink</h1>
<p className="text-xl text-center mb-14">
<h1 className="text-center text-5xl font-bold mb-10">Speedrun Chainlink</h1>
<div className="text-center text-5xl mb-10">🏃💨</div>
<p className="text-2xl text-center mb-10">
A beginner&apos;s guide to using chainlink products with{" "}
<Link className="text-accent" href="https://github.com/scaffold-eth/scaffold-eth-2">
Scaffold-ETH 2
Expand Down Expand Up @@ -44,14 +45,15 @@ const PRODUCTS = [
path: "/price-feeds",
description: "Learn how to integrate price feeds in your smart contracts using AggregatorV3Interface",
},
{
name: "🤖 Automation",
path: "/automation",
description: "Learn how to automate the execution of a transaction based on some condition",
},
{
name: "🎲 VRF",
path: "/vrf",
description: "Learn how to request a verifiably random number by spinning the wheel of fruit",
description: "Spin the wheel of fruit to request a verifiably random number from the VRF Coordinator",
},
{
name: "🤖 Automation",
path: "/automation",
description:
"Play with the simple counter contract to understand how chainlink keeper nodes perform automated tasks on chain",
},
];
8 changes: 3 additions & 5 deletions packages/nextjs/pages/price-feeds.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,9 @@ const PriceFeeds: NextPage = () => {
<InformationSection
summary={
<>
Chainlink price feeds offer a decentralized data source that provides price information for a range of
assets pairs depending on the network. The price feeds are powered by a decentralized network of
independent, security-reviewed, and Sybil-resistant oracle nodes. The{" "}
<InlineCode text="AggregatorV3Interface" /> is fixed to the price feed address used during instantiation,
but the <InlineCode text="FeedRegistry" /> is more flexible although it is only avaible on mainnet.
Chainlink price feeds bring off chain price quotes on chain using a decentralized network of oracles. The{" "}
<InlineCode text="FeedRegistry" /> contract is only avaible on mainnet, but the price feed addresses that
power the <InlineCode text="AggregatorV3Interface" /> are available on a wide range of networks
</>
}
details={[
Expand Down

0 comments on commit de49003

Please sign in to comment.