From 20445711c7029cff4a10ad1c7c493990962472e6 Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Tue, 16 May 2023 14:11:42 -0700 Subject: [PATCH 1/4] multi layer support --- packages/status-page/.default.env | 23 +- packages/status-page/src/App.svelte | 23 +- .../src/components/StatusIndicator.svelte | 1 + packages/status-page/src/domain/layer.ts | 4 + .../status-page/src/pages/home/Home.svelte | 538 +++++++++--------- packages/status-page/src/store/layer.ts | 4 + .../src/utils/buildStatusIndicators.ts | 293 ++++++++++ .../src/utils/getStateVariables.ts | 9 +- .../src/utils/layerToDisplayName.ts | 4 + 9 files changed, 614 insertions(+), 285 deletions(-) create mode 100644 packages/status-page/src/domain/layer.ts create mode 100644 packages/status-page/src/store/layer.ts create mode 100644 packages/status-page/src/utils/buildStatusIndicators.ts create mode 100644 packages/status-page/src/utils/layerToDisplayName.ts diff --git a/packages/status-page/.default.env b/packages/status-page/.default.env index 871254df35..ff979910cf 100644 --- a/packages/status-page/.default.env +++ b/packages/status-page/.default.env @@ -1,10 +1,15 @@ -VITE_NODE_ENV=dev -VITE_L1_RPC_URL="https://l1rpc.a1.taiko.xyz" -VITE_L2_RPC_URL="https://l2rpc.a1.taiko.xyz" -VITE_TAIKO_L2_ADDRESS="0x0000777700000000000000000000000000000001" -VITE_TAIKO_L1_ADDRESS="0x7B3AF414448ba906f02a1CA307C56c4ADFF27ce7" -VITE_L1_EXPLORER_URL="https://l1explorer.a1.taiko.xyz" -VITE_L2_EXPLORER_URL="https://l2explorer.a1.taiko.xyz" -VITE_FEE_TOKEN_SYMBOL="TKO"; +VITE_NODE_ENV=production +VITE_L1_RPC_URL="https://l1rpc.internal.taiko.xyz" +VITE_L2_RPC_URL="https://l2rpc.internal.taiko.xyz" +VITE_L3_RPC_URL="https://l2rpc.internal.taiko.xyz" +VITE_L2_TAIKO_L2_ADDRESS="0x1000777700000000000000000000000000000001" +VITE_L2_TAIKO_L1_ADDRESS="0x0B306BF915C4d645ff596e518fAf3F9669b97016" +VITE_L3_TAIKO_L2_ADDRESS="0x1000777700000000000000000000000000000001" +VITE_L3_TAIKO_L1_ADDRESS="0x0B306BF915C4d645ff596e518fAf3F9669b97016" +VITE_L1_EXPLORER_URL="https://l1explorer.internal.taiko.xyz" +VITE_L2_EXPLORER_URL="https://l2explorer.internal.taiko.xyz" +VITE_L3_EXPLORER_URL="https://l3explorer.internal.taiko.xyz" +VITE_FEE_TOKEN_SYMBOL=TTKO VITE_ORACLE_PROVER_ADDRESS="0x1567CDAb5F7a69154e61A16D8Ff5eE6A3e991b39" -VITE_EVENT_INDEXER_API_URL="http://localhost:4100" \ No newline at end of file +VITE_L2_EVENT_INDEXER_API_URL="http://localhost:4100" +VITE_L3_EVENT_INDEXER_API_URL="http://localhost:4100" \ No newline at end of file diff --git a/packages/status-page/src/App.svelte b/packages/status-page/src/App.svelte index 0bba71fdce..90493e6b21 100644 --- a/packages/status-page/src/App.svelte +++ b/packages/status-page/src/App.svelte @@ -6,32 +6,13 @@ import { setupI18n } from "./i18n"; import Navbar from "./components/Navbar.svelte"; import { ethers } from "ethers"; + import { layer } from "./store/layer"; + import { Layer } from "./domain/layer"; setupI18n({ withLocale: "en" }); - const l1Provider = new ethers.providers.JsonRpcProvider( - import.meta.env.VITE_L1_RPC_URL - ); - const l2Provider = new ethers.providers.JsonRpcProvider( - import.meta.env.VITE_L2_RPC_URL - ); - const routes = { "/": wrap({ component: Home, - props: { - l1Provider: l1Provider, - l1TaikoAddress: import.meta.env.VITE_TAIKO_L1_ADDRESS, - l2Provider: l2Provider, - l2TaikoAddress: import.meta.env.VITE_TAIKO_L2_ADDRESS, - l1ExplorerUrl: import.meta.env.VITE_L1_EXPLORER_URL, - l2ExplorerUrl: import.meta.env.VITE_L2_EXPLORER_URL, - feeTokenSymbol: import.meta.env.VITE_FEE_TOKEN_SYMBOL || "TKO", - oracleProverAddress: - import.meta.env.ORACLE_PROVER_ADDRESS || - "0x1567CDAb5F7a69154e61A16D8Ff5eE6A3e991b39", - eventIndexerApiUrl: import.meta.env.VITE_EVENT_INDEXER_API_URL, - }, - userData: {}, }), }; diff --git a/packages/status-page/src/components/StatusIndicator.svelte b/packages/status-page/src/components/StatusIndicator.svelte index 6164708b82..1587826bd2 100644 --- a/packages/status-page/src/components/StatusIndicator.svelte +++ b/packages/status-page/src/components/StatusIndicator.svelte @@ -52,6 +52,7 @@ } else { statusValue = 0; } + console.log(header, statusValue); } catch (e) { console.error(header, e); } diff --git a/packages/status-page/src/domain/layer.ts b/packages/status-page/src/domain/layer.ts new file mode 100644 index 0000000000..47935ccc36 --- /dev/null +++ b/packages/status-page/src/domain/layer.ts @@ -0,0 +1,4 @@ +export enum Layer { + Two, + Three, +} diff --git a/packages/status-page/src/pages/home/Home.svelte b/packages/status-page/src/pages/home/Home.svelte index 98248f1092..f28550eef5 100644 --- a/packages/status-page/src/pages/home/Home.svelte +++ b/packages/status-page/src/pages/home/Home.svelte @@ -1,5 +1,5 @@

