Skip to content

Instantly share code, notes, and snippets.

@lucadonnoh
Created April 11, 2025 13:18
Show Gist options
  • Select an option

  • Save lucadonnoh/9bdd5efeb0c2a1397131c36e0b46d7fe to your computer and use it in GitHub Desktop.

Select an option

Save lucadonnoh/9bdd5efeb0c2a1397131c36e0b46d7fe to your computer and use it in GitHub Desktop.
calculate time to inclusion for op stack
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