diff --git a/craco.config.js b/craco.config.js index c836e5f..2f7eeb3 100644 --- a/craco.config.js +++ b/craco.config.js @@ -2,13 +2,11 @@ const webpack = require("webpack"); module.exports = { babel: { - plugins: [ - "@babel/plugin-proposal-nullish-coalescing-operator", - "@babel/plugin-proposal-optional-chaining", - ], + plugins: ["@babel/plugin-proposal-nullish-coalescing-operator", "@babel/plugin-proposal-optional-chaining"], }, webpack: { configure: { + target: ["web"], module: { exprContextCritical: false, // turns off Critical dependency: the request of a dependency is an expression error }, @@ -17,7 +15,7 @@ module.exports = { }, resolve: { fallback: { - '@chainsafe/blst': false, // @chainsafe/blst is a peer dependency which is not needed in a browser environment + "@chainsafe/blst": false, // @chainsafe/blst is a peer dependency which is not needed in a browser environment http: require.resolve("stream-http"), https: require.resolve("https-browserify"), crypto: false, @@ -27,16 +25,17 @@ module.exports = { path: false, zlib: false, fs: false - }, + } }, }, plugins: [ new webpack.DefinePlugin({ - process: { argv: [] }, + process: {argv: []}, }), new webpack.ProvidePlugin({ Buffer: ["buffer", "Buffer"], }), + new webpack.LoaderTargetPlugin('web'), ], }, }; diff --git a/package.json b/package.json index 075b3a4..fae9d1a 100644 --- a/package.json +++ b/package.json @@ -15,16 +15,17 @@ }, "dependencies": { "@chainsafe/bls": "7.1.1", - "@lodestar/api": "^1.9.2", - "@lodestar/config": "^1.9.2", - "@lodestar/light-client": "^1.9.2", - "@lodestar/params": "^1.9.2", - "@lodestar/types": "^1.9.2", - "@lodestar/utils": "^1.9.2", "@chainsafe/persistent-merkle-tree": "^0.6.1", "@chainsafe/ssz": "^0.10.2", "@ethereumjs/statemanager": "^1.0.0-beta.1", "@ethersproject/abi": "^5.6.0", + "@lodestar/api": "^1.9.1", + "@lodestar/config": "^1.9.1", + "@lodestar/light-client": "^1.9.1", + "@lodestar/params": "^1.9.1", + "@lodestar/prover": "next", + "@lodestar/types": "^1.9.1", + "@lodestar/utils": "^1.9.1", "@testing-library/jest-dom": "^5.11.4", "@testing-library/react": "^11.1.0", "@testing-library/user-event": "^12.1.10", @@ -61,9 +62,9 @@ "last 1 safari version" ], "devDependencies": { - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7", - "@babel/plugin-proposal-optional-chaining": "^7.16.7", - "@craco/craco": "^6.4.4", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", + "@babel/plugin-proposal-optional-chaining": "^7.21.0", + "@craco/craco": "^7.1.0", "@types/lodash": "^4.14.177", "@typescript-eslint/parser": "^5.13.0", "eslint": "^7.14.0", @@ -73,6 +74,21 @@ "prettier": "^2.5.1", "react-scripts": "5.0.1", "react-snap": "^1.23.0", - "ts-node": "^10.4.0" + "ts-node": "^10.4.0", + "webpack-cli": "^5.1.4" + }, + "overrides": { + "webpack": "^5.88.1", + "@lodestar/prover@next" : { + "@lodestar/api": { + "cross-fetch": "3.1.5" + }, + "@lodestar/light-client": { + "cross-fetch": "3.1.5" + } + } + }, + "resolutions": { + "@lodestar/prover/**/cross-fetch": "3.1.5" } } diff --git a/src/App.tsx b/src/App.tsx index f529263..e87a062 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,186 +1,25 @@ -import React, {useEffect, useState, useRef} from "react"; -import {fromHexString, toHexString} from "@chainsafe/ssz"; -import {Api, ApiError, getClient} from "@lodestar/api"; -import {Lightclient, LightclientEvent} from "@lodestar/light-client"; -import {LightClientRestTransport} from "@lodestar/light-client/transport"; -import {createChainForkConfig} from "@lodestar/config"; -import {config as configDefault} from "@lodestar/config/default"; - -import {SyncPeriod, bellatrix, allForks} from "@lodestar/types"; -import {computeSyncPeriodAtSlot} from "@lodestar/light-client/utils"; -import {getLcLoggerConsole} from "@lodestar/light-client/utils"; - -import Web3 from "web3"; -import {toBuffer, keccak256} from "ethereumjs-util"; -import {DefaultStateManager} from "@ethereumjs/statemanager"; -import {numberToHex} from "web3-utils"; -import {defaultAbiCoder} from "@ethersproject/abi"; - +import {useContext, useState} from "react"; +import {AccountVerification} from "./components/AccountVerification"; import Footer from "./components/Footer"; -import {ErrorView} from "./components/ErrorView"; -import {Loader} from "./components/Loader"; -import {SyncStatus} from "./SyncStatus"; -import {TimeMonitor} from "./TimeMonitor"; -import {ProofReqResp} from "./ProofReqResp"; -import {ReqStatus} from "./types"; -import { - NetworkName, - networkDefault, - getNetworkData, - defaultNetworkUrls, - defaultNetworkTokens, - ERC20Contract, -} from "./Networks"; -import {ParsedAccount, DisplayAccount} from "./AccountHelper"; - -const stateManager = new DefaultStateManager(); +import {ProofProviderView} from "./components/ProofProviderView"; +import {TrustedCheckpoint} from "./components/TrustedCheckpoint"; +import {ConfigurationContext} from "./contexts/ConfigurationContext"; +import {defaultNetworkTokens, networkDefault} from "./utils/networks"; +import {UiContext} from "./contexts/UiContext"; +import {Loader} from "./components/Loader/index"; +import {ErrorView} from "./components/ErrorView/index"; +import {ProofProviderContext} from "./contexts/ProofProviderContext"; +import {ERC20Contract, NetworkName} from "./types"; export default function App(): JSX.Element { - const [network, setNetwork] = useState(networkDefault); - const [beaconApiUrl, setBeaconApiUrl] = useState(defaultNetworkUrls[networkDefault].beaconApiUrl); - const [elRpcUrl, setElRpcUrl] = useState(defaultNetworkUrls[networkDefault].elRpcUrl); - const [checkpointRootStr, setCheckpointRootStr] = useState(""); - const [reqStatusInit, setReqStatusInit] = useState>({}); - const [head, setHead] = useState(); - const [latestSyncedPeriod, setLatestSyncedPeriod] = useState(); - // Setting Avalanche Bridge as the default token address to showcase changing balances - const [address, setAddress] = useState("0x8EB8a3b98659Cce290402893d0123abb75E3ab28"); - const [accountReqStatus, setAccountReqStatus] = useState>({}); + const {network, setNetwork, beaconUrl, setBeaconUrl, rpcUrl, setRpcUrl, trustedCheckpoint, setTrustedCheckpoint} = + useContext(ConfigurationContext); + const {error, progress, setError} = useContext(UiContext); + const {initFromTrustedCheckpoint} = useContext(ProofProviderContext); + const [erc20Contracts, setErc20Contracts] = useState>( defaultNetworkTokens[networkDefault].full ); - const web3 = useRef(); - - useEffect(() => { - setBeaconApiUrl(defaultNetworkUrls[network].beaconApiUrl); - setElRpcUrl(defaultNetworkUrls[network].elRpcUrl); - setErc20Contracts(defaultNetworkTokens[network].full); - }, [network]); - - useEffect(() => { - async function fetchAndVerifyAccount() { - const client = reqStatusInit.result; - if (!client || !head || !address || !elRpcUrl) { - return; - } - - try { - if (!web3.current) web3.current = new Web3(elRpcUrl); - } catch (e) { - setAccountReqStatus({result: accountReqStatus.result, error: e as Error}); - return; - } - - const blockRes = await (client["transport"]["api"] as Api).beacon.getBlockV2(head.beacon.slot); - ApiError.assert(blockRes); - - const block = blockRes.response.data as bellatrix.SignedBeaconBlock; - const executionPayload = block.message.body.executionPayload; - - // If the merge not complete, executionPayload would not exists - if (!executionPayload) { - setAccountReqStatus({result: accountReqStatus.result, loading: `Waiting for an execution payload`}); - return; - } - - setAccountReqStatus({result: accountReqStatus.result, loading: `Fetching status from ${elRpcUrl}`}); - const verifiedAccount = await fetchAndVerifyAddressBalances({ - web3: web3.current, - executionPayload, - address, - erc20Contracts, - }); - setAccountReqStatus({result: verifiedAccount}); - } - - fetchAndVerifyAccount().catch((e) => { - setAccountReqStatus({result: accountReqStatus.result, error: e}); - }); - }, [head, address, elRpcUrl, erc20Contracts]); - - useEffect(() => { - const client = reqStatusInit.result; - if (!client) return; - - client.start(); - const head = client.getHead(); - setHead(head); - setLatestSyncedPeriod(computeSyncPeriodAtSlot(head.beacon.slot)); - - function onNewHead(newHeader: allForks.LightClientHeader) { - setHead(newHeader); - } - - function onNewCommittee(period: SyncPeriod) { - setLatestSyncedPeriod(period); - } - - client.emitter.on(LightclientEvent.lightClientFinalityHeader, onNewHead); - // client.emitter.on(LightclientEvent.committee, onNewCommittee); - - return function () { - client.emitter.off(LightclientEvent.lightClientFinalityHeader, onNewHead); - // client.emitter.off(LightclientEvent.committee, onNewCommittee); - }; - }, [reqStatusInit.result]); - - async function initializeFromCheckpointStr(checkpointRootHex: string) { - try { - // Validate root - if (!checkpointRootHex.startsWith("0x")) { - throw Error("Root must start with 0x"); - } - const checkpointRoot = fromHexString(checkpointRootHex); - if (checkpointRoot.length !== 32) { - throw Error(`Root must be 32 bytes long: ${checkpointRoot.length}`); - } - - setReqStatusInit({loading: `Syncing from trusted checkpoint: ${checkpointRootHex}`}); - - const {genesisData, chainConfig} = await getNetworkData(network, beaconApiUrl); - const config = createChainForkConfig(chainConfig); - - const client = await Lightclient.initializeFromCheckpointRoot({ - config, - logger: getLcLoggerConsole({logDebug: true}), - transport: new LightClientRestTransport(getClient({urls: [beaconApiUrl]}, {config})), - genesisData, - checkpointRoot, - }); - - setReqStatusInit({result: client}); - } catch (e) { - (e as Error).message = `Error initializing from trusted checkpoint ${checkpointRootHex}: ${(e as Error).message}`; - setReqStatusInit({error: e as Error}); - // eslint-disable-next-line no-console - console.error(e); - } - } - - async function fillCheckpointFromNode() { - try { - setReqStatusInit({loading: "Fetching checkpoint from trusted node"}); - - const client = getClient({baseUrl: beaconApiUrl}, {config: configDefault}); - const res = await client.beacon.getStateFinalityCheckpoints("head"); - ApiError.assert(res); - const finalizedCheckpoint = res.response.data.finalized; - setCheckpointRootStr(toHexString(finalizedCheckpoint.root)); - - // Hasn't load clint, just disable loader - setReqStatusInit({}); - } catch (e) { - (e as Error).message = `Error initializing from trusted node: ${(e as Error).message}`; - setReqStatusInit({error: e as Error}); - // eslint-disable-next-line no-console - console.error(e); - } - } - - function deleteState() { - // deleteSnapshot(); - setReqStatusInit({}); - } return ( <> @@ -209,209 +48,62 @@ export default function App(): JSX.Element { -

Beacon node API URL

- setBeaconApiUrl(e.target.value)} /> + setBeaconUrl(e.target.value)} />
-

Execution Rpc URL

{ - web3.current = undefined; - setElRpcUrl(e.target.value); + setRpcUrl(e.target.value); }} />
-
-
-
- any ethereum address -
- setAddress(e.target.value)} /> -
-
- {(accountReqStatus.result || accountReqStatus.error) && ( -
-
- type - -
+ -
- {accountReqStatus.loading ? ( - - ) : ( -
- - {accountReqStatus.error || !accountReqStatus.result - ? "error" - : accountReqStatus.result.verified - ? "valid" - : "invalid"} - -

- {accountReqStatus.error || !accountReqStatus.result - ? "🛑" - : accountReqStatus.result.verified - ? "✅" - : "❌"} -

-
- )} -
-
- )} -
-
- {accountReqStatus.result ? ( - - ) : accountReqStatus.loading ? ( +

+ {error ? : <>} + + {progress && progress.toString().trim().length > 0 ? ( <> -

{accountReqStatus.loading}

+

{progress}

) : ( <> )} - {accountReqStatus.error && } -

- {!reqStatusInit.result ? ( - <> -
-
-
-

- Trusted checkpoint{" "} - - (Fill with latest finalized) - -

- setCheckpointRootStr(e.target.value)} - placeholder="0xaabb..." - /> -
-
-
-
- -
-
-
- - ) : ( +
+ +
-
- )} +
- {reqStatusInit.result ? ( - <> - - - {head !== undefined && } - - ) : reqStatusInit.error ? ( - - ) : reqStatusInit.loading ? ( - <> - -

Initializing light-client - {reqStatusInit.loading}

- - ) : null} + +