Taiko Protocol Status

+

+ {layerToDisplayName($layer)} +

(Layer.Three); diff --git a/packages/status-page/src/utils/buildStatusIndicators.ts b/packages/status-page/src/utils/buildStatusIndicators.ts new file mode 100644 index 0000000000..283b51a901 --- /dev/null +++ b/packages/status-page/src/utils/buildStatusIndicators.ts @@ -0,0 +1,293 @@ +export const buildStatusIndicators() { + [ + { + statusFunc: async ( + provider: ethers.providers.JsonRpcProvider, + address: string + ) => (await getNumProvers(eventIndexerApiUrl)).uniqueProvers, + provider: l1Provider, + contractAddress: l1TaikoAddress, + header: "Unique Provers", + intervalInMs: 0, + colorFunc: (value: Status) => { + return "green"; + }, + onClick: (value: Status) => { + proverDetailsOpen = true; + }, + tooltip: + "The number of unique provers who successfully submitted a proof to the TaikoL1 smart contract.", + }, + { + statusFunc: async ( + provider: ethers.providers.JsonRpcProvider, + address: string + ) => (await getNumProposers(eventIndexerApiUrl)).uniqueProposers, + provider: l1Provider, + contractAddress: l1TaikoAddress, + header: "Unique Proposers", + intervalInMs: 0, + colorFunc: (value: Status) => { + return "green"; + }, + onClick: (value: Status) => { + proposerDetailsOpen = true; + }, + tooltip: + "The number of unique proposers who successfully submitted a proposed block to the TaikoL1 smart contract.", + }, + { + statusFunc: getLatestSyncedHeader, + watchStatusFunc: watchHeaderSynced, + provider: l1Provider, + contractAddress: l1TaikoAddress, + header: "L1 Latest Synced Header", + intervalInMs: 0, + colorFunc: (value: Status) => { + return "green"; + }, + onClick: (value: Status) => { + window.open(`${l2ExplorerUrl}/block/${value.toString()}`, "_blank"); + }, + tooltip: + "The most recent Layer 2 Header that has been synchronized with the TaikoL1 smart contract.", + }, + { + statusFunc: getLatestSyncedHeader, + watchStatusFunc: watchHeaderSynced, + provider: l2Provider, + contractAddress: l2TaikoAddress, + header: "L2 Latest Synced Header", + intervalInMs: 0, + colorFunc: (value: Status) => { + return "green"; + }, + onClick: (value: Status) => { + window.open(`${l1ExplorerUrl}/block/${value.toString()}`, "_blank"); + }, + tooltip: + "The most recent Layer 1 Header that has been synchronized with the TaikoL2 smart contract. The headers are synchronized with every L2 block.", + }, + { + statusFunc: getPendingTransactions, + watchStatusFunc: null, + provider: l2Provider, + contractAddress: "", + header: "Tx Mempool (pending)", + intervalInMs: 20000, + colorFunc: (value: Status) => { + if (BigNumber.from(value).gt(4000)) return "red"; + return "green"; + }, + tooltip: + "The current processable transactions in the mempool that have not been added to a block yet.", + }, + { + statusFunc: getQueuedTransactions, + watchStatusFunc: null, + provider: l2Provider, + contractAddress: "", + header: "Tx Mempool (queued)", + intervalInMs: 20000, + colorFunc: (value: Status) => { + if (BigNumber.from(value).gt(4000)) return "red"; + return "green"; + }, + tooltip: + "The current transactions in the mempool where the transaction nonce is not in sequence. They are currently non-processable.", + }, + { + statusFunc: getAvailableSlots, + watchStatusFunc: null, + provider: l1Provider, + contractAddress: l1TaikoAddress, + header: "Available Slots", + intervalInMs: 20000, + colorFunc: (value: Status) => { + if (BigNumber.from(value).eq(0)) return "red"; + return "green"; + }, + tooltip: + "The amount of slots for proposed blocks on the TaikoL1 smart contract. When this number is 0, no blocks can be proposed until a block has been proven.", + }, + { + statusFunc: getLastVerifiedBlockId, + watchStatusFunc: null, + provider: l1Provider, + contractAddress: l1TaikoAddress, + header: "Last Verified Block ID", + intervalInMs: 20000, + colorFunc: (value: Status) => { + return "green"; + }, + tooltip: + "The most recently verified Layer 2 block on the TaikoL1 smart contract.", + }, + { + statusFunc: getNextBlockId, + watchStatusFunc: null, + provider: l1Provider, + contractAddress: l1TaikoAddress, + header: "Next Block ID", + intervalInMs: 20000, + colorFunc: (value: Status) => { + return "green"; + }, + tooltip: + "The ID that the next proposed block on the TaikoL1 smart contract will receive.", + }, + { + statusFunc: getPendingBlocks, + watchStatusFunc: null, + provider: l1Provider, + contractAddress: l1TaikoAddress, + header: "Unverified Blocks", + intervalInMs: 20000, + colorFunc: (value: Status) => { + if (BigNumber.from(value).eq(0)) { + return "red"; + } else if (BigNumber.from(value).lt(5)) { + return "yellow"; + } else { + return "green"; + } + }, + tooltip: + "The amount of pending proposed blocks that have not been proven on the TaikoL1 smart contract.", + }, + { + statusFunc: getEthDeposits, + watchStatusFunc: null, + provider: l1Provider, + contractAddress: l1TaikoAddress, + header: "ETH Deposits", + intervalInMs: 20000, + colorFunc: (value: Status) => { + if (BigNumber.from(value).eq(0)) { + return "green"; + } else if (BigNumber.from(value).lt(32)) { + return "yellow"; + } else { + return "red"; + } + }, + tooltip: "The number of pending ETH deposits for L1 => L2", + }, + { + statusFunc: getNextEthDepositToProcess, + watchStatusFunc: null, + provider: l1Provider, + contractAddress: l1TaikoAddress, + header: "Next ETH Deposit", + intervalInMs: 20000, + colorFunc: (value: Status) => { + return "green"; + }, + tooltip: "The next ETH deposit that will be processed", + }, + { + statusFunc: getGasPrice, + watchStatusFunc: null, + provider: l2Provider, + contractAddress: "", + header: "Gas Price (gwei)", + intervalInMs: 20000, + colorFunc: (value: Status) => { + return "green"; + }, + tooltip: + "The current recommended gas price for a transaction on Layer 2.", + }, + ]; + + try { + statusIndicators.push({ + statusFunc: getBlockFee, + watchStatusFunc: null, + provider: l1Provider, + contractAddress: l1TaikoAddress, + header: "Block Fee", + intervalInMs: 15000, + colorFunc: function (status: Status) { + return "green"; // todo: whats green, yellow, red? + }, + tooltip: + "The current fee to propose a block to the TaikoL1 smart contract.", + }); + statusIndicators.push({ + statusFunc: getProofReward, + watchStatusFunc: null, + provider: l1Provider, + contractAddress: l1TaikoAddress, + header: "Proof Reward", + intervalInMs: 15000, + colorFunc: function (status: Status) { + return "green"; // todo: whats green, yellow, red? + }, + tooltip: + "The current reward for successfully submitting a proof for a proposed block on the TaikoL1 smart contract.", + }); + } catch (e) { + console.error(e); + } + + try { + statusIndicators.push({ + provider: l1Provider, + contractAddress: l1TaikoAddress, + header: "Latest Proof", + intervalInMs: 0, + status: "0", + watchStatusFunc: async ( + provider: ethers.providers.JsonRpcProvider, + address: string, + onEvent: (value: Status) => void + ) => { + const contract = new Contract(address, TaikoL1, provider); + contract.on( + "BlockProven", + ( + id, + parentHash, + blockHash, + signalRoot, + prover, + provenAt, + ...args + ) => { + // ignore oracle prover + if (prover.toLowerCase() !== oracleProverAddress.toLowerCase()) { + onEvent(new Date().getTime().toString()); + } + } + ); + }, + colorFunc: function (status: Status) { + return "green"; // todo: whats green, yellow, red? + }, + tooltip: "The most recent block proof submitted on TaikoL1 contract.", + }); + + statusIndicators.push({ + provider: l1Provider, + contractAddress: l1TaikoAddress, + statusFunc: async ( + provider: ethers.providers.JsonRpcProvider, + address: string + ) => { + const stateVars = await getStateVariables(provider, address); + return `${stateVars.proofTimeIssued.toNumber() / 1000} seconds`; + }, + colorFunc: function (status: Status) { + return "green"; // todo: whats green, yellow, red? + }, + header: "Average Proof Time", + intervalInMs: 5 * 1000, + tooltip: + "The current average proof time, updated when a block is successfully proven.", + }); + } catch (e) { + console.error(e); + } + +} \ No newline at end of file diff --git a/packages/status-page/src/utils/getStateVariables.ts b/packages/status-page/src/utils/getStateVariables.ts index d0b83e0b7b..e2d60cc4e0 100644 --- a/packages/status-page/src/utils/getStateVariables.ts +++ b/packages/status-page/src/utils/getStateVariables.ts @@ -5,6 +5,7 @@ const cacheTime = 1000 * 15; // 15 seconds type StateVarsCache = { cachedAt: number; stateVars: any; + chainId: number; }; let stateVarsCache: StateVarsCache; @@ -13,7 +14,12 @@ export const getStateVariables = async ( provider: ethers.providers.JsonRpcProvider, contractAddress: string ) => { - if (stateVarsCache && stateVarsCache.cachedAt + cacheTime > Date.now()) { + const { chainId } = await provider.getNetwork(); + if ( + stateVarsCache && + stateVarsCache.chainId === chainId && + stateVarsCache.cachedAt + cacheTime > Date.now() + ) { return stateVarsCache.stateVars; } @@ -22,6 +28,7 @@ export const getStateVariables = async ( stateVarsCache = { stateVars: vars, cachedAt: Date.now(), + chainId: chainId, }; return vars; }; diff --git a/packages/status-page/src/utils/layerToDisplayName.ts b/packages/status-page/src/utils/layerToDisplayName.ts new file mode 100644 index 0000000000..886c0accff --- /dev/null +++ b/packages/status-page/src/utils/layerToDisplayName.ts @@ -0,0 +1,4 @@ +import { Layer } from "../domain/layer"; + +export const layerToDisplayName = (layer: Layer) => + layer === Layer.Two ? "Taiko L2" : "Taiko L3"; From 1b39f1dde0b31efc04edbf7601d2772995066359 Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Tue, 16 May 2023 14:24:53 -0700 Subject: [PATCH 2/4] cleanup code --- .../status-page/src/pages/home/Home.svelte | 402 +----------- .../src/utils/buildStatusIndicators.ts | 609 +++++++++--------- packages/status-page/src/utils/initConfig.ts | 48 ++ 3 files changed, 392 insertions(+), 667 deletions(-) create mode 100644 packages/status-page/src/utils/initConfig.ts diff --git a/packages/status-page/src/pages/home/Home.svelte b/packages/status-page/src/pages/home/Home.svelte index f28550eef5..7f5d6ea486 100644 --- a/packages/status-page/src/pages/home/Home.svelte +++ b/packages/status-page/src/pages/home/Home.svelte @@ -1,400 +1,52 @@ @@ -430,10 +82,10 @@ class="grid grid-cols-2 gap-4 text-center my-10 max-h-96 overflow-y-auto" slot="body" > - {#await getNumProvers(eventIndexerApiUrl) then provers} + {#await getNumProvers(config.eventIndexerApiUrl) then provers} {#each provers.provers as prover} @@ -454,10 +106,10 @@ class="grid grid-cols-2 gap-4 text-center my-10 max-h-96 overflow-y-auto" slot="body" > - {#await getNumProposers(eventIndexerApiUrl) then proposers} + {#await getNumProposers(config.eventIndexerApiUrl) then proposers} {#each proposers.proposers as proposer} diff --git a/packages/status-page/src/utils/buildStatusIndicators.ts b/packages/status-page/src/utils/buildStatusIndicators.ts index 283b51a901..2b880929c6 100644 --- a/packages/status-page/src/utils/buildStatusIndicators.ts +++ b/packages/status-page/src/utils/buildStatusIndicators.ts @@ -1,293 +1,318 @@ -export const buildStatusIndicators() { - [ - { - statusFunc: async ( - provider: ethers.providers.JsonRpcProvider, - address: string - ) => (await getNumProvers(eventIndexerApiUrl)).uniqueProvers, - provider: l1Provider, - contractAddress: l1TaikoAddress, - header: "Unique Provers", - intervalInMs: 0, - colorFunc: (value: Status) => { - return "green"; - }, - onClick: (value: Status) => { - proverDetailsOpen = true; - }, - tooltip: - "The number of unique provers who successfully submitted a proof to the TaikoL1 smart contract.", - }, - { - statusFunc: async ( - provider: ethers.providers.JsonRpcProvider, - address: string - ) => (await getNumProposers(eventIndexerApiUrl)).uniqueProposers, - provider: l1Provider, - contractAddress: l1TaikoAddress, - header: "Unique Proposers", - intervalInMs: 0, - colorFunc: (value: Status) => { - return "green"; - }, - onClick: (value: Status) => { - proposerDetailsOpen = true; - }, - tooltip: - "The number of unique proposers who successfully submitted a proposed block to the TaikoL1 smart contract.", - }, - { - statusFunc: getLatestSyncedHeader, - watchStatusFunc: watchHeaderSynced, - provider: l1Provider, - contractAddress: l1TaikoAddress, - header: "L1 Latest Synced Header", - intervalInMs: 0, - colorFunc: (value: Status) => { - return "green"; - }, - onClick: (value: Status) => { - window.open(`${l2ExplorerUrl}/block/${value.toString()}`, "_blank"); - }, - tooltip: - "The most recent Layer 2 Header that has been synchronized with the TaikoL1 smart contract.", - }, - { - statusFunc: getLatestSyncedHeader, - watchStatusFunc: watchHeaderSynced, - provider: l2Provider, - contractAddress: l2TaikoAddress, - header: "L2 Latest Synced Header", - intervalInMs: 0, - colorFunc: (value: Status) => { - return "green"; - }, - onClick: (value: Status) => { - window.open(`${l1ExplorerUrl}/block/${value.toString()}`, "_blank"); - }, - tooltip: - "The most recent Layer 1 Header that has been synchronized with the TaikoL2 smart contract. The headers are synchronized with every L2 block.", - }, - { - statusFunc: getPendingTransactions, - watchStatusFunc: null, - provider: l2Provider, - contractAddress: "", - header: "Tx Mempool (pending)", - intervalInMs: 20000, - colorFunc: (value: Status) => { - if (BigNumber.from(value).gt(4000)) return "red"; - return "green"; - }, - tooltip: - "The current processable transactions in the mempool that have not been added to a block yet.", - }, - { - statusFunc: getQueuedTransactions, - watchStatusFunc: null, - provider: l2Provider, - contractAddress: "", - header: "Tx Mempool (queued)", - intervalInMs: 20000, - colorFunc: (value: Status) => { - if (BigNumber.from(value).gt(4000)) return "red"; - return "green"; - }, - tooltip: - "The current transactions in the mempool where the transaction nonce is not in sequence. They are currently non-processable.", - }, - { - statusFunc: getAvailableSlots, - watchStatusFunc: null, - provider: l1Provider, - contractAddress: l1TaikoAddress, - header: "Available Slots", - intervalInMs: 20000, - colorFunc: (value: Status) => { - if (BigNumber.from(value).eq(0)) return "red"; - return "green"; - }, - tooltip: - "The amount of slots for proposed blocks on the TaikoL1 smart contract. When this number is 0, no blocks can be proposed until a block has been proven.", - }, - { - statusFunc: getLastVerifiedBlockId, - watchStatusFunc: null, - provider: l1Provider, - contractAddress: l1TaikoAddress, - header: "Last Verified Block ID", - intervalInMs: 20000, - colorFunc: (value: Status) => { - return "green"; - }, - tooltip: - "The most recently verified Layer 2 block on the TaikoL1 smart contract.", - }, - { - statusFunc: getNextBlockId, - watchStatusFunc: null, - provider: l1Provider, - contractAddress: l1TaikoAddress, - header: "Next Block ID", - intervalInMs: 20000, - colorFunc: (value: Status) => { - return "green"; - }, - tooltip: - "The ID that the next proposed block on the TaikoL1 smart contract will receive.", - }, - { - statusFunc: getPendingBlocks, - watchStatusFunc: null, - provider: l1Provider, - contractAddress: l1TaikoAddress, - header: "Unverified Blocks", - intervalInMs: 20000, - colorFunc: (value: Status) => { - if (BigNumber.from(value).eq(0)) { - return "red"; - } else if (BigNumber.from(value).lt(5)) { - return "yellow"; - } else { - return "green"; - } - }, - tooltip: - "The amount of pending proposed blocks that have not been proven on the TaikoL1 smart contract.", - }, - { - statusFunc: getEthDeposits, - watchStatusFunc: null, - provider: l1Provider, - contractAddress: l1TaikoAddress, - header: "ETH Deposits", - intervalInMs: 20000, - colorFunc: (value: Status) => { - if (BigNumber.from(value).eq(0)) { - return "green"; - } else if (BigNumber.from(value).lt(32)) { - return "yellow"; - } else { - return "red"; - } - }, - tooltip: "The number of pending ETH deposits for L1 => L2", - }, - { - statusFunc: getNextEthDepositToProcess, - watchStatusFunc: null, - provider: l1Provider, - contractAddress: l1TaikoAddress, - header: "Next ETH Deposit", - intervalInMs: 20000, - colorFunc: (value: Status) => { - return "green"; - }, - tooltip: "The next ETH deposit that will be processed", - }, - { - statusFunc: getGasPrice, - watchStatusFunc: null, - provider: l2Provider, - contractAddress: "", - header: "Gas Price (gwei)", - intervalInMs: 20000, - colorFunc: (value: Status) => { - return "green"; - }, - tooltip: - "The current recommended gas price for a transaction on Layer 2.", - }, - ]; - - try { - statusIndicators.push({ - statusFunc: getBlockFee, - watchStatusFunc: null, - provider: l1Provider, - contractAddress: l1TaikoAddress, - header: "Block Fee", - intervalInMs: 15000, - colorFunc: function (status: Status) { - return "green"; // todo: whats green, yellow, red? - }, - tooltip: - "The current fee to propose a block to the TaikoL1 smart contract.", - }); - statusIndicators.push({ - statusFunc: getProofReward, - watchStatusFunc: null, - provider: l1Provider, - contractAddress: l1TaikoAddress, - header: "Proof Reward", - intervalInMs: 15000, - colorFunc: function (status: Status) { - return "green"; // todo: whats green, yellow, red? - }, - tooltip: - "The current reward for successfully submitting a proof for a proposed block on the TaikoL1 smart contract.", - }); - } catch (e) { - console.error(e); - } - - try { - statusIndicators.push({ - provider: l1Provider, - contractAddress: l1TaikoAddress, - header: "Latest Proof", - intervalInMs: 0, - status: "0", - watchStatusFunc: async ( - provider: ethers.providers.JsonRpcProvider, - address: string, - onEvent: (value: Status) => void +import { BigNumber, Contract, ethers } from "ethers"; +import TaikoL1 from "../constants/abi/TaikoL1"; +import type { Status, StatusIndicatorProp } from "../domain/status"; +import { getAvailableSlots } from "./getAvailableSlots"; +import { getBlockFee } from "./getBlockFee"; +import { getEthDeposits } from "./getEthDeposits"; +import { getGasPrice } from "./getGasPrice"; +import { getLastVerifiedBlockId } from "./getLastVerifiedBlockId"; +import { getLatestSyncedHeader } from "./getLatestSyncedHeader"; +import { getNextBlockId } from "./getNextBlockId"; +import { getNextEthDepositToProcess } from "./getNextEthDepositToProcess"; +import { getNumProposers } from "./getNumProposers"; +import { getNumProvers } from "./getNumProvers"; +import { getPendingBlocks } from "./getPendingBlocks"; +import { getPendingTransactions } from "./getPendingTransactions"; +import { getProofReward } from "./getProofReward"; +import { getQueuedTransactions } from "./getQueuedTransactions"; +import { getStateVariables } from "./getStateVariables"; +import type { initConfig } from "./initConfig"; +import { watchHeaderSynced } from "./watchHeaderSynced"; + +export function buildStatusIndicators( + config: ReturnType, + onProverClick: (value: Status) => void, + onProposerClick: (value: Status) => void +) { + const indicators: StatusIndicatorProp[] = [ + { + statusFunc: async ( + provider: ethers.providers.JsonRpcProvider, + address: string + ) => (await getNumProvers(config.eventIndexerApiUrl)).uniqueProvers, + provider: config.l1Provider, + contractAddress: config.l1TaikoAddress, + header: "Unique Provers", + intervalInMs: 0, + colorFunc: (value: Status) => { + return "green"; + }, + onClick: onProverClick, + tooltip: + "The number of unique provers who successfully submitted a proof to the TaikoL1 smart contract.", + }, + { + statusFunc: async ( + provider: ethers.providers.JsonRpcProvider, + address: string + ) => (await getNumProposers(config.eventIndexerApiUrl)).uniqueProposers, + provider: config.l1Provider, + contractAddress: config.l1TaikoAddress, + header: "Unique Proposers", + intervalInMs: 0, + colorFunc: (value: Status) => { + return "green"; + }, + onClick: onProposerClick, + tooltip: + "The number of unique proposers who successfully submitted a proposed block to the TaikoL1 smart contract.", + }, + { + statusFunc: getLatestSyncedHeader, + watchStatusFunc: watchHeaderSynced, + provider: config.l1Provider, + contractAddress: config.l1TaikoAddress, + header: "L1 Latest Synced Header", + intervalInMs: 0, + colorFunc: (value: Status) => { + return "green"; + }, + onClick: (value: Status) => { + window.open( + `${config.l2ExplorerUrl}/block/${value.toString()}`, + "_blank" + ); + }, + tooltip: + "The most recent Layer 2 Header that has been synchronized with the TaikoL1 smart contract.", + }, + { + statusFunc: getLatestSyncedHeader, + watchStatusFunc: watchHeaderSynced, + provider: config.l2Provider, + contractAddress: config.l2TaikoAddress, + header: "L2 Latest Synced Header", + intervalInMs: 0, + colorFunc: (value: Status) => { + return "green"; + }, + onClick: (value: Status) => { + window.open( + `${config.l1ExplorerUrl}/block/${value.toString()}`, + "_blank" + ); + }, + tooltip: + "The most recent Layer 1 Header that has been synchronized with the TaikoL2 smart contract. The headers are synchronized with every L2 block.", + }, + { + statusFunc: getPendingTransactions, + watchStatusFunc: null, + provider: config.l2Provider, + contractAddress: "", + header: "Tx Mempool (pending)", + intervalInMs: 20000, + colorFunc: (value: Status) => { + if (BigNumber.from(value).gt(4000)) return "red"; + return "green"; + }, + tooltip: + "The current processable transactions in the mempool that have not been added to a block yet.", + }, + { + statusFunc: getQueuedTransactions, + watchStatusFunc: null, + provider: config.l2Provider, + contractAddress: "", + header: "Tx Mempool (queued)", + intervalInMs: 20000, + colorFunc: (value: Status) => { + if (BigNumber.from(value).gt(4000)) return "red"; + return "green"; + }, + tooltip: + "The current transactions in the mempool where the transaction nonce is not in sequence. They are currently non-processable.", + }, + { + statusFunc: getAvailableSlots, + watchStatusFunc: null, + provider: config.l1Provider, + contractAddress: config.l1TaikoAddress, + header: "Available Slots", + intervalInMs: 20000, + colorFunc: (value: Status) => { + if (BigNumber.from(value).eq(0)) return "red"; + return "green"; + }, + tooltip: + "The amount of slots for proposed blocks on the TaikoL1 smart contract. When this number is 0, no blocks can be proposed until a block has been proven.", + }, + { + statusFunc: getLastVerifiedBlockId, + watchStatusFunc: null, + provider: config.l1Provider, + contractAddress: config.l1TaikoAddress, + header: "Last Verified Block ID", + intervalInMs: 20000, + colorFunc: (value: Status) => { + return "green"; + }, + tooltip: + "The most recently verified Layer 2 block on the TaikoL1 smart contract.", + }, + { + statusFunc: getNextBlockId, + watchStatusFunc: null, + provider: config.l1Provider, + contractAddress: config.l1TaikoAddress, + header: "Next Block ID", + intervalInMs: 20000, + colorFunc: (value: Status) => { + return "green"; + }, + tooltip: + "The ID that the next proposed block on the TaikoL1 smart contract will receive.", + }, + { + statusFunc: getPendingBlocks, + watchStatusFunc: null, + provider: config.l1Provider, + contractAddress: config.l1TaikoAddress, + header: "Unverified Blocks", + intervalInMs: 20000, + colorFunc: (value: Status) => { + if (BigNumber.from(value).eq(0)) { + return "red"; + } else if (BigNumber.from(value).lt(5)) { + return "yellow"; + } else { + return "green"; + } + }, + tooltip: + "The amount of pending proposed blocks that have not been proven on the TaikoL1 smart contract.", + }, + { + statusFunc: getEthDeposits, + watchStatusFunc: null, + provider: config.l1Provider, + contractAddress: config.l1TaikoAddress, + header: "ETH Deposits", + intervalInMs: 20000, + colorFunc: (value: Status) => { + if (BigNumber.from(value).eq(0)) { + return "green"; + } else if (BigNumber.from(value).lt(32)) { + return "yellow"; + } else { + return "red"; + } + }, + tooltip: "The number of pending ETH deposits for L1 => L2", + }, + { + statusFunc: getNextEthDepositToProcess, + watchStatusFunc: null, + provider: config.l1Provider, + contractAddress: config.l1TaikoAddress, + header: "Next ETH Deposit", + intervalInMs: 20000, + colorFunc: (value: Status) => { + return "green"; + }, + tooltip: "The next ETH deposit that will be processed", + }, + { + statusFunc: getGasPrice, + watchStatusFunc: null, + provider: config.l2Provider, + contractAddress: "", + header: "Gas Price (gwei)", + intervalInMs: 20000, + colorFunc: (value: Status) => { + return "green"; + }, + tooltip: + "The current recommended gas price for a transaction on Layer 2.", + }, + ]; + + try { + indicators.push({ + statusFunc: getBlockFee, + watchStatusFunc: null, + provider: config.l1Provider, + contractAddress: config.l1TaikoAddress, + header: "Block Fee", + intervalInMs: 15000, + colorFunc: function (status: Status) { + return "green"; // todo: whats green, yellow, red? + }, + tooltip: + "The current fee to propose a block to the TaikoL1 smart contract.", + }); + indicators.push({ + statusFunc: getProofReward, + watchStatusFunc: null, + provider: config.l1Provider, + contractAddress: config.l1TaikoAddress, + header: "Proof Reward", + intervalInMs: 15000, + colorFunc: function (status: Status) { + return "green"; // todo: whats green, yellow, red? + }, + tooltip: + "The current reward for successfully submitting a proof for a proposed block on the TaikoL1 smart contract.", + }); + indicators.push({ + provider: config.l1Provider, + contractAddress: config.l1TaikoAddress, + header: "Latest Proof", + intervalInMs: 0, + status: "0", + watchStatusFunc: async ( + provider: ethers.providers.JsonRpcProvider, + address: string, + onEvent: (value: Status) => void + ) => { + const contract = new Contract(address, TaikoL1, provider); + contract.on( + "BlockProven", + ( + id, + parentHash, + blockHash, + signalRoot, + prover, + provenAt, + ...args ) => { - const contract = new Contract(address, TaikoL1, provider); - contract.on( - "BlockProven", - ( - id, - parentHash, - blockHash, - signalRoot, - prover, - provenAt, - ...args - ) => { - // ignore oracle prover - if (prover.toLowerCase() !== oracleProverAddress.toLowerCase()) { - onEvent(new Date().getTime().toString()); - } - } - ); - }, - colorFunc: function (status: Status) { - return "green"; // todo: whats green, yellow, red? - }, - tooltip: "The most recent block proof submitted on TaikoL1 contract.", - }); - - statusIndicators.push({ - provider: l1Provider, - contractAddress: l1TaikoAddress, - statusFunc: async ( - provider: ethers.providers.JsonRpcProvider, - address: string - ) => { - const stateVars = await getStateVariables(provider, address); - return `${stateVars.proofTimeIssued.toNumber() / 1000} seconds`; - }, - colorFunc: function (status: Status) { - return "green"; // todo: whats green, yellow, red? - }, - header: "Average Proof Time", - intervalInMs: 5 * 1000, - tooltip: - "The current average proof time, updated when a block is successfully proven.", - }); - } catch (e) { - console.error(e); - } - -} \ No newline at end of file + // ignore oracle prover + if ( + prover.toLowerCase() !== config.oracleProverAddress.toLowerCase() + ) { + onEvent(new Date().getTime().toString()); + } + } + ); + }, + colorFunc: function (status: Status) { + return "green"; // todo: whats green, yellow, red? + }, + tooltip: "The most recent block proof submitted on TaikoL1 contract.", + }); + + indicators.push({ + provider: config.l1Provider, + contractAddress: config.l1TaikoAddress, + statusFunc: async ( + provider: ethers.providers.JsonRpcProvider, + address: string + ) => { + const stateVars = await getStateVariables(provider, address); + return `${stateVars.proofTimeIssued.toNumber() / 1000} seconds`; + }, + colorFunc: function (status: Status) { + return "green"; // todo: whats green, yellow, red? + }, + header: "Average Proof Time", + intervalInMs: 5 * 1000, + tooltip: + "The current average proof time, updated when a block is successfully proven.", + }); + } catch (e) { + console.error(e); + } + + return indicators; +} diff --git a/packages/status-page/src/utils/initConfig.ts b/packages/status-page/src/utils/initConfig.ts new file mode 100644 index 0000000000..d7625fbe48 --- /dev/null +++ b/packages/status-page/src/utils/initConfig.ts @@ -0,0 +1,48 @@ +import { ethers } from "ethers"; +import { Layer } from "../domain/layer"; + +export function initConfig(layer: Layer) { + const l1Provider = new ethers.providers.JsonRpcProvider( + layer === Layer.Two + ? import.meta.env.VITE_L1_RPC_URL + : import.meta.env.VITE_L2_RPC_URL + ); + const l2Provider = new ethers.providers.JsonRpcProvider( + layer === Layer.Two + ? import.meta.env.VITE_L2_RPC_URL + : import.meta.env.VITE_L3_RPC_URL + ); + + const l1TaikoAddress = + layer === Layer.Two + ? import.meta.env.VITE_L2_TAIKO_L1_ADDRESS + : import.meta.env.VITE_L3_TAIKO_L1_ADDRESS; + const l2TaikoAddress = + layer === Layer.Two + ? import.meta.env.VITE_L2_TAIKO_L2_ADDRESS + : import.meta.env.VITE_L3_TAIKO_L2_ADDRESS; + const l1ExplorerUrl = import.meta.env.VITE_L1_EXPLORER_URL; + const l2ExplorerUrl = + layer === Layer.Two + ? import.meta.env.VITE_L2_EXPLORER_URL + : import.meta.env.VITE_L3_EXPLORER_URL; + const feeTokenSymbol = import.meta.env.VITE_FEE_TOKEN_SYMBOL || "TKO"; + const oracleProverAddress = + import.meta.env.ORACLE_PROVER_ADDRESS || + "0x1567CDAb5F7a69154e61A16D8Ff5eE6A3e991b39"; + const eventIndexerApiUrl = + layer === Layer.Two + ? import.meta.env.VITE_L2_EVENT_INDEXER_API_URL + : import.meta.env.VITE_L3_EVENT_INDEXER_API_URL; + return { + l1Provider, + l2Provider, + l1TaikoAddress, + l2TaikoAddress, + l1ExplorerUrl, + l2ExplorerUrl, + feeTokenSymbol, + oracleProverAddress, + eventIndexerApiUrl, + }; +} From d67ddc542dc255befa9e8dec483b9fcf8e8c6147 Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Tue, 16 May 2023 14:26:14 -0700 Subject: [PATCH 3/4] defualt to l2 --- packages/status-page/src/store/layer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/status-page/src/store/layer.ts b/packages/status-page/src/store/layer.ts index 3a8950dde7..c8842876b0 100644 --- a/packages/status-page/src/store/layer.ts +++ b/packages/status-page/src/store/layer.ts @@ -1,4 +1,4 @@ import { Layer } from "../domain/layer"; import { writable } from "svelte/store"; -export const layer = writable(Layer.Three); +export const layer = writable(Layer.Two); From 45bda5926148bbe74d121911b1f49f31bb0e6f76 Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Tue, 16 May 2023 14:46:19 -0700 Subject: [PATCH 4/4] rm console log --- packages/status-page/src/components/StatusIndicator.svelte | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/status-page/src/components/StatusIndicator.svelte b/packages/status-page/src/components/StatusIndicator.svelte index 1587826bd2..6164708b82 100644 --- a/packages/status-page/src/components/StatusIndicator.svelte +++ b/packages/status-page/src/components/StatusIndicator.svelte @@ -52,7 +52,6 @@ } else { statusValue = 0; } - console.log(header, statusValue); } catch (e) { console.error(header, e); }