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);
}
};
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;
};