From 8624c70877eabf423ab032f6cf4edeb2939b78b5 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Tue, 31 Aug 2021 15:06:26 -0300 Subject: [PATCH 1/2] Generalize implementation to support multiple block queries --- src/usePriceOracle.ts | 51 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 8 deletions(-) diff --git a/src/usePriceOracle.ts b/src/usePriceOracle.ts index e318a47..0fc909a 100644 --- a/src/usePriceOracle.ts +++ b/src/usePriceOracle.ts @@ -7,6 +7,18 @@ import AggregatorV3Interface from "@chainlink/contracts/abi/v0.8/AggregatorV3Int export const useETHUSDOracle = ( provider: JsonRpcProvider | undefined, blockTag: BlockTag | undefined +) => { + const priceMap = useMultipleETHUSDOracle(provider, [blockTag]); + + if (blockTag === undefined) { + return undefined; + } + return priceMap[blockTag]; +}; + +export const useMultipleETHUSDOracle = ( + provider: JsonRpcProvider | undefined, + blockTags: (BlockTag | undefined)[] ) => { const ethFeed = useMemo(() => { if (!provider || provider.network.chainId !== 1) { @@ -21,22 +33,45 @@ export const useETHUSDOracle = ( } }, [provider]); - const [latestPriceData, setLatestPriceData] = useState(); + const [latestPriceData, setLatestPriceData] = useState< + Record + >({}); useEffect(() => { - if (!ethFeed || !blockTag) { + if (!ethFeed) { return; } + const priceReaders: Promise[] = []; + for (const blockTag of blockTags) { + priceReaders.push( + (async () => { + try { + const priceData = await ethFeed.latestRoundData({ blockTag }); + return BigNumber.from(priceData.answer); + } catch (err) { + console.error(err); + return undefined; + } + })() + ); + } const readData = async () => { - try { - const priceData = await ethFeed.latestRoundData({ blockTag }); - setLatestPriceData(BigNumber.from(priceData.answer)); - } catch (err) { - console.error(err); + const results = await Promise.all(priceReaders); + const priceMap: Record = {}; + for (let i = 0; i < blockTags.length; i++) { + const blockTag = blockTags[i]; + const result = results[i]; + if (blockTag === undefined || result === undefined) { + continue; + } + + priceMap[blockTag] = result; } + + setLatestPriceData(priceMap); }; readData(); - }, [ethFeed, blockTag]); + }, [ethFeed, blockTags]); return latestPriceData; }; From 7a43a56e26a1528f97807faf095e1fea155c7b9c Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Tue, 31 Aug 2021 16:49:52 -0300 Subject: [PATCH 2/2] Apply usd price on search results --- src/AddressTransactions.tsx | 11 +++++++++++ src/BlockTransactions.tsx | 1 + src/block/BlockTransactionResults.tsx | 6 ++++++ src/search/ResultHeader.tsx | 4 +++- src/search/TransactionItem.tsx | 19 ++++++++++++++++--- src/search/useFeeToggler.ts | 8 ++++---- 6 files changed, 41 insertions(+), 8 deletions(-) diff --git a/src/AddressTransactions.tsx b/src/AddressTransactions.tsx index e79253e..f4fd2ff 100644 --- a/src/AddressTransactions.tsx +++ b/src/AddressTransactions.tsx @@ -1,5 +1,6 @@ import React, { useState, useEffect, useMemo, useContext } from "react"; import { useParams, useLocation, useHistory } from "react-router-dom"; +import { BlockTag } from "@ethersproject/abstract-provider"; import { getAddress, isAddress } from "@ethersproject/address"; import queryString from "query-string"; import Blockies from "react-blockies"; @@ -16,6 +17,7 @@ import { RuntimeContext } from "./useRuntime"; import { useENSCache } from "./useReverseCache"; import { useFeeToggler } from "./search/useFeeToggler"; import { SelectionContext, useSelection } from "./useSelection"; +import { useMultipleETHUSDOracle } from "./usePriceOracle"; type BlockParams = { addressOrName: string; @@ -150,6 +152,14 @@ const AddressTransactions: React.FC = () => { const page = useMemo(() => controller?.getPage(), [controller]); const reverseCache = useENSCache(provider, page); + const blockTags: BlockTag[] = useMemo(() => { + if (!page) { + return []; + } + return page.map((p) => p.blockNumber); + }, [page]); + const priceMap = useMultipleETHUSDOracle(provider, blockTags); + document.title = `Address ${params.addressOrName} | Otterscan`; const [feeDisplay, feeDisplayToggler] = useFeeToggler(); @@ -215,6 +225,7 @@ const AddressTransactions: React.FC = () => { ensCache={reverseCache} selectedAddress={checksummedAddress} feeDisplay={feeDisplay} + priceMap={priceMap} /> ))}
diff --git a/src/BlockTransactions.tsx b/src/BlockTransactions.tsx index 5a7209f..ed9b2c7 100644 --- a/src/BlockTransactions.tsx +++ b/src/BlockTransactions.tsx @@ -47,6 +47,7 @@ const BlockTransactions: React.FC = () => { = ({ + blockTag, page, total, pageNumber, @@ -26,6 +30,7 @@ const BlockTransactionResults: React.FC = ({ const [feeDisplay, feeDisplayToggler] = useFeeToggler(); const { provider } = useContext(RuntimeContext); const reverseCache = useENSCache(provider, page); + const priceMap = useMultipleETHUSDOracle(provider, [blockTag]); return ( @@ -55,6 +60,7 @@ const BlockTransactionResults: React.FC = ({ tx={tx} ensCache={reverseCache} feeDisplay={feeDisplay} + priceMap={priceMap} /> ))}
diff --git a/src/search/ResultHeader.tsx b/src/search/ResultHeader.tsx index 13880bd..5fbcbc2 100644 --- a/src/search/ResultHeader.tsx +++ b/src/search/ResultHeader.tsx @@ -23,7 +23,9 @@ const ResultHeader: React.FC = ({ className="text-link-blue hover:text-link-blue-hover" onClick={feeDisplayToggler} > - {feeDisplay === FeeDisplay.TX_FEE ? "Txn Fee" : "Gas Price"} + {feeDisplay === FeeDisplay.TX_FEE && "Txn Fee"} + {feeDisplay === FeeDisplay.TX_FEE_USD && "Txn Fee (USD)"} + {feeDisplay === FeeDisplay.GAS_PRICE && "Gas Price"}
diff --git a/src/search/TransactionItem.tsx b/src/search/TransactionItem.tsx index f5196d8..ffafc09 100644 --- a/src/search/TransactionItem.tsx +++ b/src/search/TransactionItem.tsx @@ -1,4 +1,6 @@ import React from "react"; +import { BlockTag } from "@ethersproject/abstract-provider"; +import { BigNumber } from "@ethersproject/bignumber"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faExclamationCircle } from "@fortawesome/free-solid-svg-icons/faExclamationCircle"; import MethodName from "../components/MethodName"; @@ -15,12 +17,14 @@ import TransactionValue from "../components/TransactionValue"; import { ENSReverseCache, ProcessedTransaction } from "../types"; import { FeeDisplay } from "./useFeeToggler"; import { formatValue } from "../components/formatter"; +import ETH2USDValue from "../components/ETH2USDValue"; type TransactionItemProps = { tx: ProcessedTransaction; ensCache?: ENSReverseCache; selectedAddress?: string; feeDisplay: FeeDisplay; + priceMap: Record; }; const TransactionItem: React.FC = ({ @@ -28,6 +32,7 @@ const TransactionItem: React.FC = ({ ensCache, selectedAddress, feeDisplay, + priceMap, }) => { let direction: Direction | undefined; if (selectedAddress) { @@ -123,9 +128,17 @@ const TransactionItem: React.FC = ({ - {feeDisplay === FeeDisplay.TX_FEE - ? formatValue(tx.fee, 18) - : formatValue(tx.gasPrice, 9)} + {feeDisplay === FeeDisplay.TX_FEE && formatValue(tx.fee, 18)} + {feeDisplay === FeeDisplay.TX_FEE_USD && + (priceMap[tx.blockNumber] ? ( + + ) : ( + "N/A" + ))} + {feeDisplay === FeeDisplay.GAS_PRICE && formatValue(tx.gasPrice, 9)} ); diff --git a/src/search/useFeeToggler.ts b/src/search/useFeeToggler.ts index 7a82a9c..722221c 100644 --- a/src/search/useFeeToggler.ts +++ b/src/search/useFeeToggler.ts @@ -2,16 +2,16 @@ import { useState } from "react"; export enum FeeDisplay { TX_FEE, + TX_FEE_USD, GAS_PRICE, } export const useFeeToggler = (): [FeeDisplay, () => void] => { const [feeDisplay, setFeeDisplay] = useState(FeeDisplay.TX_FEE); const feeDisplayToggler = () => { - if (feeDisplay === FeeDisplay.TX_FEE) { - setFeeDisplay(FeeDisplay.GAS_PRICE); - } else { - setFeeDisplay(FeeDisplay.TX_FEE); + setFeeDisplay(feeDisplay + 1); + if (feeDisplay === FeeDisplay.GAS_PRICE) { + setFeeDisplay(0); } };