Skip to content

Transaction Pool

The Transaction Pool (TxPool) is a crucial component that manages pending transactions before they're included in blocks. It handles transaction ordering, replacement, validation, and lifecycle management.

Quick Start

import { createTevmNode } from 'tevm'
import { createAddress } from 'tevm/address'
import { createImpersonatedTx } from 'tevm/tx'
 
// Initialize node and get txpool
const node = createTevmNode()
const txPool = await node.getTxPool()
 
// Create and add a transaction
const tx = createImpersonatedTx({
  impersonatedAddress: createAddress('0x1234...'),
  to: createAddress('0x2345...'),
  value: 1000000000000000000n, // 1 ETH
  gasLimit: 21000n,
  maxFeePerGas: 20000000000n,
  maxPriorityFeePerGas: 20000000000n,
  nonce: 0n,
})
 
await txPool.addUnverified(tx)

Key Features

  • ๐Ÿ” Transaction Validation - Comprehensive validation including nonce, balance, and gas checks
  • ๐Ÿ”„ Transaction Replacement - Replace pending transactions with higher gas price versions
  • ๐Ÿ“Š Nonce Ordering - Maintains correct transaction sequence per account
  • ๐Ÿงน Automatic Pruning - Removes old transactions to prevent memory bloat
  • โšก Performance Optimized - Efficient handling of large transaction volumes

Core Concepts

Pool Limits

The TxPool enforces several limits to ensure stable operation:

const LIMITS = {
  MAX_POOL_SIZE: 5000,           // Maximum total transactions
  MAX_TXS_PER_ACCOUNT: 100,      // Maximum per account
  MIN_GAS_PRICE: 100000000n,     // 0.1 GWei minimum
  TX_MAX_DATA_SIZE: 128 * 1024,  // 128KB max transaction size
}

Transaction Lifecycle

  1. Addition - Transactions enter the pool via add() or addUnverified()
  2. Validation - Optional checks for nonce, balance, and gas parameters
  3. Storage - Valid transactions are stored and ordered by nonce
  4. Pruning - Old transactions are removed after POOLED_STORAGE_TIME_LIMIT (20 minutes)

Detailed Usage

Adding Transactions

Two methods for adding transactions, each with different validation levels:

// Method 1: With full validation
try {
  await txPool.add(tx)
} catch (error) {
  if (error.message.includes('insufficient balance')) {
    console.error('Account has insufficient funds')
  }
}
 
// Method 2: Without validation (faster)
await txPool.addUnverified(tx)

Transaction Replacement

Replace a pending transaction by submitting a new one with the same nonce and higher gas price:

const originalTx = createImpersonatedTx({
  // ... base transaction params ...
  maxFeePerGas: 20000000000n,
  nonce: 0n,
})
 
const replacementTx = createImpersonatedTx({
  // ... same params as original ...
  maxFeePerGas: 30000000000n, // At least 10% higher
  nonce: 0n, // Same nonce as original
})
 
await txPool.addUnverified(originalTx)
await txPool.addUnverified(replacementTx) // Replaces originalTx

Note: Replacement transactions must increase gas price by at least MIN_GAS_PRICE_BUMP_PERCENT (10%)

Querying Transactions

// Get transactions by sender
const senderTxs = await txPool.getBySenderAddress(senderAddress)
 
// Get transactions by hash
const txHashes = [hash1, hash2]
const specificTxs = txPool.getByHash(txHashes)
 
// Get ordered transactions for mining
const orderedTxs = await txPool.txsByPriceAndNonce({
  baseFee: currentBaseFee,
  allowedBlobs: 6, // For EIP-4844
})

Block Processing

When new blocks are added, update the pool:

import { mineHandler } from 'tevm/actions'
 
// Mine new blocks
await mineHandler(node)()
 
// Remove included transactions
txPool.removeNewBlockTxs(newBlocks)

Advanced Features

Pool Management

// Start transaction processing
txPool.start()
 
// Stop processing (but keep transactions)
txPool.stop()
 
// Clear all transactions
txPool.close()
 
// Manual cleanup of old transactions
txPool.cleanup()

Transaction Types

The pool supports all Ethereum transaction types:

  • Legacy Transactions
  • EIP-2930 (Access Lists)
  • EIP-1559 (Fee Market)
  • EIP-4844 (Blob Transactions)
  • Tevm Impersonated Transactions

Best Practices

1. Transaction Creation

Always use createImpersonatedTx with proper types:

const tx = createImpersonatedTx({
  impersonatedAddress: createAddress(address),
  to: createAddress(recipient),
  value: parseEther('1'), // Use helper functions for values
  gasLimit: 21000n,
  maxFeePerGas: gweiToWei('20'),
  maxPriorityFeePerGas: gweiToWei('2'),
  nonce: 0n,
})

2. Error Handling

Implement comprehensive error handling:

try {
  await txPool.add(tx)
} catch (error) {
  switch (true) {
    case error.message.includes('insufficient balance'):
      // Handle balance error
      break
    case error.message.includes('nonce too low'):
      // Handle nonce error
      break
    case error.message.includes('gas price too low'):
      // Handle gas price error
      break
    default:
      // Handle unknown errors
  }
}

3. Performance Optimization

  • Use addUnverified for bulk operations
  • Implement proper cleanup cycles
  • Monitor pool size and transaction age

4. Memory Management

// Regular cleanup cycle
setInterval(() => {
  txPool.cleanup()
}, 5 * 60 * 1000) // Every 5 minutes
 
// Monitor pool size
const poolSize = txPool.txsInPool
if (poolSize > MAX_POOL_SIZE * 0.8) {
  console.warn('Pool approaching capacity')
}

API Reference

TxPool Class

class TxPool {
  constructor(options: { vm: Vm })
  async add(tx: Transaction): Promise<void>
  async addUnverified(tx: Transaction): Promise<void>
  async getBySenderAddress(address: Address): Promise<TxPoolObject[]>
  getByHash(hashes: Uint8Array[]): Transaction[]
  removeByHash(hash: string): void
  removeNewBlockTxs(blocks: Block[]): void
  start(): boolean
  stop(): boolean
  cleanup(): void
  close(): void
}

Related Topics