Skip to content

Commit

Permalink
Add ability to view DS Blocks (#13)
Browse files Browse the repository at this point in the history
* Changed Runtime Context to contain Zilliqa object

* Reformatted code so that all Pending components are together

* Added basic components for DS Blocks

* Implemented polling for most recent DS Block

* Added function to convert to Otterscan timestamp format

* Added detailed DS Block page

* Added ability to search for DS Blocks using search bar

* Added DS Block List page
  • Loading branch information
lucac-zilliqa authored and rrw-zilliqa committed Jan 30, 2024
1 parent 6969191 commit e587d6b
Show file tree
Hide file tree
Showing 31 changed files with 670 additions and 55 deletions.
15 changes: 12 additions & 3 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ import { RuntimeContext, useRuntime } from "./useRuntime";
import WarningHeader from "./WarningHeader";

const Block = lazy(() => import("./execution/Block"));
const BlockList = lazy(() => import("./execution/BlockList"));
const BlockTransactions = lazy(() => import("./execution/BlockTransactions"));
const BlockTransactionByIndex = lazy(
() => import("./execution/block/BlockTransactionByIndex"),
);
const DSBlock = lazy(() => import("./execution/DSBlock"));
const BlockList = lazy(() => import("./execution/BlockList"));
const DSBlockList = lazy(() => import("./execution/DSBlockList"));
const Address = lazy(() => import("./execution/Address"));
const Transaction = lazy(() => import("./execution/Transaction"));
const AllContracts = lazy(() => import("./token/AllContracts"));
Expand Down Expand Up @@ -61,12 +63,19 @@ const App = () => {
path="block/:blockNumberOrHash"
element={<Block />}
/>
<Route
path="block/:blockNumber/txs"
element={<BlockTransactions />}
/>
<Route
path="dsblock/:dsBlockNumberOrHash"
element={<DSBlock />}
/>
<Route
path="blocklist" element={ <BlockList/>}
/>
<Route
path="block/:blockNumber/txs"
element={<BlockTransactions />}
path="dsblocklist" element={ <DSBlockList/>}
/>
<Route
path="block/:blockNumberOrHash/tx/:txIndex"
Expand Down
3 changes: 2 additions & 1 deletion src/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,13 @@ const Header: FC<HeaderProps> = ({sourcifyPresent}) => {
className="w-full rounded-l border-b border-l border-t px-2 py-1 text-sm focus:outline-none"
type="text"
size={60}
placeholder={`Type "/" to search by address / txn hash / block number${
placeholder={`Type "/" to search by address / txn hash / # ds block number${
provider?._network.getPlugin(
"org.ethers.plugins.network.Ens",
) !== null
? " / ENS name"
: ""
size={80}
}`}
onChange={handleChange}
ref={searchRef}
Expand Down
14 changes: 11 additions & 3 deletions src/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import { commify } from "./utils/utils";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faBurn } from "@fortawesome/free-solid-svg-icons";
import Header from "./Header";
import RecentBlocks from "./execution/block/recentBlocks";
import RecentBlocks from "./execution/block/RecentBlocks";
import RecentDSBlocks from "./execution/block/RecentDSBlocks";


const Home: FC = () => {
Expand All @@ -29,8 +30,15 @@ const Home: FC = () => {
return (
<>
<Header sourcifyPresent= {false} />
<RecentBlocks />
<div className="flex grow flex-col items-center pb-5">
< <div className="grid grid-cols-5 gap-x-1 mx-1">
<span className="col-span-2">
<RecentDSBlocks />
</span>
<span className="col-span-3">
<RecentBlocks />
</span>
</div>
<div className="flex grow flex-col items-center pb-5">
{isScanning && <CameraScanner turnOffScan={() => setScanning(false)} />}
<div className="mb-10 mt-5 flex max-h-64 grow items-end">
<Logo />
Expand Down
2 changes: 1 addition & 1 deletion src/components/BlockNotFound.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ type BlockNotFoundProps = {

const BlockNotFound: React.FC<BlockNotFoundProps> = ({ blockNumberOrHash }) => (
<ContentFrame>
<div className="py-4 text-sm">Block "{blockNumberOrHash}" not found.</div>
<div className="py-4 text-sm">Tx Block "{blockNumberOrHash}" not found.</div>
</ContentFrame>
);

Expand Down
35 changes: 35 additions & 0 deletions src/components/DSBlockLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { FC, memo } from "react";
import { NavLink } from "react-router-dom";
import { BlockTag } from "@ethersproject/abstract-provider";
import { commify } from "@ethersproject/units";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCube } from "@fortawesome/free-solid-svg-icons";
import { dsBlockURL } from "../url";

type DSBlockLinkProps = {
blockTag: BlockTag;
};

const DSBlockLink: FC<DSBlockLinkProps> = ({ blockTag }) => {
const isNum = typeof blockTag === "number";
let text = blockTag;
if (isNum) {
text = commify(blockTag);
}

return (
<NavLink
className={`flex-inline items-baseline space-x-1 text-link-blue hover:text-link-blue-hover ${
isNum ? "font-blocknum" : "font-hash"
}`}
to={dsBlockURL(blockTag)}
>
<span className="text-orange-500">
<FontAwesomeIcon className="self-center" icon={faCube} size="1x" />
</span>
<span>{text}</span>
</NavLink>
);
};

export default memo(DSBlockLink);
14 changes: 14 additions & 0 deletions src/components/DSBlockNotFound.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from "react";
import ContentFrame from "./ContentFrame";

type DSBlockNotFoundProps = {
blockNumberOrHash: string;
};

const DSBlockNotFound: React.FC<DSBlockNotFoundProps> = ({ blockNumberOrHash }) => (
<ContentFrame>
<div className="py-4 text-sm">DS Block "{blockNumberOrHash}" not found.</div>
</ContentFrame>
);

export default React.memo(DSBlockNotFound);
10 changes: 4 additions & 6 deletions src/execution/BlockList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import StandardFrame from "../components/StandardFrame";
import { PAGE_SIZE } from "../params";
import { RuntimeContext } from "../useRuntime";
import StandardSubtitle from "../components/StandardSubtitle";
import { useLatestBlockHeader } from "../useLatestBlock";
import { useLatestBlockNumber } from "../useLatestBlock";
import { useRecentBlocks } from "../useErigonHooks";
import BlockItem from "../search/BlockItem";
import PendingBlockResults from "../search/PendingBlockResults";
import { PendingBlockResults } from "../search/PendingResults";
import StandardSelectionBoundary from "../selection/StandardSelectionBoundary";
import { useFeeToggler } from "../search/useFeeToggler";
import ContentFrame from "../components/ContentFrame";
Expand All @@ -18,10 +18,8 @@ import SearchResultNavBar from "../search/SearchResultNavBar";
const BlockList: React.FC = () => {
const { provider } = useContext(RuntimeContext);

const latestBlock = useLatestBlockHeader(provider);

const latestBlockNum = useLatestBlockNumber(provider);
const [feeDisplay, feeDisplayToggler] = useFeeToggler();
const latestBlockNum = latestBlock?.number;


const [searchParams] = useSearchParams();
Expand All @@ -43,7 +41,7 @@ const BlockList: React.FC = () => {
return (
<StandardFrame>
<StandardSubtitle>
<div className="flex items-baseline space-x-1">Block List</div>
<div className="flex items-baseline space-x-1">Tx Block List</div>
</StandardSubtitle>
<ContentFrame isLoading={isLoading}>
<SearchResultNavBar
Expand Down
86 changes: 86 additions & 0 deletions src/execution/DSBlock.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { useMemo, useContext, FC } from "react";
import { useParams } from "react-router-dom";
import { commify } from "@ethersproject/units";
import StandardFrame from "../components/StandardFrame";
import StandardSubtitle from "../components/StandardSubtitle";
import NavBlock from "../components/NavBlock";
import ContentFrame from "../components/ContentFrame";
import BlockNotFound from "../components/BlockNotFound";
import InfoRow from "../components/InfoRow";
import Timestamp from "../components/Timestamp";
import BlockLink from "../components/BlockLink";
import DecoratedAddressLink from "./components/DecoratedAddressLink";
import { RuntimeContext } from "../useRuntime";
import { useLatestBlockChainInfo } from "../useLatestBlock";
import { dsBlockURL } from "../url";
import { useBlockPageTitle } from "../useTitle";
import { useDSBlockData } from "../useZilliqaHooks";
import { pubKeyToAddr, zilliqaToOtterscanTimestamp } from "../utils/utils";

// TODO: Figure out what we want to do with the previous Hash field
const DSBlock: FC = () => {
const { zilliqa } = useContext(RuntimeContext);
const { dsBlockNumberOrHash } = useParams();
if (dsBlockNumberOrHash === undefined) {
throw new Error("dsBlockNumberOrHash couldn't be undefined here");
}

const { data: dsBlock, isLoading } = useDSBlockData(zilliqa, dsBlockNumberOrHash);
useBlockPageTitle(parseInt(dsBlockNumberOrHash));

const latestBlockChainInfo = useLatestBlockChainInfo(zilliqa);
const latestDSBlockNum = latestBlockChainInfo?.CurrentDSEpoch;

return (
<StandardFrame>
<StandardSubtitle>
<div className="flex items-baseline space-x-1">
<span>DS Block</span>
<span className="text-base text-gray-500">#{dsBlockNumberOrHash}</span>
{dsBlock && (
<NavBlock
entityNum={parseInt(dsBlock.header.BlockNum)}
latestEntityNum={latestDSBlockNum !== undefined ? parseInt(latestDSBlockNum) : undefined}
urlBuilder={dsBlockURL}
/>
)}
</div>
</StandardSubtitle>
{dsBlock === null && (
<BlockNotFound blockNumberOrHash={dsBlockNumberOrHash} />
)}
{dsBlock === undefined && (
<ContentFrame>
<InfoRow title="Block Height">Loading DS Block data...</InfoRow>
</ContentFrame>
)}
{dsBlock && (
<ContentFrame isLoading={isLoading}>
<InfoRow title="Block Height">
<span className="font-bold">{commify(dsBlock.header.BlockNum)}</span>
</InfoRow>
<InfoRow title="Timestamp">
<Timestamp value={zilliqaToOtterscanTimestamp(dsBlock.header.Timestamp)} />
</InfoRow>
<InfoRow title="DS Leader">
<DecoratedAddressLink address={pubKeyToAddr(dsBlock.header.LeaderPubKey)} miner />
</InfoRow>
<InfoRow title="Gas Used/Limit">
{commify(dsBlock.header.GasPrice)}
</InfoRow>
<InfoRow title="Difficulty">
{commify(dsBlock.header.Difficulty.toString())}
</InfoRow>
<InfoRow title="Total Difficulty">
{commify(dsBlock.header.DifficultyDS.toString())}
</InfoRow>
<InfoRow title="Previous Hash">
<BlockLink blockTag={dsBlock.header.PrevHash} />
</InfoRow>
</ContentFrame>
)}
</StandardFrame>
);
};

export default DSBlock;
68 changes: 68 additions & 0 deletions src/execution/DSBlockList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React, { useContext } from "react";
import { useSearchParams } from "react-router-dom";
import StandardFrame from "../components/StandardFrame";
import { PAGE_SIZE } from "../params";
import { RuntimeContext } from "../useRuntime";
import StandardSubtitle from "../components/StandardSubtitle";
import { useLatestBlockChainInfo } from "../useLatestBlock";
import { PendingRecentDSBlockResults } from "../search/PendingResults";
import StandardSelectionBoundary from "../selection/StandardSelectionBoundary";
import ContentFrame from "../components/ContentFrame";
import { totalBlocksFormatter } from "../search/messages";
import SearchResultNavBar from "../search/SearchResultNavBar";
import { useDSBlocksData } from "../useZilliqaHooks";
import RecentDSBlockItem from "../search/RecentDSBlockItem";
import DSBlockResultHeader from "../search/DSBlockResultHeader";
import DSBlockItem from "../search/DSBlockItem";

const DSBlockList: React.FC = () => {
const { zilliqa } = useContext(RuntimeContext);

const latestBlockChainInfo = useLatestBlockChainInfo(zilliqa);
const latestBlockNum = latestBlockChainInfo?.CurrentDSEpoch;
const latestBlockNumInt = latestBlockNum !== undefined ? parseInt(latestBlockNum, 10) : undefined;

const [searchParams] = useSearchParams();
let pageNumber = 1;
const p = searchParams.get("p");
if (p) {
try {
pageNumber = parseInt(p);
} catch (err) {}
}

const { data, isLoading } = useDSBlocksData(
zilliqa,
latestBlockNumInt,
pageNumber - 1,
PAGE_SIZE
);

return (
<StandardFrame>
<StandardSubtitle>
<div className="flex items-baseline space-x-1">DS Block List</div>
</StandardSubtitle>
<ContentFrame isLoading={isLoading}>
<SearchResultNavBar
pageNumber={pageNumber}
pageSize={PAGE_SIZE}
total={latestBlockNumInt}
totalFormatter={totalBlocksFormatter}
/>
<DSBlockResultHeader/>
{data ? (
<StandardSelectionBoundary>
{data.map((block) => (
block ? <DSBlockItem key={block.header.BlockNum} block={block} /> : <></>
))}
</StandardSelectionBoundary>
) : (
<PendingRecentDSBlockResults />
)}
</ContentFrame>
</StandardFrame>
);
};

export default DSBlockList;
2 changes: 1 addition & 1 deletion src/execution/address/AddressTransactionResults.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import TransactionLink from "../../components/TransactionLink";
import { useProxyAttributes } from "../../ots2/usePrototypeTransferHooks";
import PendingResults from "../../search/PendingResults";
import ResultHeader from "../../search/ResultHeader";
import PendingTransactionResults from "../../search/PendingTransactionResults";
import { PendingTransactionResults } from "../../search/PendingResults";
import TransactionResultHeader from "../../search/TransactionResultHeader";
import { SearchController } from "../../search/search";
import TransactionItem from "../../search/TransactionItem";
Expand Down
2 changes: 1 addition & 1 deletion src/execution/block/BlockTransactionResults.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import ContentFrame from "../../components/ContentFrame";
import StandardSelectionBoundary from "../../selection/StandardSelectionBoundary";
import SearchResultNavBar from "../../search/SearchResultNavBar";
import TransactionResultHeader from "../../search/TransactionResultHeader";
import PendingTransactionResults from "../../search/PendingTransactionResults";
import { PendingTransactionResults } from "../../search/PendingResults";
import TransactionItem from "../../search/TransactionItem";
import { useFeeToggler } from "../../search/useFeeToggler";
import { totalTransactionsFormatter } from "../../search/messages";
Expand Down
9 changes: 4 additions & 5 deletions src/execution/block/RecentBlocks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,19 @@ import StandardSelectionBoundary from "../../selection/StandardSelectionBoundary
import { useFeeToggler } from "../../search/useFeeToggler";
import { RuntimeContext } from "../../useRuntime";
import { useRecentBlocks } from "../../useErigonHooks";
import { useLatestBlockHeader } from "../../useLatestBlock";
import { useLatestBlockNumber } from "../../useLatestBlock";
import { RECENT_SIZE } from "../../params";
import RecentBlockItem from "../../search/RecentBlockItem";
import RecentBlockResultHeader from "../../search/RecentBlockResultHeader";
import PendingBlockResults from "../../search/PendingBlockResults";
import { PendingRecentBlockResults } from "../../search/PendingResults";
import RecentNavBar from "../../search/RecentNavBar";


const RecentBlocks: FC = () => {
const { provider } = useContext(RuntimeContext);
const [feeDisplay, feeDisplayToggler] = useFeeToggler();

const latestBlock = useLatestBlockHeader(provider);
const latestBlockNum = latestBlock?.number;
const latestBlockNum = useLatestBlockNumber(provider);

// Uses hook to get the most recent blocks
const { data, isLoading } = useRecentBlocks(
Expand All @@ -42,7 +41,7 @@ const RecentBlocks: FC = () => {
))}
</StandardSelectionBoundary>
) : (
<PendingBlockResults />
<PendingRecentBlockResults />
)}
</ContentFrame>
);
Expand Down
Loading

0 comments on commit e587d6b

Please sign in to comment.