Merge branch 'feature/usd-price-on-search-results' into develop
This commit is contained in:
commit
47212a1db0
|
@ -1,5 +1,6 @@
|
||||||
import React, { useState, useEffect, useMemo, useContext } from "react";
|
import React, { useState, useEffect, useMemo, useContext } from "react";
|
||||||
import { useParams, useLocation, useHistory } from "react-router-dom";
|
import { useParams, useLocation, useHistory } from "react-router-dom";
|
||||||
|
import { BlockTag } from "@ethersproject/abstract-provider";
|
||||||
import { getAddress, isAddress } from "@ethersproject/address";
|
import { getAddress, isAddress } from "@ethersproject/address";
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
import Blockies from "react-blockies";
|
import Blockies from "react-blockies";
|
||||||
|
@ -16,6 +17,7 @@ import { RuntimeContext } from "./useRuntime";
|
||||||
import { useENSCache } from "./useReverseCache";
|
import { useENSCache } from "./useReverseCache";
|
||||||
import { useFeeToggler } from "./search/useFeeToggler";
|
import { useFeeToggler } from "./search/useFeeToggler";
|
||||||
import { SelectionContext, useSelection } from "./useSelection";
|
import { SelectionContext, useSelection } from "./useSelection";
|
||||||
|
import { useMultipleETHUSDOracle } from "./usePriceOracle";
|
||||||
|
|
||||||
type BlockParams = {
|
type BlockParams = {
|
||||||
addressOrName: string;
|
addressOrName: string;
|
||||||
|
@ -150,6 +152,14 @@ const AddressTransactions: React.FC = () => {
|
||||||
const page = useMemo(() => controller?.getPage(), [controller]);
|
const page = useMemo(() => controller?.getPage(), [controller]);
|
||||||
const reverseCache = useENSCache(provider, page);
|
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`;
|
document.title = `Address ${params.addressOrName} | Otterscan`;
|
||||||
|
|
||||||
const [feeDisplay, feeDisplayToggler] = useFeeToggler();
|
const [feeDisplay, feeDisplayToggler] = useFeeToggler();
|
||||||
|
@ -215,6 +225,7 @@ const AddressTransactions: React.FC = () => {
|
||||||
ensCache={reverseCache}
|
ensCache={reverseCache}
|
||||||
selectedAddress={checksummedAddress}
|
selectedAddress={checksummedAddress}
|
||||||
feeDisplay={feeDisplay}
|
feeDisplay={feeDisplay}
|
||||||
|
priceMap={priceMap}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
<div className="flex justify-between items-baseline py-3">
|
<div className="flex justify-between items-baseline py-3">
|
||||||
|
|
|
@ -47,6 +47,7 @@ const BlockTransactions: React.FC = () => {
|
||||||
<StandardFrame>
|
<StandardFrame>
|
||||||
<BlockTransactionHeader blockTag={blockNumber.toNumber()} />
|
<BlockTransactionHeader blockTag={blockNumber.toNumber()} />
|
||||||
<BlockTransactionResults
|
<BlockTransactionResults
|
||||||
|
blockTag={blockNumber.toNumber()}
|
||||||
page={txs}
|
page={txs}
|
||||||
total={totalTxs ?? 0}
|
total={totalTxs ?? 0}
|
||||||
pageNumber={pageNumber}
|
pageNumber={pageNumber}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import React, { useContext } from "react";
|
import React, { useContext } from "react";
|
||||||
|
import { BlockTag } from "@ethersproject/abstract-provider";
|
||||||
import ContentFrame from "../ContentFrame";
|
import ContentFrame from "../ContentFrame";
|
||||||
import PageControl from "../search/PageControl";
|
import PageControl from "../search/PageControl";
|
||||||
import ResultHeader from "../search/ResultHeader";
|
import ResultHeader from "../search/ResultHeader";
|
||||||
|
@ -10,14 +11,17 @@ import { SelectionContext, useSelection } from "../useSelection";
|
||||||
import { useENSCache } from "../useReverseCache";
|
import { useENSCache } from "../useReverseCache";
|
||||||
import { ProcessedTransaction } from "../types";
|
import { ProcessedTransaction } from "../types";
|
||||||
import { PAGE_SIZE } from "../params";
|
import { PAGE_SIZE } from "../params";
|
||||||
|
import { useMultipleETHUSDOracle } from "../usePriceOracle";
|
||||||
|
|
||||||
type BlockTransactionResultsProps = {
|
type BlockTransactionResultsProps = {
|
||||||
|
blockTag: BlockTag;
|
||||||
page?: ProcessedTransaction[];
|
page?: ProcessedTransaction[];
|
||||||
total: number;
|
total: number;
|
||||||
pageNumber: number;
|
pageNumber: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
const BlockTransactionResults: React.FC<BlockTransactionResultsProps> = ({
|
const BlockTransactionResults: React.FC<BlockTransactionResultsProps> = ({
|
||||||
|
blockTag,
|
||||||
page,
|
page,
|
||||||
total,
|
total,
|
||||||
pageNumber,
|
pageNumber,
|
||||||
|
@ -26,6 +30,7 @@ const BlockTransactionResults: React.FC<BlockTransactionResultsProps> = ({
|
||||||
const [feeDisplay, feeDisplayToggler] = useFeeToggler();
|
const [feeDisplay, feeDisplayToggler] = useFeeToggler();
|
||||||
const { provider } = useContext(RuntimeContext);
|
const { provider } = useContext(RuntimeContext);
|
||||||
const reverseCache = useENSCache(provider, page);
|
const reverseCache = useENSCache(provider, page);
|
||||||
|
const priceMap = useMultipleETHUSDOracle(provider, [blockTag]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ContentFrame>
|
<ContentFrame>
|
||||||
|
@ -55,6 +60,7 @@ const BlockTransactionResults: React.FC<BlockTransactionResultsProps> = ({
|
||||||
tx={tx}
|
tx={tx}
|
||||||
ensCache={reverseCache}
|
ensCache={reverseCache}
|
||||||
feeDisplay={feeDisplay}
|
feeDisplay={feeDisplay}
|
||||||
|
priceMap={priceMap}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
<div className="flex justify-between items-baseline py-3">
|
<div className="flex justify-between items-baseline py-3">
|
||||||
|
|
|
@ -23,7 +23,9 @@ const ResultHeader: React.FC<ResultHeaderProps> = ({
|
||||||
className="text-link-blue hover:text-link-blue-hover"
|
className="text-link-blue hover:text-link-blue-hover"
|
||||||
onClick={feeDisplayToggler}
|
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"}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { BlockTag } from "@ethersproject/abstract-provider";
|
||||||
|
import { BigNumber } from "@ethersproject/bignumber";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { faExclamationCircle } from "@fortawesome/free-solid-svg-icons/faExclamationCircle";
|
import { faExclamationCircle } from "@fortawesome/free-solid-svg-icons/faExclamationCircle";
|
||||||
import MethodName from "../components/MethodName";
|
import MethodName from "../components/MethodName";
|
||||||
|
@ -15,12 +17,14 @@ import TransactionValue from "../components/TransactionValue";
|
||||||
import { ENSReverseCache, ProcessedTransaction } from "../types";
|
import { ENSReverseCache, ProcessedTransaction } from "../types";
|
||||||
import { FeeDisplay } from "./useFeeToggler";
|
import { FeeDisplay } from "./useFeeToggler";
|
||||||
import { formatValue } from "../components/formatter";
|
import { formatValue } from "../components/formatter";
|
||||||
|
import ETH2USDValue from "../components/ETH2USDValue";
|
||||||
|
|
||||||
type TransactionItemProps = {
|
type TransactionItemProps = {
|
||||||
tx: ProcessedTransaction;
|
tx: ProcessedTransaction;
|
||||||
ensCache?: ENSReverseCache;
|
ensCache?: ENSReverseCache;
|
||||||
selectedAddress?: string;
|
selectedAddress?: string;
|
||||||
feeDisplay: FeeDisplay;
|
feeDisplay: FeeDisplay;
|
||||||
|
priceMap: Record<BlockTag, BigNumber>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const TransactionItem: React.FC<TransactionItemProps> = ({
|
const TransactionItem: React.FC<TransactionItemProps> = ({
|
||||||
|
@ -28,6 +32,7 @@ const TransactionItem: React.FC<TransactionItemProps> = ({
|
||||||
ensCache,
|
ensCache,
|
||||||
selectedAddress,
|
selectedAddress,
|
||||||
feeDisplay,
|
feeDisplay,
|
||||||
|
priceMap,
|
||||||
}) => {
|
}) => {
|
||||||
let direction: Direction | undefined;
|
let direction: Direction | undefined;
|
||||||
if (selectedAddress) {
|
if (selectedAddress) {
|
||||||
|
@ -123,9 +128,17 @@ const TransactionItem: React.FC<TransactionItemProps> = ({
|
||||||
<TransactionValue value={tx.value} />
|
<TransactionValue value={tx.value} />
|
||||||
</span>
|
</span>
|
||||||
<span className="font-balance text-xs text-gray-500 truncate">
|
<span className="font-balance text-xs text-gray-500 truncate">
|
||||||
{feeDisplay === FeeDisplay.TX_FEE
|
{feeDisplay === FeeDisplay.TX_FEE && formatValue(tx.fee, 18)}
|
||||||
? formatValue(tx.fee, 18)
|
{feeDisplay === FeeDisplay.TX_FEE_USD &&
|
||||||
: formatValue(tx.gasPrice, 9)}
|
(priceMap[tx.blockNumber] ? (
|
||||||
|
<ETH2USDValue
|
||||||
|
ethAmount={tx.fee}
|
||||||
|
eth2USDValue={priceMap[tx.blockNumber]}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
"N/A"
|
||||||
|
))}
|
||||||
|
{feeDisplay === FeeDisplay.GAS_PRICE && formatValue(tx.gasPrice, 9)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -2,16 +2,16 @@ import { useState } from "react";
|
||||||
|
|
||||||
export enum FeeDisplay {
|
export enum FeeDisplay {
|
||||||
TX_FEE,
|
TX_FEE,
|
||||||
|
TX_FEE_USD,
|
||||||
GAS_PRICE,
|
GAS_PRICE,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useFeeToggler = (): [FeeDisplay, () => void] => {
|
export const useFeeToggler = (): [FeeDisplay, () => void] => {
|
||||||
const [feeDisplay, setFeeDisplay] = useState<FeeDisplay>(FeeDisplay.TX_FEE);
|
const [feeDisplay, setFeeDisplay] = useState<FeeDisplay>(FeeDisplay.TX_FEE);
|
||||||
const feeDisplayToggler = () => {
|
const feeDisplayToggler = () => {
|
||||||
if (feeDisplay === FeeDisplay.TX_FEE) {
|
setFeeDisplay(feeDisplay + 1);
|
||||||
setFeeDisplay(FeeDisplay.GAS_PRICE);
|
if (feeDisplay === FeeDisplay.GAS_PRICE) {
|
||||||
} else {
|
setFeeDisplay(0);
|
||||||
setFeeDisplay(FeeDisplay.TX_FEE);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,18 @@ import AggregatorV3Interface from "@chainlink/contracts/abi/v0.8/AggregatorV3Int
|
||||||
export const useETHUSDOracle = (
|
export const useETHUSDOracle = (
|
||||||
provider: JsonRpcProvider | undefined,
|
provider: JsonRpcProvider | undefined,
|
||||||
blockTag: BlockTag | 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(() => {
|
const ethFeed = useMemo(() => {
|
||||||
if (!provider || provider.network.chainId !== 1) {
|
if (!provider || provider.network.chainId !== 1) {
|
||||||
|
@ -21,22 +33,45 @@ export const useETHUSDOracle = (
|
||||||
}
|
}
|
||||||
}, [provider]);
|
}, [provider]);
|
||||||
|
|
||||||
const [latestPriceData, setLatestPriceData] = useState<BigNumber>();
|
const [latestPriceData, setLatestPriceData] = useState<
|
||||||
|
Record<BlockTag, BigNumber>
|
||||||
|
>({});
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!ethFeed || !blockTag) {
|
if (!ethFeed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const priceReaders: Promise<BigNumber | undefined>[] = [];
|
||||||
|
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 () => {
|
const readData = async () => {
|
||||||
try {
|
const results = await Promise.all(priceReaders);
|
||||||
const priceData = await ethFeed.latestRoundData({ blockTag });
|
const priceMap: Record<BlockTag, BigNumber> = {};
|
||||||
setLatestPriceData(BigNumber.from(priceData.answer));
|
for (let i = 0; i < blockTags.length; i++) {
|
||||||
} catch (err) {
|
const blockTag = blockTags[i];
|
||||||
console.error(err);
|
const result = results[i];
|
||||||
|
if (blockTag === undefined || result === undefined) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
priceMap[blockTag] = result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setLatestPriceData(priceMap);
|
||||||
};
|
};
|
||||||
readData();
|
readData();
|
||||||
}, [ethFeed, blockTag]);
|
}, [ethFeed, blockTags]);
|
||||||
|
|
||||||
return latestPriceData;
|
return latestPriceData;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue