> ## 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.

# EventLog.sortLogs

> Sort logs by block number then log index

<Card title="Try it Live" icon="play" href="https://playground.tevm.sh?example=primitives/event-log.ts">
  Run EventLog examples in the interactive playground
</Card>

<Tabs />

## Sort Order

### Primary: Block Number

Logs sorted by block number ascending (earliest blocks first):

```typescript theme={null}
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:

```typescript theme={null}
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:

```typescript theme={null}
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

```typescript theme={null}
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

```typescript theme={null}
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

```typescript theme={null}
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

```typescript theme={null}
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

```typescript theme={null}
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

```typescript theme={null}
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

```typescript theme={null}
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}`);
      }
    }
  }
}
```

### Pagination

```typescript theme={null}
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:

1. Compare block numbers (ascending)
2. If equal, compare log indices (ascending)
3. Treat undefined as 0

```typescript theme={null}
// 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;
  });
}
```

## Performance

* **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:

```typescript theme={null}
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

* [filterLogs](/primitives/eventlog/filterlogs) - Filter before sorting
* [isRemoved](/primitives/eventlog/isremoved) - Filter removed logs
* [Fundamentals](/primitives/eventlog/fundamentals) - Log ordering and blocks
