From 5f04ce8018a31c95162ef9f22ed521cebd9993a0 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Tue, 13 Jul 2021 18:12:49 -0300 Subject: [PATCH 1/4] First attempt at showing chainlink eth/usd and fast gas price feeds info --- package-lock.json | 11 +++++++ package.json | 1 + src/PriceBox.tsx | 79 +++++++++++++++++++++++++++++++++++++++++++++++ src/Title.tsx | 44 ++++++++++++++------------ 4 files changed, 115 insertions(+), 20 deletions(-) create mode 100644 src/PriceBox.tsx diff --git a/package-lock.json b/package-lock.json index 9fe273e..df0002c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "version": "0.1.0", "license": "MIT", "dependencies": { + "@chainlink/contracts": "^0.2.1", "@craco/craco": "^6.2.0", "@fontsource/fira-code": "^4.5.0", "@fontsource/roboto": "^4.5.0", @@ -1203,6 +1204,11 @@ "version": "0.2.3", "license": "MIT" }, + "node_modules/@chainlink/contracts": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@chainlink/contracts/-/contracts-0.2.1.tgz", + "integrity": "sha512-mAQgPQKiqW3tLMlp31NgcnXpwG3lttgKU0izAqKiirJ9LH7rQ+O0oHIVR5Qp2yuqgmfbLsgfdLo4GcVC8IFz3Q==" + }, "node_modules/@cnakazawa/watch": { "version": "1.0.4", "license": "Apache-2.0", @@ -19913,6 +19919,11 @@ "@bcoe/v8-coverage": { "version": "0.2.3" }, + "@chainlink/contracts": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@chainlink/contracts/-/contracts-0.2.1.tgz", + "integrity": "sha512-mAQgPQKiqW3tLMlp31NgcnXpwG3lttgKU0izAqKiirJ9LH7rQ+O0oHIVR5Qp2yuqgmfbLsgfdLo4GcVC8IFz3Q==" + }, "@cnakazawa/watch": { "version": "1.0.4", "requires": { diff --git a/package.json b/package.json index d76b66f..09b401f 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "private": true, "license": "MIT", "dependencies": { + "@chainlink/contracts": "^0.2.1", "@craco/craco": "^6.2.0", "@fontsource/fira-code": "^4.5.0", "@fontsource/roboto": "^4.5.0", diff --git a/src/PriceBox.tsx b/src/PriceBox.tsx new file mode 100644 index 0000000..be78555 --- /dev/null +++ b/src/PriceBox.tsx @@ -0,0 +1,79 @@ +import React, { useState, useEffect, useMemo, useContext } from "react"; +import { ethers } from "ethers"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faGasPump } from "@fortawesome/free-solid-svg-icons"; +import AggregatorV3Interface from "@chainlink/contracts/abi/v0.8/AggregatorV3Interface.json"; +import { RuntimeContext } from "./useRuntime"; + +const ETH_FEED_DECIMALS = 8; + +const PriceBox: React.FC = () => { + const { provider } = useContext(RuntimeContext); + const ethFeed = useMemo( + () => + provider && + new ethers.Contract("eth-usd.data.eth", AggregatorV3Interface, provider), + [provider] + ); + const gasFeed = useMemo( + () => + provider && + new ethers.Contract( + "fast-gas-gwei.data.eth", + AggregatorV3Interface, + provider + ), + [provider] + ); + + const [latestPriceData, setLatestPriceData] = useState(); + const [latestGasData, setLatestGasData] = useState(); + useEffect(() => { + if (!ethFeed || !gasFeed) { + return; + } + + const readData = async () => { + const [priceData, gasData] = await Promise.all([ + ethFeed.latestRoundData(), + await gasFeed.latestRoundData(), + ]); + setLatestPriceData(priceData); + setLatestGasData(gasData); + }; + readData(); + }, [ethFeed, gasFeed]); + + return ( + <> + {latestPriceData && ( +
+ + Eth: $ + + {ethers.utils.commify( + ethers.utils.formatUnits( + latestPriceData.answer, + ETH_FEED_DECIMALS + ) + )} + + + {latestGasData && ( + <> + | + + + + {ethers.utils.formatUnits(latestGasData.answer, "gwei")} Gwei + + + + )} +
+ )} + + ); +}; + +export default React.memo(PriceBox); diff --git a/src/Title.tsx b/src/Title.tsx index c8aab8f..c64fdbd 100644 --- a/src/Title.tsx +++ b/src/Title.tsx @@ -1,6 +1,7 @@ import React, { useState, useRef } from "react"; import { Link, useHistory } from "react-router-dom"; import useKeyboardShortcut from "use-keyboard-shortcut"; +import PriceBox from "./PriceBox"; const Title: React.FC = () => { const [search, setSearch] = useState(); @@ -41,27 +42,30 @@ const Title: React.FC = () => { Otterscan -
- - -
+ + + + ); }; From a36749ee3d3721c29fa93581db0a8283a5306b5a Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Tue, 13 Jul 2021 18:55:32 -0300 Subject: [PATCH 2/4] Apply value formatting --- src/PriceBox.tsx | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/src/PriceBox.tsx b/src/PriceBox.tsx index be78555..933d046 100644 --- a/src/PriceBox.tsx +++ b/src/PriceBox.tsx @@ -4,6 +4,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faGasPump } from "@fortawesome/free-solid-svg-icons"; import AggregatorV3Interface from "@chainlink/contracts/abi/v0.8/AggregatorV3Interface.json"; import { RuntimeContext } from "./useRuntime"; +import { formatValue } from "./components/formatter"; const ETH_FEED_DECIMALS = 8; @@ -44,29 +45,43 @@ const PriceBox: React.FC = () => { readData(); }, [ethFeed, gasFeed]); + const [latestPrice, timestamp] = useMemo(() => { + if (!latestPriceData) { + return [undefined, undefined]; + } + + const price = latestPriceData.answer.div(10 ** (ETH_FEED_DECIMALS - 2)); + const formattedPrice = ethers.utils.commify( + ethers.utils.formatUnits(price, 2) + ); + + const timestamp = new Date(latestPriceData.updatedAt * 1000); + return [formattedPrice, timestamp]; + }, [latestPriceData]); + + const latestGasPrice = useMemo(() => { + if (!latestGasData) { + return undefined; + } + return formatValue(latestGasData.answer, 9); + }, [latestGasData]); + return ( <> {latestPriceData && ( -
+
- Eth: $ - - {ethers.utils.commify( - ethers.utils.formatUnits( - latestPriceData.answer, - ETH_FEED_DECIMALS - ) - )} - + Eth: ${latestPrice} {latestGasData && ( <> | - - {ethers.utils.formatUnits(latestGasData.answer, "gwei")} Gwei - + {latestGasPrice} Gwei )} From d2555f14cb4a220e7858d4512c2333fbee54bb4f Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Tue, 13 Jul 2021 19:07:42 -0300 Subject: [PATCH 3/4] Display last eth/usd price and fast gas price updates separately on hover tooltip --- src/PriceBox.tsx | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/PriceBox.tsx b/src/PriceBox.tsx index 933d046..1598276 100644 --- a/src/PriceBox.tsx +++ b/src/PriceBox.tsx @@ -45,7 +45,7 @@ const PriceBox: React.FC = () => { readData(); }, [ethFeed, gasFeed]); - const [latestPrice, timestamp] = useMemo(() => { + const [latestPrice, latestPriceTimestamp] = useMemo(() => { if (!latestPriceData) { return [undefined, undefined]; } @@ -59,27 +59,32 @@ const PriceBox: React.FC = () => { return [formattedPrice, timestamp]; }, [latestPriceData]); - const latestGasPrice = useMemo(() => { + const [latestGasPrice, latestGasPriceTimestamp] = useMemo(() => { if (!latestGasData) { - return undefined; + return [undefined, undefined]; } - return formatValue(latestGasData.answer, 9); + + const formattedGas = formatValue(latestGasData.answer, 9); + const timestamp = new Date(latestGasData.updatedAt * 1000); + return [formattedGas, timestamp]; }, [latestGasData]); return ( <> {latestPriceData && ( -
- +
+ Eth: ${latestPrice} {latestGasData && ( <> | - + {latestGasPrice} Gwei From 2c650f06f7754a99db6f2d462e7d4105d70124bb Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Tue, 13 Jul 2021 19:23:39 -0300 Subject: [PATCH 4/4] Signal possibly outdated feed info based on the tip of the chain --- src/PriceBox.tsx | 12 +++++++++++- tailwind.config.js | 3 +++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/PriceBox.tsx b/src/PriceBox.tsx index 1598276..af5e461 100644 --- a/src/PriceBox.tsx +++ b/src/PriceBox.tsx @@ -5,11 +5,17 @@ import { faGasPump } from "@fortawesome/free-solid-svg-icons"; import AggregatorV3Interface from "@chainlink/contracts/abi/v0.8/AggregatorV3Interface.json"; import { RuntimeContext } from "./useRuntime"; import { formatValue } from "./components/formatter"; +import { useLatestBlock } from "./useLatestBlock"; const ETH_FEED_DECIMALS = 8; const PriceBox: React.FC = () => { const { provider } = useContext(RuntimeContext); + const latestBlock = useLatestBlock(provider); + + const maybeOutdated: boolean = + latestBlock !== undefined && + Date.now() / 1000 - latestBlock.timestamp > 3600; const ethFeed = useMemo( () => provider && @@ -72,7 +78,11 @@ const PriceBox: React.FC = () => { return ( <> {latestPriceData && ( -
+
diff --git a/tailwind.config.js b/tailwind.config.js index 6db5164..3f72cce 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,3 +1,5 @@ +const colors = require("tailwindcss/colors"); + module.exports = { purge: ["./src/**/*.{js,jsx,ts,tsx}", "./public/index.html"], darkMode: false, // or 'media' or 'class' @@ -6,6 +8,7 @@ module.exports = { colors: { "link-blue": "#3498db", "link-blue-hover": "#0468ab", + orange: colors.orange, }, fontFamily: { sans: ["Roboto"],