Skip to content

Commit 9a4dd76

Browse files
lucac-zilliqarrw-zilliqa
authored andcommitted
Add ability to view DS Blocks (#13)
* 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
1 parent 9fb41d9 commit 9a4dd76

31 files changed

Lines changed: 670 additions & 55 deletions

src/App.tsx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@ import { RuntimeContext, useRuntime } from "./useRuntime";
1010
import WarningHeader from "./WarningHeader";
1111

1212
const Block = lazy(() => import("./execution/Block"));
13-
const BlockList = lazy(() => import("./execution/BlockList"));
1413
const BlockTransactions = lazy(() => import("./execution/BlockTransactions"));
1514
const BlockTransactionByIndex = lazy(
1615
() => import("./execution/block/BlockTransactionByIndex"),
1716
);
17+
const DSBlock = lazy(() => import("./execution/DSBlock"));
18+
const BlockList = lazy(() => import("./execution/BlockList"));
19+
const DSBlockList = lazy(() => import("./execution/DSBlockList"));
1820
const Address = lazy(() => import("./execution/Address"));
1921
const Transaction = lazy(() => import("./execution/Transaction"));
2022
const AllContracts = lazy(() => import("./token/AllContracts"));
@@ -61,12 +63,19 @@ const App = () => {
6163
path="block/:blockNumberOrHash"
6264
element={<Block />}
6365
/>
66+
<Route
67+
path="block/:blockNumber/txs"
68+
element={<BlockTransactions />}
69+
/>
70+
<Route
71+
path="dsblock/:dsBlockNumberOrHash"
72+
element={<DSBlock />}
73+
/>
6474
<Route
6575
path="blocklist" element={ <BlockList/>}
6676
/>
6777
<Route
68-
path="block/:blockNumber/txs"
69-
element={<BlockTransactions />}
78+
path="dsblocklist" element={ <DSBlockList/>}
7079
/>
7180
<Route
7281
path="block/:blockNumberOrHash/tx/:txIndex"

src/Header.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,13 @@ const Header: FC<HeaderProps> = ({sourcifyPresent}) => {
6060
className="w-full rounded-l border-b border-l border-t px-2 py-1 text-sm focus:outline-none"
6161
type="text"
6262
size={60}
63-
placeholder={`Type "/" to search by address / txn hash / block number${
63+
placeholder={`Type "/" to search by address / txn hash / # ds block number${
6464
provider?._network.getPlugin(
6565
"org.ethers.plugins.network.Ens",
6666
) !== null
6767
? " / ENS name"
6868
: ""
69+
size={80}
6970
}`}
7071
onChange={handleChange}
7172
ref={searchRef}

src/Home.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ import { commify } from "./utils/utils";
1313
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
1414
import { faBurn } from "@fortawesome/free-solid-svg-icons";
1515
import Header from "./Header";
16-
import RecentBlocks from "./execution/block/recentBlocks";
16+
import RecentBlocks from "./execution/block/RecentBlocks";
17+
import RecentDSBlocks from "./execution/block/RecentDSBlocks";
1718

1819

1920
const Home: FC = () => {
@@ -29,8 +30,15 @@ const Home: FC = () => {
2930
return (
3031
<>
3132
<Header sourcifyPresent= {false} />
32-
<RecentBlocks />
33-
<div className="flex grow flex-col items-center pb-5">
33+
< <div className="grid grid-cols-5 gap-x-1 mx-1">
34+
<span className="col-span-2">
35+
<RecentDSBlocks />
36+
</span>
37+
<span className="col-span-3">
38+
<RecentBlocks />
39+
</span>
40+
</div>
41+
<div className="flex grow flex-col items-center pb-5">
3442
{isScanning && <CameraScanner turnOffScan={() => setScanning(false)} />}
3543
<div className="mb-10 mt-5 flex max-h-64 grow items-end">
3644
<Logo />

src/components/BlockNotFound.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ type BlockNotFoundProps = {
77

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

src/components/DSBlockLink.tsx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { FC, memo } from "react";
2+
import { NavLink } from "react-router-dom";
3+
import { BlockTag } from "@ethersproject/abstract-provider";
4+
import { commify } from "@ethersproject/units";
5+
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
6+
import { faCube } from "@fortawesome/free-solid-svg-icons";
7+
import { dsBlockURL } from "../url";
8+
9+
type DSBlockLinkProps = {
10+
blockTag: BlockTag;
11+
};
12+
13+
const DSBlockLink: FC<DSBlockLinkProps> = ({ blockTag }) => {
14+
const isNum = typeof blockTag === "number";
15+
let text = blockTag;
16+
if (isNum) {
17+
text = commify(blockTag);
18+
}
19+
20+
return (
21+
<NavLink
22+
className={`flex-inline items-baseline space-x-1 text-link-blue hover:text-link-blue-hover ${
23+
isNum ? "font-blocknum" : "font-hash"
24+
}`}
25+
to={dsBlockURL(blockTag)}
26+
>
27+
<span className="text-orange-500">
28+
<FontAwesomeIcon className="self-center" icon={faCube} size="1x" />
29+
</span>
30+
<span>{text}</span>
31+
</NavLink>
32+
);
33+
};
34+
35+
export default memo(DSBlockLink);

src/components/DSBlockNotFound.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import React from "react";
2+
import ContentFrame from "./ContentFrame";
3+
4+
type DSBlockNotFoundProps = {
5+
blockNumberOrHash: string;
6+
};
7+
8+
const DSBlockNotFound: React.FC<DSBlockNotFoundProps> = ({ blockNumberOrHash }) => (
9+
<ContentFrame>
10+
<div className="py-4 text-sm">DS Block "{blockNumberOrHash}" not found.</div>
11+
</ContentFrame>
12+
);
13+
14+
export default React.memo(DSBlockNotFound);

src/execution/BlockList.tsx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import StandardFrame from "../components/StandardFrame";
44
import { PAGE_SIZE } from "../params";
55
import { RuntimeContext } from "../useRuntime";
66
import StandardSubtitle from "../components/StandardSubtitle";
7-
import { useLatestBlockHeader } from "../useLatestBlock";
7+
import { useLatestBlockNumber } from "../useLatestBlock";
88
import { useRecentBlocks } from "../useErigonHooks";
99
import BlockItem from "../search/BlockItem";
10-
import PendingBlockResults from "../search/PendingBlockResults";
10+
import { PendingBlockResults } from "../search/PendingResults";
1111
import StandardSelectionBoundary from "../selection/StandardSelectionBoundary";
1212
import { useFeeToggler } from "../search/useFeeToggler";
1313
import ContentFrame from "../components/ContentFrame";
@@ -18,10 +18,8 @@ import SearchResultNavBar from "../search/SearchResultNavBar";
1818
const BlockList: React.FC = () => {
1919
const { provider } = useContext(RuntimeContext);
2020

21-
const latestBlock = useLatestBlockHeader(provider);
22-
21+
const latestBlockNum = useLatestBlockNumber(provider);
2322
const [feeDisplay, feeDisplayToggler] = useFeeToggler();
24-
const latestBlockNum = latestBlock?.number;
2523

2624

2725
const [searchParams] = useSearchParams();
@@ -43,7 +41,7 @@ const BlockList: React.FC = () => {
4341
return (
4442
<StandardFrame>
4543
<StandardSubtitle>
46-
<div className="flex items-baseline space-x-1">Block List</div>
44+
<div className="flex items-baseline space-x-1">Tx Block List</div>
4745
</StandardSubtitle>
4846
<ContentFrame isLoading={isLoading}>
4947
<SearchResultNavBar

src/execution/DSBlock.tsx

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import { useMemo, useContext, FC } from "react";
2+
import { useParams } from "react-router-dom";
3+
import { commify } from "@ethersproject/units";
4+
import StandardFrame from "../components/StandardFrame";
5+
import StandardSubtitle from "../components/StandardSubtitle";
6+
import NavBlock from "../components/NavBlock";
7+
import ContentFrame from "../components/ContentFrame";
8+
import BlockNotFound from "../components/BlockNotFound";
9+
import InfoRow from "../components/InfoRow";
10+
import Timestamp from "../components/Timestamp";
11+
import BlockLink from "../components/BlockLink";
12+
import DecoratedAddressLink from "./components/DecoratedAddressLink";
13+
import { RuntimeContext } from "../useRuntime";
14+
import { useLatestBlockChainInfo } from "../useLatestBlock";
15+
import { dsBlockURL } from "../url";
16+
import { useBlockPageTitle } from "../useTitle";
17+
import { useDSBlockData } from "../useZilliqaHooks";
18+
import { pubKeyToAddr, zilliqaToOtterscanTimestamp } from "../utils/utils";
19+
20+
// TODO: Figure out what we want to do with the previous Hash field
21+
const DSBlock: FC = () => {
22+
const { zilliqa } = useContext(RuntimeContext);
23+
const { dsBlockNumberOrHash } = useParams();
24+
if (dsBlockNumberOrHash === undefined) {
25+
throw new Error("dsBlockNumberOrHash couldn't be undefined here");
26+
}
27+
28+
const { data: dsBlock, isLoading } = useDSBlockData(zilliqa, dsBlockNumberOrHash);
29+
useBlockPageTitle(parseInt(dsBlockNumberOrHash));
30+
31+
const latestBlockChainInfo = useLatestBlockChainInfo(zilliqa);
32+
const latestDSBlockNum = latestBlockChainInfo?.CurrentDSEpoch;
33+
34+
return (
35+
<StandardFrame>
36+
<StandardSubtitle>
37+
<div className="flex items-baseline space-x-1">
38+
<span>DS Block</span>
39+
<span className="text-base text-gray-500">#{dsBlockNumberOrHash}</span>
40+
{dsBlock && (
41+
<NavBlock
42+
entityNum={parseInt(dsBlock.header.BlockNum)}
43+
latestEntityNum={latestDSBlockNum !== undefined ? parseInt(latestDSBlockNum) : undefined}
44+
urlBuilder={dsBlockURL}
45+
/>
46+
)}
47+
</div>
48+
</StandardSubtitle>
49+
{dsBlock === null && (
50+
<BlockNotFound blockNumberOrHash={dsBlockNumberOrHash} />
51+
)}
52+
{dsBlock === undefined && (
53+
<ContentFrame>
54+
<InfoRow title="Block Height">Loading DS Block data...</InfoRow>
55+
</ContentFrame>
56+
)}
57+
{dsBlock && (
58+
<ContentFrame isLoading={isLoading}>
59+
<InfoRow title="Block Height">
60+
<span className="font-bold">{commify(dsBlock.header.BlockNum)}</span>
61+
</InfoRow>
62+
<InfoRow title="Timestamp">
63+
<Timestamp value={zilliqaToOtterscanTimestamp(dsBlock.header.Timestamp)} />
64+
</InfoRow>
65+
<InfoRow title="DS Leader">
66+
<DecoratedAddressLink address={pubKeyToAddr(dsBlock.header.LeaderPubKey)} miner />
67+
</InfoRow>
68+
<InfoRow title="Gas Used/Limit">
69+
{commify(dsBlock.header.GasPrice)}
70+
</InfoRow>
71+
<InfoRow title="Difficulty">
72+
{commify(dsBlock.header.Difficulty.toString())}
73+
</InfoRow>
74+
<InfoRow title="Total Difficulty">
75+
{commify(dsBlock.header.DifficultyDS.toString())}
76+
</InfoRow>
77+
<InfoRow title="Previous Hash">
78+
<BlockLink blockTag={dsBlock.header.PrevHash} />
79+
</InfoRow>
80+
</ContentFrame>
81+
)}
82+
</StandardFrame>
83+
);
84+
};
85+
86+
export default DSBlock;

src/execution/DSBlockList.tsx

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import React, { useContext } from "react";
2+
import { useSearchParams } from "react-router-dom";
3+
import StandardFrame from "../components/StandardFrame";
4+
import { PAGE_SIZE } from "../params";
5+
import { RuntimeContext } from "../useRuntime";
6+
import StandardSubtitle from "../components/StandardSubtitle";
7+
import { useLatestBlockChainInfo } from "../useLatestBlock";
8+
import { PendingRecentDSBlockResults } from "../search/PendingResults";
9+
import StandardSelectionBoundary from "../selection/StandardSelectionBoundary";
10+
import ContentFrame from "../components/ContentFrame";
11+
import { totalBlocksFormatter } from "../search/messages";
12+
import SearchResultNavBar from "../search/SearchResultNavBar";
13+
import { useDSBlocksData } from "../useZilliqaHooks";
14+
import RecentDSBlockItem from "../search/RecentDSBlockItem";
15+
import DSBlockResultHeader from "../search/DSBlockResultHeader";
16+
import DSBlockItem from "../search/DSBlockItem";
17+
18+
const DSBlockList: React.FC = () => {
19+
const { zilliqa } = useContext(RuntimeContext);
20+
21+
const latestBlockChainInfo = useLatestBlockChainInfo(zilliqa);
22+
const latestBlockNum = latestBlockChainInfo?.CurrentDSEpoch;
23+
const latestBlockNumInt = latestBlockNum !== undefined ? parseInt(latestBlockNum, 10) : undefined;
24+
25+
const [searchParams] = useSearchParams();
26+
let pageNumber = 1;
27+
const p = searchParams.get("p");
28+
if (p) {
29+
try {
30+
pageNumber = parseInt(p);
31+
} catch (err) {}
32+
}
33+
34+
const { data, isLoading } = useDSBlocksData(
35+
zilliqa,
36+
latestBlockNumInt,
37+
pageNumber - 1,
38+
PAGE_SIZE
39+
);
40+
41+
return (
42+
<StandardFrame>
43+
<StandardSubtitle>
44+
<div className="flex items-baseline space-x-1">DS Block List</div>
45+
</StandardSubtitle>
46+
<ContentFrame isLoading={isLoading}>
47+
<SearchResultNavBar
48+
pageNumber={pageNumber}
49+
pageSize={PAGE_SIZE}
50+
total={latestBlockNumInt}
51+
totalFormatter={totalBlocksFormatter}
52+
/>
53+
<DSBlockResultHeader/>
54+
{data ? (
55+
<StandardSelectionBoundary>
56+
{data.map((block) => (
57+
block ? <DSBlockItem key={block.header.BlockNum} block={block} /> : <></>
58+
))}
59+
</StandardSelectionBoundary>
60+
) : (
61+
<PendingRecentDSBlockResults />
62+
)}
63+
</ContentFrame>
64+
</StandardFrame>
65+
);
66+
};
67+
68+
export default DSBlockList;

src/execution/address/AddressTransactionResults.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import TransactionLink from "../../components/TransactionLink";
88
import { useProxyAttributes } from "../../ots2/usePrototypeTransferHooks";
99
import PendingResults from "../../search/PendingResults";
1010
import ResultHeader from "../../search/ResultHeader";
11-
import PendingTransactionResults from "../../search/PendingTransactionResults";
11+
import { PendingTransactionResults } from "../../search/PendingResults";
1212
import TransactionResultHeader from "../../search/TransactionResultHeader";
1313
import { SearchController } from "../../search/search";
1414
import TransactionItem from "../../search/TransactionItem";

0 commit comments

Comments
 (0)