Add beacon chain finalized state info

This commit is contained in:
Willian Mitsuda 2022-08-29 15:52:25 -03:00
parent 81b9213df1
commit 08d8cded31
No known key found for this signature in database
4 changed files with 85 additions and 0 deletions

View File

@ -1,4 +1,5 @@
{ {
"erigonURL": "http://localhost:8545", "erigonURL": "http://localhost:8545",
"beaconAPI": null,
"assetsURLPrefix": "http://localhost:3001" "assetsURLPrefix": "http://localhost:3001"
} }

View File

@ -10,6 +10,7 @@ import { RuntimeContext } from "./useRuntime";
import { useLatestBlockHeader } from "./useLatestBlock"; import { useLatestBlockHeader } from "./useLatestBlock";
import { blockURL } from "./url"; import { blockURL } from "./url";
import { useGenericSearch } from "./search/search"; import { useGenericSearch } from "./search/search";
import { useFinalizedSlot, useSlotTime } from "./useBeacon";
const CameraScanner = React.lazy(() => import("./search/CameraScanner")); const CameraScanner = React.lazy(() => import("./search/CameraScanner"));
@ -18,6 +19,8 @@ const Home: React.FC = () => {
const [searchRef, handleChange, handleSubmit] = useGenericSearch(); const [searchRef, handleChange, handleSubmit] = useGenericSearch();
const latestBlock = useLatestBlockHeader(provider); const latestBlock = useLatestBlockHeader(provider);
const beaconData = useFinalizedSlot();
const slotTime = useSlotTime(beaconData?.data.header.message.slot);
const [isScanning, setScanning] = useState<boolean>(false); const [isScanning, setScanning] = useState<boolean>(false);
document.title = "Home | Otterscan"; document.title = "Home | Otterscan";
@ -87,6 +90,20 @@ const Home: React.FC = () => {
<Timestamp value={latestBlock.timestamp} /> <Timestamp value={latestBlock.timestamp} />
</NavLink> </NavLink>
)} )}
{beaconData && (
<div className="flex flex-col items-center space-y-1 mt-5 text-sm text-gray-500">
<div>
Finalized slot: {commify(beaconData.data.header.message.slot)}
</div>
{slotTime && <Timestamp value={slotTime} />}
<div>
State root:{" "}
<span className="font-hash">
{beaconData.data.header.message.state_root}
</span>
</div>
</div>
)}
</div> </div>
</div> </div>
); );

66
src/useBeacon.ts Normal file
View File

@ -0,0 +1,66 @@
import { useContext } from "react";
import useSWR from "swr";
import useSWRImmutable from "swr/immutable";
import { RuntimeContext } from "./useRuntime";
// 12s
const SLOT_TIME = 12;
// TODO: remove duplication with other json fetchers
const jsonFetcher = async (url: string) => {
try {
const res = await fetch(url);
if (res.ok) {
return res.json();
}
return null;
} catch (err) {
console.warn(`error while getting beacon data: url=${url} err=${err}`);
return null;
}
};
export const useFinalizedSlot = () => {
const { config } = useContext(RuntimeContext);
// Each slot is 12s, so program SWR to revalidate at this interval
const { data, error } = useSWR(
config?.beaconAPI
? `${config?.beaconAPI}/eth/v1/beacon/headers/finalized`
: null,
jsonFetcher,
{
revalidateOnFocus: false,
refreshInterval: SLOT_TIME * 1000,
}
);
if (error) {
return undefined;
}
return data;
};
export const useBeaconGenesis = () => {
const { config } = useContext(RuntimeContext);
const { data, error } = useSWRImmutable(
config?.beaconAPI ? `${config?.beaconAPI}/eth/v1/beacon/genesis` : null,
jsonFetcher
);
if (error) {
return undefined;
}
return data;
};
export const useSlotTime = (slot: number | undefined): number | undefined => {
const genesis = useBeaconGenesis();
if (slot === undefined || genesis === undefined) {
return undefined;
}
const rawDate = genesis.data.genesis_time;
return parseInt(rawDate) + slot * SLOT_TIME;
};

View File

@ -2,6 +2,7 @@ import { useState, useEffect } from "react";
export type OtterscanConfig = { export type OtterscanConfig = {
erigonURL?: string; erigonURL?: string;
beaconAPI?: string;
assetsURLPrefix?: string; assetsURLPrefix?: string;
}; };