import React, { useEffect, useState, useMemo, useContext } from "react"; import { useParams, NavLink } from "react-router-dom"; import { ethers, BigNumber } from "ethers"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faChevronLeft, faChevronRight, } from "@fortawesome/free-solid-svg-icons"; import StandardFrame from "./StandardFrame"; import StandardSubtitle from "./StandardSubtitle"; import ContentFrame from "./ContentFrame"; import NavButton from "./components/NavButton"; import Timestamp from "./components/Timestamp"; import GasValue from "./components/GasValue"; import BlockLink from "./components/BlockLink"; import DecoratedAddressLink from "./components/DecoratedAddressLink"; import TransactionValue from "./components/TransactionValue"; import HexValue from "./components/HexValue"; import { RuntimeContext } from "./useRuntime"; import { useLatestBlockNumber } from "./useLatestBlock"; type BlockParams = { blockNumberOrHash: string; }; interface ExtendedBlock extends ethers.providers.Block { blockReward: BigNumber; unclesReward: BigNumber; feeReward: BigNumber; size: number; sha3Uncles: string; stateRoot: string; totalDifficulty: BigNumber; } const Block: React.FC = () => { const { provider } = useContext(RuntimeContext); const params = useParams(); const [block, setBlock] = useState(); useEffect(() => { if (!provider) { return; } const readBlock = async () => { let blockPromise: Promise; if (ethers.utils.isHexString(params.blockNumberOrHash, 32)) { blockPromise = provider.send("eth_getBlockByHash", [ params.blockNumberOrHash, false, ]); } else { blockPromise = provider.send("eth_getBlockByNumber", [ params.blockNumberOrHash, false, ]); } const [_rawBlock, _rawIssuance, _rawReceipts] = await Promise.all([ blockPromise, provider.send("erigon_issuance", [params.blockNumberOrHash]), provider.send("eth_getBlockReceipts", [params.blockNumberOrHash]), ]); const receipts = (_rawReceipts as any[]).map((r) => provider.formatter.receipt(r) ); const fees = receipts.reduce( (acc, r) => acc.add(r.effectiveGasPrice.mul(r.gasUsed)), BigNumber.from(0) ); const _block = provider.formatter.block(_rawBlock); const extBlock: ExtendedBlock = { blockReward: provider.formatter.bigNumber(_rawIssuance.blockReward), unclesReward: provider.formatter.bigNumber(_rawIssuance.uncleReward), feeReward: fees, size: provider.formatter.number(_rawBlock.size), sha3Uncles: _rawBlock.sha3Uncles, stateRoot: _rawBlock.stateRoot, totalDifficulty: provider.formatter.bigNumber( _rawBlock.totalDifficulty ), ..._block, }; setBlock(extBlock); }; readBlock(); }, [provider, params.blockNumberOrHash]); useEffect(() => { if (block) { document.title = `Block #${block.number} | Otterscan`; } }, [block]); const extraStr = useMemo(() => { try { return block && ethers.utils.toUtf8String(block.extraData); } catch (err) { console.error("Error while converting block extra data to string"); console.error(err); } }, [block]); const latestBlockNumber = useLatestBlockNumber(provider); return (
Block #{params.blockNumberOrHash} {block && (
= latestBlockNumber } > = latestBlockNumber } >
)}
{block && ( {ethers.utils.commify(block.number)} {block.transactions.length} transactions {" "} in this block {!block.feeReward.isZero() && ( <> {" "} ( +{" "} ) )} {ethers.utils.commify(block.difficulty)} {ethers.utils.commify(block.totalDifficulty.toString())} {ethers.utils.commify(block.size)} bytes {extraStr} (Hex:{" "} {block.extraData}) N/A {block.nonce} )}
); }; type InfoRowProps = { title: string; }; const InfoRow: React.FC = ({ title, children }) => (
{title}:
{children}
); export default React.memo(Block);