diff --git a/src/Block.tsx b/src/Block.tsx index 9f3a652..caa309b 100644 --- a/src/Block.tsx +++ b/src/Block.tsx @@ -17,11 +17,14 @@ import BlockLink from "./components/BlockLink"; import DecoratedAddressLink from "./components/DecoratedAddressLink"; import TransactionValue from "./components/TransactionValue"; import FormattedBalance from "./components/FormattedBalance"; +import ETH2USDValue from "./components/ETH2USDValue"; +import USDValue from "./components/USDValue"; import HexValue from "./components/HexValue"; import { RuntimeContext } from "./useRuntime"; import { useLatestBlockNumber } from "./useLatestBlock"; import { blockTxsURL } from "./url"; import { useBlockData } from "./useErigonHooks"; +import { useETHUSDOracle } from "./usePriceOracle"; type BlockParams = { blockNumberOrHash: string; @@ -53,6 +56,7 @@ const Block: React.FC = () => { block && block.gasUsed.mul(10000).div(block.gasLimit).toNumber() / 100; const latestBlockNumber = useLatestBlockNumber(provider); + const blockETHUSDPrice = useETHUSDOracle(provider, block?.number); return ( @@ -99,6 +103,17 @@ const Block: React.FC = () => { ) )} + {blockETHUSDPrice && ( + <> + {" "} + + + + + )} @@ -147,7 +162,9 @@ const Block: React.FC = () => { {extraStr} (Hex:{" "} {block.extraData}) - N/A + + + {commify(block.difficulty)} {commify(block.totalDifficulty.toString())} diff --git a/src/Transaction.tsx b/src/Transaction.tsx index cb5a4e1..94f998f 100644 --- a/src/Transaction.tsx +++ b/src/Transaction.tsx @@ -8,6 +8,7 @@ import Logs from "./transaction/Logs"; import { RuntimeContext } from "./useRuntime"; import { SelectionContext, useSelection } from "./useSelection"; import { useInternalOperations, useTxData } from "./useErigonHooks"; +import { useETHUSDOracle } from "./usePriceOracle"; type TransactionParams = { txhash: string; @@ -36,6 +37,8 @@ const Transaction: React.FC = () => { const selectionCtx = useSelection(); + const blockETHUSDPrice = useETHUSDOracle(provider, txData?.blockNumber); + return ( Transaction Details @@ -53,6 +56,7 @@ const Transaction: React.FC = () => { txData={txData} internalOps={internalOps} sendsEthToMiner={sendsEthToMiner} + ethUSDPrice={blockETHUSDPrice} /> diff --git a/src/components/ETH2USDValue.tsx b/src/components/ETH2USDValue.tsx new file mode 100644 index 0000000..ef131d8 --- /dev/null +++ b/src/components/ETH2USDValue.tsx @@ -0,0 +1,26 @@ +import React from "react"; +import { BigNumber, FixedNumber } from "@ethersproject/bignumber"; +import { commify } from "@ethersproject/units"; + +type ETH2USDValueProps = { + ethAmount: BigNumber; + eth2USDValue: BigNumber; +}; + +const ETH2USDValue: React.FC = ({ + ethAmount, + eth2USDValue, +}) => { + const value = ethAmount.mul(eth2USDValue).div(10 ** 8); + + return ( + + $ + + {commify(FixedNumber.fromValue(value, 18).round(2).toString())} + + + ); +}; + +export default React.memo(ETH2USDValue); diff --git a/src/components/USDValue.tsx b/src/components/USDValue.tsx new file mode 100644 index 0000000..e488d25 --- /dev/null +++ b/src/components/USDValue.tsx @@ -0,0 +1,29 @@ +import React from "react"; +import { BigNumber, FixedNumber } from "@ethersproject/bignumber"; +import { commify } from "@ethersproject/units"; + +const ETH_FEED_DECIMALS = 8; + +type USDValueProps = { + value: BigNumber | undefined; +}; + +const USDValue: React.FC = ({ value }) => ( + + {value ? ( + <> + $ + + {commify( + FixedNumber.fromValue(value, ETH_FEED_DECIMALS).round(2).toString() + )} + {" "} + / ETH + + ) : ( + "N/A" + )} + +); + +export default React.memo(USDValue); diff --git a/src/transaction/Details.tsx b/src/transaction/Details.tsx index 500d45e..1f3cd65 100644 --- a/src/transaction/Details.tsx +++ b/src/transaction/Details.tsx @@ -1,5 +1,5 @@ import React, { useMemo, useState } from "react"; -import { formatEther } from "@ethersproject/units"; +import { BigNumber } from "@ethersproject/bignumber"; import { toUtf8String } from "@ethersproject/strings"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faCheckCircle } from "@fortawesome/free-solid-svg-icons/faCheckCircle"; @@ -19,7 +19,9 @@ import MethodName from "../components/MethodName"; import TransactionType from "../components/TransactionType"; import RewardSplit from "./RewardSplit"; import GasValue from "../components/GasValue"; +import USDValue from "../components/USDValue"; import FormattedBalance from "../components/FormattedBalance"; +import ETH2USDValue from "../components/ETH2USDValue"; import TokenTransferItem from "../TokenTransferItem"; import { TransactionData, InternalOperation } from "../types"; import PercentageBar from "../components/PercentageBar"; @@ -31,12 +33,14 @@ type DetailsProps = { txData: TransactionData; internalOps?: InternalOperation[]; sendsEthToMiner: boolean; + ethUSDPrice: BigNumber | undefined; }; const Details: React.FC = ({ txData, internalOps, sendsEthToMiner, + ethUSDPrice, }) => { const hasEIP1559 = txData.blockBaseFeePerGas !== undefined && @@ -170,9 +174,12 @@ const Details: React.FC = ({ )} - - {formatEther(txData.value)} Ether - + Ether{" "} + {!txData.value.isZero() && ethUSDPrice && ( + + + + )} = ({
- Ether + Ether{" "} + {ethUSDPrice && ( + + + + )}
{hasEIP1559 && }
- N/A + + +
diff --git a/src/usePriceOracle.ts b/src/usePriceOracle.ts new file mode 100644 index 0000000..e318a47 --- /dev/null +++ b/src/usePriceOracle.ts @@ -0,0 +1,42 @@ +import { useEffect, useMemo, useState } from "react"; +import { JsonRpcProvider, BlockTag } from "@ethersproject/providers"; +import { Contract } from "@ethersproject/contracts"; +import { BigNumber } from "@ethersproject/bignumber"; +import AggregatorV3Interface from "@chainlink/contracts/abi/v0.8/AggregatorV3Interface.json"; + +export const useETHUSDOracle = ( + provider: JsonRpcProvider | undefined, + blockTag: BlockTag | undefined +) => { + const ethFeed = useMemo(() => { + if (!provider || provider.network.chainId !== 1) { + return undefined; + } + + try { + return new Contract("eth-usd.data.eth", AggregatorV3Interface, provider); + } catch (err) { + console.error(err); + return undefined; + } + }, [provider]); + + const [latestPriceData, setLatestPriceData] = useState(); + useEffect(() => { + if (!ethFeed || !blockTag) { + return; + } + + const readData = async () => { + try { + const priceData = await ethFeed.latestRoundData({ blockTag }); + setLatestPriceData(BigNumber.from(priceData.answer)); + } catch (err) { + console.error(err); + } + }; + readData(); + }, [ethFeed, blockTag]); + + return latestPriceData; +};