diff --git a/packages/status-page/.default.env b/packages/status-page/.default.env index 78c288f875..c69f749f49 100644 --- a/packages/status-page/.default.env +++ b/packages/status-page/.default.env @@ -3,6 +3,6 @@ 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_TOKENOMICS_ENABLED=false VITE_L1_EXPLORER_URL="https://l1explorer.a1.taiko.xyz" -VITE_L2_EXPLORER_URL="https://l2explorer.a1.taiko.xyz" \ No newline at end of file +VITE_L2_EXPLORER_URL="https://l2explorer.a1.taiko.xyz" +VITE_FEE_TOKEN_SYMBOL="TKO"; \ No newline at end of file diff --git a/packages/status-page/src/App.svelte b/packages/status-page/src/App.svelte index 74f5e8fbaf..58088e0f6f 100644 --- a/packages/status-page/src/App.svelte +++ b/packages/status-page/src/App.svelte @@ -14,9 +14,6 @@ const l2Provider = new ethers.providers.JsonRpcProvider( import.meta.env.VITE_L2_RPC_URL ); - const l2BootnodeProvider = new ethers.providers.JsonRpcProvider( - import.meta.env.VITE_L2_BOOTNODE_URL - ); const routes = { "/": wrap({ @@ -26,9 +23,9 @@ l1TaikoAddress: import.meta.env.VITE_TAIKO_L1_ADDRESS, l2Provider: l2Provider, l2TaikoAddress: import.meta.env.VITE_TAIKO_L2_ADDRESS, - isTokenomicsEnabled: import.meta.env.VITE_TOKENOMICS_ENABLED, l1ExplorerUrl: import.meta.env.VITE_L1_EXPLORER_URL, l2ExplorerUrl: import.meta.env.VITE_L2_EXPLORER_URL, + feeTokenSymbol: import.meta.env.FEE_TOKEN_SYMBOL || "TKO", }, userData: {}, }), diff --git a/packages/status-page/src/components/StatusIndicator.svelte b/packages/status-page/src/components/StatusIndicator.svelte index bbb0a1f233..a9ffd9b982 100644 --- a/packages/status-page/src/components/StatusIndicator.svelte +++ b/packages/status-page/src/components/StatusIndicator.svelte @@ -3,7 +3,7 @@ import { displayStatusValue } from "../utils/displayStatusValue"; import { onDestroy, onMount } from "svelte"; import Loader from "../components/Loader.svelte"; - import type Status from "../domain/status"; + import type { Status } from "../domain/status"; import { fade } from "svelte/transition"; import Tooltip from "./Tooltip.svelte"; import TooltipModal from "./TooltipModal.svelte"; @@ -16,6 +16,8 @@ contractAddress: string ) => Promise; + export let status: Status; + export let watchStatusFunc: ( provider: ethers.providers.JsonRpcProvider, contractAddress: string, @@ -40,7 +42,13 @@ onMount(async () => { try { - statusValue = await statusFunc(provider, contractAddress); + if (status) { + statusValue = status; + } else if (statusFunc) { + statusValue = await statusFunc(provider, contractAddress); + } else { + statusValue = 0; + } } catch (e) { console.error(e); } diff --git a/packages/status-page/src/domain/status.ts b/packages/status-page/src/domain/status.ts index 64fc47b822..3e675488ea 100644 --- a/packages/status-page/src/domain/status.ts +++ b/packages/status-page/src/domain/status.ts @@ -1,3 +1,24 @@ +import type { ethers } from "ethers"; + type Status = string | number | boolean; -export default Status; +type StatusIndicatorProp = { + statusFunc?: ( + provider: ethers.providers.JsonRpcProvider, + contractAddress: string + ) => Promise; + watchStatusFunc?: ( + provider: ethers.providers.JsonRpcProvider, + contractAddress: string, + onEvent: (value: Status) => void + ) => void; + provider: ethers.providers.JsonRpcProvider; + contractAddress: string; + header: string; + intervalInMs: number; + colorFunc: (value: Status) => string; + onClick?: (value: Status) => void; + tooltip: string; + status?: Status; +}; +export { Status, StatusIndicatorProp }; diff --git a/packages/status-page/src/pages/home/Home.svelte b/packages/status-page/src/pages/home/Home.svelte index 98d4750400..d7bcf3724f 100644 --- a/packages/status-page/src/pages/home/Home.svelte +++ b/packages/status-page/src/pages/home/Home.svelte @@ -14,17 +14,19 @@ import { getQueuedTransactions } from "../../utils/getQueuedTransactions"; import { onMount } from "svelte"; import { getProofReward } from "../../utils/getProofReward"; - import type Status from "../../domain/status"; + import type { Status, StatusIndicatorProp } from "../../domain/status"; + import { getConfig } from "../../utils/getConfig"; + import { getStateVariables } from "../../utils/getStateVariables"; export let l1Provider: ethers.providers.JsonRpcProvider; export let l1TaikoAddress: string; export let l2Provider: ethers.providers.JsonRpcProvider; export let l2TaikoAddress: string; - export let isTokenomicsEnabled: boolean = false; export let l1ExplorerUrl: string; export let l2ExplorerUrl: string; + export let feeTokenSymbol: string; - const statusIndicators = [ + let statusIndicators: StatusIndicatorProp[] = [ { statusFunc: getLatestSyncedHeader, watchStatusFunc: watchHeaderSynced, @@ -57,17 +59,6 @@ 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: getProposers, - // watchStatusFunc: null, - // provider: l1Provider, - // contractAddress: l1TaikoAddress, - // header: "Unique Proposers", - // intervalInMs: 0, - // colorFunc: (value: Status) => { - // return "green"; - // }, - // }, { statusFunc: getPendingTransactions, watchStatusFunc: null, @@ -178,36 +169,105 @@ colorFunc: (value: Status) => { return "green"; }, - tooltip: "The current recommended gas price for a transaction on Layer 2.", + tooltip: + "The current recommended gas price for a transaction on Layer 2.", }, ]; - onMount(() => { - if (isTokenomicsEnabled) { + onMount(async () => { + try { + const config = await getConfig(l1Provider, l1TaikoAddress); + if (!Object.hasOwn(config, "enableTokenomics")) return; + if (config.enableTokenomics) { + statusIndicators.push({ + statusFunc: getBlockFee, + watchStatusFunc: null, + provider: l1Provider, + contractAddress: l1TaikoAddress, + header: "Block Fee", + intervalInMs: 15000, + colorFunc: null, + 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: null, + tooltip: + "The current reward for successfully submitting a proof for a proposed block on the TaikoL1 smart contract.", + }); + } + } catch (e) { + console.error(e); + } + + try { + const stateVars = await getStateVariables(l1Provider, l1TaikoAddress); + + // TODO: remove. this check prevents this code from running before we deploy next testnet + // since the state vars have had large changes. + if (stateVars.length < 10) { + return; + } + + statusIndicators.push({ + status: stateVars[4], + provider: l1Provider, + contractAddress: l1TaikoAddress, + header: "Latest Proposal", + intervalInMs: 0, + colorFunc: function (status: Status) { + return "green"; // todo: whats green, yellow, red? + }, + tooltip: "The most recent block proposal on TaikoL1 contract.", + }); + statusIndicators.push({ - statusFunc: getBlockFee, - watchStatusFunc: null, + status: stateVars[9], provider: l1Provider, contractAddress: l1TaikoAddress, - header: "Block Fee", - intervalInMs: 15000, + header: "Average Proof Time", + intervalInMs: 0, colorFunc: null, tooltip: - "The current fee to propose a block to the TaikoL1 smart contract.", + "The current average proof time, updated when a block is successfully proven.", }); statusIndicators.push({ - statusFunc: getProofReward, - watchStatusFunc: null, + status: stateVars[9], provider: l1Provider, contractAddress: l1TaikoAddress, - header: "Proof Reward", - intervalInMs: 15000, - colorFunc: null, + header: "Average Block Time", + intervalInMs: 0, + 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.", + "The current average block time, updated when a block is successfully proposed.", }); + + statusIndicators.push({ + status: `${ethers.utils.parseEther(stateVars[3])} ${feeTokenSymbol}`, + provider: l1Provider, + contractAddress: l1TaikoAddress, + header: "Fee Base", + intervalInMs: 0, + colorFunc: function (status: Status) { + return "green"; // todo: whats green, yellow, red? + }, + tooltip: "The current fee base for proposing and rewarding", + }); + } catch (e) { + console.error(e); } + + statusIndicators = statusIndicators; }); @@ -228,6 +288,7 @@ onClick={statusIndicator.onClick} intervalInMs={statusIndicator.intervalInMs} tooltip={statusIndicator.tooltip} + status={statusIndicator.status} /> {/each} diff --git a/packages/status-page/src/utils/getConfig.ts b/packages/status-page/src/utils/getConfig.ts new file mode 100644 index 0000000000..a66be0994c --- /dev/null +++ b/packages/status-page/src/utils/getConfig.ts @@ -0,0 +1,10 @@ +import { BigNumber, Contract, ethers } from "ethers"; +import TaikoL1 from "../constants/abi/TaikoL1"; + +export const getConfig = async ( + provider: ethers.providers.JsonRpcProvider, + contractAddress: string +) => { + const contract: Contract = new Contract(contractAddress, TaikoL1, provider); + return await contract.getConfig(); +}; diff --git a/packages/status-page/src/utils/getStateVariables.ts b/packages/status-page/src/utils/getStateVariables.ts new file mode 100644 index 0000000000..8d38c22430 --- /dev/null +++ b/packages/status-page/src/utils/getStateVariables.ts @@ -0,0 +1,10 @@ +import { BigNumber, Contract, ethers } from "ethers"; +import TaikoL1 from "../constants/abi/TaikoL1"; + +export const getStateVariables = async ( + provider: ethers.providers.JsonRpcProvider, + contractAddress: string +) => { + const contract: Contract = new Contract(contractAddress, TaikoL1, provider); + return await contract.getStateVariables(); +};