-
-
Save lucadonnoh/9bdd5efeb0c2a1397131c36e0b46d7fe to your computer and use it in GitHub Desktop.
calculate time to inclusion for op stack
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import axios from 'axios'; | |
| // ANSI color codes for prettier logs. | |
| const RESET = "\x1b[0m"; | |
| const YELLOW = "\x1b[33m"; | |
| const GREEN = "\x1b[32m"; | |
| const BLUE = "\x1b[34m"; | |
| const CYAN = "\x1b[36m"; | |
| const RED = "\x1b[31m"; | |
| // --- Type Definitions --- | |
| interface Block { | |
| hash: string; | |
| number: number; // Block number as a number | |
| parentHash: string; | |
| timestamp: number; // Unix epoch seconds | |
| } | |
| interface SyncStatus { | |
| current_l1: Block; // Latest L1 block (used as inclusion time) | |
| current_l1_finalized: Block; | |
| head_l1: Block; | |
| safe_l1: Block; | |
| finalized_l1: Block; | |
| unsafe_l2: Block; | |
| safe_l2: Block; // L2 block that is marked safe (its timestamp is production time) | |
| finalized_l2: Block; | |
| pending_safe_l2: Block; | |
| cross_unsafe_l2: Block; | |
| local_safe_l2: Block; | |
| } | |
| interface TimeToInclusion { | |
| l2Block: number; | |
| timeToInclusion: number; // in seconds | |
| } | |
| // --- Global Variables --- | |
| let previousSafeL2: number | undefined = undefined; | |
| const timeToInclusionList: TimeToInclusion[] = []; | |
| /** | |
| * Converts seconds to a human-readable format "X min Y sec" when applicable. | |
| * | |
| * @param seconds number of seconds | |
| * @returns a formatted string (e.g., "1 min 2.50 sec" or "45.00 sec") | |
| */ | |
| function formatTime(seconds: number): string { | |
| const minutes = Math.floor(seconds / 60); | |
| const secs = seconds - minutes * 60; | |
| return minutes > 0 ? `${minutes} min ${secs.toFixed(2)} sec` : `${secs.toFixed(2)} sec`; | |
| } | |
| /** | |
| * Computes min, max, and average values from an array of numbers. | |
| */ | |
| function calculateStatistics(delays: number[]): { min: number; max: number; avg: number } { | |
| const min = Math.min(...delays); | |
| const max = Math.max(...delays); | |
| const sum = delays.reduce((a, b) => a + b, 0); | |
| const avg = delays.length > 0 ? sum / delays.length : 0; | |
| return { min, max, avg }; | |
| } | |
| /** | |
| * Fetches sync status from the L1 API using the JSON-RPC method "optimism_syncStatus". | |
| */ | |
| async function fetchSyncStatus(): Promise<SyncStatus> { | |
| const payload = { | |
| jsonrpc: "2.0", | |
| method: "optimism_syncStatus", | |
| params: [], | |
| id: 1, | |
| }; | |
| try { | |
| const response = await axios.post('<OP_RPC_URL>', payload, { | |
| headers: { "Content-Type": "application/json" }, | |
| }); | |
| const result: SyncStatus = response.data.result || response.data; | |
| // Log key details with color and clear formatting. | |
| console.log("------------------------------------------------------"); | |
| console.log(`${CYAN}Fetched SyncStatus:${RESET}`); | |
| console.log(`${YELLOW} Safe L2 Block:${RESET} | |
| • Hash : ${result.safe_l2.hash} | |
| • Number : ${result.safe_l2.number} | |
| • Prod. Time: ${result.safe_l2.timestamp}`); | |
| // also print current l1 block | |
| console.log(`${YELLOW} Current L1 Block:${RESET} | |
| • Hash : ${result.current_l1.hash} | |
| • Number : ${result.current_l1.number} | |
| • Time : ${result.current_l1.timestamp}`); | |
| console.log("------------------------------------------------------"); | |
| return result; | |
| } catch (error) { | |
| console.error("Error fetching sync status:", error); | |
| throw error; | |
| } | |
| } | |
| /** | |
| * Monitors safe L2 blocks and computes the time-to-inclusion statistics for any new safe blocks. | |
| * | |
| * - The safe_l2 block's timestamp is the production time. | |
| * - The current L1 timestamp is used as the inclusion time for new safe blocks. | |
| * - Assumes an L2 block time of 2 seconds. | |
| */ | |
| async function monitorSafeL2Blocks(): Promise<void> { | |
| try { | |
| const syncStatus = await fetchSyncStatus(); | |
| const currentL1 = syncStatus.current_l1; | |
| const currentL1Timestamp = currentL1.timestamp; | |
| const safeL2Block = syncStatus.safe_l2; | |
| const currentSafeL2Number = safeL2Block.number; | |
| const safeL2ProductionTime = safeL2Block.timestamp; | |
| console.log(`${BLUE}Current Safe L2 Block:${RESET} ${currentSafeL2Number} (Produced at: ${safeL2ProductionTime})`); | |
| console.log(`${BLUE}Current L1 Head (Inclusion Time Candidate):${RESET} ${currentL1Timestamp}`); | |
| // On initial poll, just initialize the tracker. | |
| if (previousSafeL2 === undefined) { | |
| previousSafeL2 = currentSafeL2Number; | |
| console.log(`${GREEN}Initial poll:${RESET} Tracker set. No time-to-inclusion calculated.`); | |
| return; | |
| } | |
| // If there are no new safe blocks, print a minimal message. | |
| if (currentSafeL2Number <= previousSafeL2) { | |
| console.log(`${GREEN}No new safe L2 blocks detected.${RESET}\n`); | |
| return; | |
| } | |
| // For new safe blocks, compute the delays. | |
| console.log(`${RED}New safe L2 blocks detected:${RESET} Blocks ${previousSafeL2 + 1} to ${currentSafeL2Number}`); | |
| console.log(`${GREEN}Using current L1 timestamp as inclusion time: ${currentL1Timestamp}${RESET}\n`); | |
| const delays: number[] = []; | |
| for (let block = previousSafeL2 + 1; block <= currentSafeL2Number; block++) { | |
| // Estimated production time for each block, assuming 2s per block. | |
| const estimatedL2ProductionTime = safeL2ProductionTime - ((currentSafeL2Number - block) * 2); | |
| const inclusionDelay = currentL1Timestamp - estimatedL2ProductionTime; | |
| delays.push(inclusionDelay); | |
| // Optionally store detailed results. | |
| timeToInclusionList.push({ | |
| l2Block: block, | |
| timeToInclusion: inclusionDelay, | |
| }); | |
| } | |
| // Compute statistics for the delays. | |
| const { min, max, avg } = calculateStatistics(delays); | |
| console.log("------------------------------------------------------"); | |
| console.log(`${CYAN}Batch Statistics for New Safe L2 Blocks:${RESET}`); | |
| console.log(` Minimum Time-to-Inclusion: ${formatTime(min)}`); | |
| console.log(` Maximum Time-to-Inclusion: ${formatTime(max)}`); | |
| console.log(` Average Time-to-Inclusion: ${formatTime(avg)}`); | |
| console.log("------------------------------------------------------\n"); | |
| // Update tracker. | |
| previousSafeL2 = currentSafeL2Number; | |
| console.log(`${BLUE}Tracker Updated:${RESET} previousSafeL2 set to ${previousSafeL2}\n`); | |
| } catch (error) { | |
| console.error("Error during monitoring:", error); | |
| } | |
| } | |
| /** | |
| * Starts the monitoring loop by calling monitorSafeL2Blocks immediately, | |
| * then every 3 seconds. | |
| */ | |
| function startMonitoring() { | |
| monitorSafeL2Blocks(); | |
| setInterval(monitorSafeL2Blocks, 3000); | |
| } | |
| // --- Start the Monitoring Process --- | |
| startMonitoring(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment