Documentation Index
Fetch the complete documentation index at: https://voltaire.tevm.sh/llms.txt
Use this file to discover all available pages before exploring further.
Try it Live
Run EventLog examples in the interactive playground
Sort Order
Primary: Block Number
Logs sorted by block number ascending (earliest blocks first):
import { EventLog } from 'tevm';
const sorted = EventLog.sortLogs([
{ blockNumber: 18000003n, logIndex: 0 },
{ blockNumber: 18000001n, logIndex: 0 },
{ blockNumber: 18000002n, logIndex: 0 },
]);
// Result:
// 1. blockNumber: 18000001n
// 2. blockNumber: 18000002n
// 3. blockNumber: 18000003n
Secondary: Log Index
Within same block, sorted by log index ascending:
import { EventLog } from 'tevm';
const sorted = EventLog.sortLogs([
{ blockNumber: 18000000n, logIndex: 10 },
{ blockNumber: 18000000n, logIndex: 2 },
{ blockNumber: 18000000n, logIndex: 5 },
]);
// Result:
// 1. logIndex: 2
// 2. logIndex: 5
// 3. logIndex: 10
Missing Values
Logs without blockNumber or logIndex treated as 0:
import { EventLog } from 'tevm';
const sorted = EventLog.sortLogs([
{ blockNumber: 100n, logIndex: 5 },
{ /* no blockNumber, no logIndex */ },
{ blockNumber: 50n, logIndex: 2 },
]);
// Result:
// 1. No blockNumber (treated as 0)
// 2. blockNumber: 50n, logIndex: 2
// 3. blockNumber: 100n, logIndex: 5
Usage Patterns
Chronological Processing
import { EventLog } from 'tevm';
// Sort before processing
const sorted = EventLog.sortLogs(allLogs);
for (const log of sorted) {
console.log(`Block ${log.blockNumber}, Log ${log.logIndex}`);
// Process in blockchain order
}
Combining with Filtering
import { EventLog, Address, Hash } from 'tevm';
const TRANSFER_SIG = Hash('0xddf252ad...');
// Filter then sort
const transfers = EventLog.filterLogs(allLogs, {
address: usdcAddress,
topics: [TRANSFER_SIG],
});
const sorted = EventLog.sortLogs(transfers);
console.log(`Found ${sorted.length} transfers in chronological order`);
Finding Latest Log
import { EventLog } from 'tevm';
const sorted = EventLog.sortLogs(logs);
const latest = sorted[sorted.length - 1];
if (latest) {
console.log(`Latest log: block ${latest.blockNumber}, index ${latest.logIndex}`);
}
Finding Earliest Log
import { EventLog } from 'tevm';
const sorted = EventLog.sortLogs(logs);
const earliest = sorted[0];
if (earliest) {
console.log(`Earliest log: block ${earliest.blockNumber}, index ${earliest.logIndex}`);
}
Grouping by Block
import { EventLog } from 'tevm';
const sorted = EventLog.sortLogs(allLogs);
// Group by block
const byBlock = new Map<bigint, EventLog[]>();
for (const log of sorted) {
const blockNum = log.blockNumber ?? 0n;
if (!byBlock.has(blockNum)) {
byBlock.set(blockNum, []);
}
byBlock.get(blockNum)!.push(log);
}
// Process each block's logs (already sorted by logIndex)
for (const [blockNumber, blockLogs] of byBlock) {
console.log(`\nBlock ${blockNumber}: ${blockLogs.length} logs`);
for (const log of blockLogs) {
console.log(` Log ${log.logIndex}: ${Address.toHex(log.address)}`);
}
}
Time-Ordered Event Replay
import { EventLog } from 'tevm';
function replayEvents(logs: EventLog[]): void {
const sorted = EventLog.sortLogs(logs);
let currentBlock = 0n;
for (const log of sorted) {
if (log.blockNumber && log.blockNumber > currentBlock) {
console.log(`\n=== Block ${log.blockNumber} ===`);
currentBlock = log.blockNumber;
}
// Decode and replay event
const sig = log.getTopic0();
if (sig) {
console.log(` Event: ${Hash.toHex(sig).slice(0, 10)}...`);
// ... decode and apply state changes
}
}
}
replayEvents(allLogs);
Detecting Missing Logs
import { EventLog } from 'tevm';
function detectGaps(logs: EventLog[]): void {
const sorted = EventLog.sortLogs(logs);
for (let i = 1; i < sorted.length; i++) {
const prev = sorted[i - 1];
const curr = sorted[i];
const prevBlock = prev.blockNumber ?? 0n;
const currBlock = curr.blockNumber ?? 0n;
if (currBlock === prevBlock) {
// Same block: check log index
const prevIndex = prev.logIndex ?? 0;
const currIndex = curr.logIndex ?? 0;
if (currIndex > prevIndex + 1) {
console.log(`Gap in block ${currBlock}: missing log indices ${prevIndex + 1} to ${currIndex - 1}`);
}
}
}
}
import { EventLog } from 'tevm';
function paginateLogs(logs: EventLog[], page: number, pageSize: number) {
const sorted = EventLog.sortLogs(logs);
const start = page * pageSize;
const end = start + pageSize;
return {
logs: sorted.slice(start, end),
total: sorted.length,
page,
pageSize,
hasMore: end < sorted.length,
hasLess: start > 0,
};
}
const result = paginateLogs(allLogs, 0, 100);
console.log(`Page 1: ${result.logs.length} logs`);
console.log(`Total: ${result.total} logs`);
console.log(`Has more: ${result.hasMore}`);
Implementation
Sort algorithm:
- Compare block numbers (ascending)
- If equal, compare log indices (ascending)
- Treat undefined as 0
// Equivalent implementation
function sortLogs(logs: readonly EventLog[]): EventLog[] {
return [...logs].sort((a, b) => {
const blockA = a.blockNumber ?? 0n;
const blockB = b.blockNumber ?? 0n;
if (blockA !== blockB) {
return blockA < blockB ? -1 : 1;
}
const indexA = a.logIndex ?? 0;
const indexB = b.logIndex ?? 0;
return indexA - indexB;
});
}
- Time complexity: O(n log n)
- Space complexity: O(n) (creates new array)
- Stable sort: Maintains relative order of equal elements
For large arrays, sort once and reuse:
import { EventLog } from 'tevm';
// Sort once
const sorted = EventLog.sortLogs(allLogs);
// Reuse sorted array for multiple operations
const first100 = sorted.slice(0, 100);
const last100 = sorted.slice(-100);
const middle = sorted.slice(1000, 2000);
See Also