otterscan/src/Block.tsx

234 lines
7.6 KiB
TypeScript
Raw Normal View History

2021-07-08 19:02:42 +00:00
import React, { useEffect, useState, useMemo, useContext } from "react";
2021-07-01 18:21:40 +00:00
import { useParams, NavLink } from "react-router-dom";
import { ethers, BigNumber } from "ethers";
2021-07-04 01:33:34 +00:00
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
faChevronLeft,
faChevronRight,
} from "@fortawesome/free-solid-svg-icons";
2021-07-01 18:21:40 +00:00
import StandardFrame from "./StandardFrame";
import StandardSubtitle from "./StandardSubtitle";
import ContentFrame from "./ContentFrame";
2021-07-04 01:33:34 +00:00
import NavButton from "./components/NavButton";
2021-07-01 18:21:40 +00:00
import Timestamp from "./components/Timestamp";
import GasValue from "./components/GasValue";
import BlockLink from "./components/BlockLink";
import DecoratedAddressLink from "./components/DecoratedAddressLink";
2021-07-03 21:51:57 +00:00
import TransactionValue from "./components/TransactionValue";
import HexValue from "./components/HexValue";
2021-07-09 05:07:20 +00:00
import { RuntimeContext } from "./useRuntime";
import { useLatestBlockNumber } from "./useLatestBlock";
2021-07-01 18:21:40 +00:00
type BlockParams = {
blockNumberOrHash: string;
};
interface ExtendedBlock extends ethers.providers.Block {
2021-07-03 21:51:57 +00:00
blockReward: BigNumber;
unclesReward: BigNumber;
feeReward: BigNumber;
2021-07-01 18:21:40 +00:00
size: number;
sha3Uncles: string;
stateRoot: string;
totalDifficulty: BigNumber;
}
const Block: React.FC = () => {
2021-07-09 05:07:20 +00:00
const { provider } = useContext(RuntimeContext);
2021-07-01 18:21:40 +00:00
const params = useParams<BlockParams>();
const [block, setBlock] = useState<ExtendedBlock>();
useEffect(() => {
2021-07-08 19:02:42 +00:00
if (!provider) {
return;
}
2021-07-01 18:21:40 +00:00
const readBlock = async () => {
2021-07-03 21:51:57 +00:00
let blockPromise: Promise<any>;
2021-07-01 18:21:40 +00:00
if (ethers.utils.isHexString(params.blockNumberOrHash, 32)) {
2021-07-03 21:51:57 +00:00
blockPromise = provider.send("eth_getBlockByHash", [
2021-07-01 18:21:40 +00:00
params.blockNumberOrHash,
false,
]);
} else {
2021-07-03 21:51:57 +00:00
blockPromise = provider.send("eth_getBlockByNumber", [
2021-07-01 18:21:40 +00:00
params.blockNumberOrHash,
false,
]);
}
2021-07-03 22:14:03 +00:00
const [_rawBlock, _rawIssuance, _rawReceipts] = await Promise.all([
2021-07-03 21:51:57 +00:00
blockPromise,
provider.send("erigon_issuance", [params.blockNumberOrHash]),
2021-07-03 22:14:03 +00:00
provider.send("eth_getBlockReceipts", [params.blockNumberOrHash]),
2021-07-03 21:51:57 +00:00
]);
2021-07-03 22:14:03 +00:00
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)
);
2021-07-01 18:21:40 +00:00
const _block = provider.formatter.block(_rawBlock);
const extBlock: ExtendedBlock = {
2021-07-03 21:51:57 +00:00
blockReward: provider.formatter.bigNumber(_rawIssuance.blockReward),
unclesReward: provider.formatter.bigNumber(_rawIssuance.uncleReward),
2021-07-03 22:14:03 +00:00
feeReward: fees,
2021-07-01 18:21:40 +00:00
size: provider.formatter.number(_rawBlock.size),
sha3Uncles: _rawBlock.sha3Uncles,
stateRoot: _rawBlock.stateRoot,
totalDifficulty: provider.formatter.bigNumber(
_rawBlock.totalDifficulty
),
..._block,
};
setBlock(extBlock);
};
readBlock();
2021-07-08 19:02:42 +00:00
}, [provider, params.blockNumberOrHash]);
2021-07-01 18:21:40 +00:00
useEffect(() => {
if (block) {
document.title = `Block #${block.number} | Otterscan`;
}
}, [block]);
const extraStr = useMemo(() => {
try {
return block && ethers.utils.toUtf8String(block.extraData);
} catch (err) {
2021-07-03 22:15:22 +00:00
console.error("Error while converting block extra data to string");
2021-07-01 18:21:40 +00:00
console.error(err);
}
}, [block]);
2021-07-08 19:02:42 +00:00
const latestBlockNumber = useLatestBlockNumber(provider);
2021-07-01 18:21:40 +00:00
return (
<StandardFrame>
<StandardSubtitle>
2021-07-23 22:20:49 +00:00
<div className="flex space-x-1 items-baseline">
<span>Block</span>
<span className="text-base text-gray-500">
#{params.blockNumberOrHash}
</span>
{block && (
<div className="pl-2 self-center flex space-x-1">
2021-07-04 01:33:34 +00:00
<NavButton
blockNum={block.number - 1}
disabled={block.number === 0}
>
<FontAwesomeIcon icon={faChevronLeft} />
</NavButton>
<NavButton
blockNum={block.number + 1}
disabled={
latestBlockNumber === undefined ||
block.number >= latestBlockNumber
}
2021-07-04 01:33:34 +00:00
>
<FontAwesomeIcon icon={faChevronRight} />
</NavButton>
<NavButton
blockNum={latestBlockNumber!}
disabled={
latestBlockNumber === undefined ||
block.number >= latestBlockNumber
}
>
<FontAwesomeIcon icon={faChevronRight} />
<FontAwesomeIcon icon={faChevronRight} />
</NavButton>
2021-07-04 01:33:34 +00:00
</div>
2021-07-23 22:20:49 +00:00
)}
</div>
</StandardSubtitle>
{block && (
<ContentFrame>
<InfoRow title="Block Height">
<span className="font-bold">
{ethers.utils.commify(block.number)}
</span>
2021-07-01 18:21:40 +00:00
</InfoRow>
<InfoRow title="Timestamp">
<Timestamp value={block.timestamp} />
</InfoRow>
<InfoRow title="Transactions">
<NavLink
className="bg-link-blue bg-opacity-10 text-link-blue hover:bg-opacity-100 hover:text-white rounded-lg px-2 py-1 text-xs"
to={`/block/${block.number}/txs`}
>
{block.transactions.length} transactions
</NavLink>{" "}
in this block
</InfoRow>
<InfoRow title="Mined by">
<DecoratedAddressLink address={block.miner} miner />
2021-07-01 18:21:40 +00:00
</InfoRow>
2021-07-03 21:51:57 +00:00
<InfoRow title="Block Reward">
2021-07-03 22:14:03 +00:00
<TransactionValue value={block.blockReward.add(block.feeReward)} />
{!block.feeReward.isZero() && (
<>
{" "}
(<TransactionValue value={block.blockReward} hideUnit /> +{" "}
<TransactionValue value={block.feeReward} hideUnit />)
</>
)}
2021-07-03 21:51:57 +00:00
</InfoRow>
<InfoRow title="Uncles Reward">
<TransactionValue value={block.unclesReward} />
</InfoRow>
2021-07-01 18:21:40 +00:00
<InfoRow title="Difficult">
{ethers.utils.commify(block.difficulty)}
</InfoRow>
<InfoRow title="Total Difficult">
{ethers.utils.commify(block.totalDifficulty.toString())}
</InfoRow>
<InfoRow title="Size">
{ethers.utils.commify(block.size)} bytes
</InfoRow>
<InfoRow title="Gas Used">
<GasValue value={block.gasUsed} />
</InfoRow>
<InfoRow title="Gas Limit">
<GasValue value={block.gasLimit} />
</InfoRow>
<InfoRow title="Extra Data">
{extraStr} (Hex:{" "}
<span className="font-data">{block.extraData}</span>)
</InfoRow>
<InfoRow title="Ether Price">N/A</InfoRow>
<InfoRow title="Hash">
2021-07-04 01:33:34 +00:00
<HexValue value={block.hash} />
2021-07-01 18:21:40 +00:00
</InfoRow>
<InfoRow title="Parent Hash">
<BlockLink blockTag={block.parentHash} />
</InfoRow>
<InfoRow title="Sha3Uncles">
<HexValue value={block.sha3Uncles} />
2021-07-01 18:21:40 +00:00
</InfoRow>
<InfoRow title="StateRoot">
<HexValue value={block.stateRoot} />
2021-07-01 18:21:40 +00:00
</InfoRow>
<InfoRow title="Nonce">
<span className="font-data">{block.nonce}</span>
</InfoRow>
</ContentFrame>
)}
</StandardFrame>
);
};
type InfoRowProps = {
title: string;
};
const InfoRow: React.FC<InfoRowProps> = ({ title, children }) => (
<div className="grid grid-cols-4 py-4 text-sm">
<div>{title}:</div>
<div className="col-span-3">{children}</div>
</div>
);
export default React.memo(Block);