Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

Receipts & Logs

Tevm Node manages transaction receipts and event logs through the ReceiptsManager and filter system — enabling event listening, status tracking, and log filtering.

Quick Start

Basic Usage
import { createTevmNode, PREFUNDED_ACCOUNTS } from 'tevm'
import { callHandler, mineHandler } from 'tevm/actions'
import { hexToBytes } from 'tevm/utils'
 
const node = createTevmNode()
const receiptsManager = await node.getReceiptsManager()
 
const callResult = await callHandler(node)({
  addToMempool: true,
  from: PREFUNDED_ACCOUNTS[0].address,
  to: '0x2345678901234567890123456789012345678901',
  value: 1000000000000000000n,
})
 
await mineHandler(node)({})
 
if (!callResult.txHash) {
  throw new Error('Transaction was not added to the mempool')
}
 
const receiptResult = await receiptsManager.getReceiptByTxHash(
  hexToBytes(callResult.txHash)
)
 
if (receiptResult) {
  const [receipt, blockHash, txIndex, logIndex] = receiptResult
  console.log({
    status: 'status' in receipt ? receipt.status : undefined,
    gasUsed: receipt.cumulativeBlockGasUsed,
    logs: receipt.logs
  })
}

Receipt Types

Tevm supports receipts across all Ethereum hard forks:

  • Pre-Byzantium — uses state root for transaction results
  • Post-Byzantium — uses status codes (success/failure)
  • EIP-4844 — includes blob gas information
interface PreByzantiumReceipt {
  stateRoot: Uint8Array        // Merkle root after transaction
  cumulativeBlockGasUsed: bigint
  logs: Log[]
  // No status field
}
 
interface PostByzantiumReceipt {
  status: number               // 1 = success, 0 = failure
  cumulativeBlockGasUsed: bigint
  logs: Log[]
}
 
interface EIP4844Receipt extends PostByzantiumReceipt {
  blobGasUsed: bigint
  blobGasPrice: bigint
}
function processReceipt(receiptResult) {
  if (!receiptResult) return 'Receipt not found'
  const [receipt] = receiptResult
 
  if ('status' in receipt) {
    return `Transaction ${receipt.status === 1 ? 'succeeded' : 'failed'}`
  } else {
    return `Transaction included with state root: 0x${Buffer.from(receipt.stateRoot).toString('hex')}`
  }
}

Working with Event Logs

Contract Deployment

import { createTevmNode, PREFUNDED_ACCOUNTS } from 'tevm'
import { callHandler, mineHandler } from 'tevm/actions'
import { SimpleContract } from 'tevm/contract'
import { encodeDeployData } from 'viem'
 
const node = createTevmNode()
 
const deployResult = await callHandler(node)({
  addToMempool: true,
  from: PREFUNDED_ACCOUNTS[0].address,
  data: encodeDeployData(SimpleContract.deploy(2n)),
  throwOnFail: false,
})
 
await mineHandler(node)({})
 
const contractAddress = deployResult.createdAddress
if (!contractAddress) {
  throw new Error('Contract deployment failed')
}

Emit Events

import { PREFUNDED_ACCOUNTS } from 'tevm'
import { callHandler, mineHandler } from 'tevm/actions'
import { SimpleContract } from 'tevm/contract'
import { encodeFunctionData } from 'viem'
 
const contract = SimpleContract.withAddress(contractAddress)
const callResult = await callHandler(node)({
  blockTag: 'pending',
  addToMempool: true,
  from: PREFUNDED_ACCOUNTS[0].address,
  to: contractAddress,
  data: encodeFunctionData(contract.write.set(42n)),
  gas: 100000n,
  throwOnFail: false,
})
 
await mineHandler(node)({})

Query Logs

import { createAddress } from 'tevm/address'
import { hexToBytes } from 'tevm/utils'
 
const vm = await node.getVm()
const receiptsManager = await node.getReceiptsManager()
const fromBlock = await vm.blockchain.getBlock(0n)
const toBlock = await vm.blockchain.getCanonicalHeadBlock()
const contract = createAddress(contractAddress)
 
// 1. All logs from the contract
const contractLogs = await receiptsManager.getLogs(
  fromBlock, toBlock, [contract.toBytes()], undefined
)
 
// 2. Logs for a specific event
const setEventSignature = '0x012c78e2b84325878b1bd9d250d772cfe5bda7722d795f45036fa5e1e6e303fc'
const setLogs = await receiptsManager.getLogs(
  fromBlock, toBlock, [contract.toBytes()], [hexToBytes(setEventSignature)]
)
 
// 3. Logs with wildcard topics
const wildcardLogs = await receiptsManager.getLogs(
  fromBlock,
  toBlock,
  [contract.toBytes()],
  [null]
)

Process Log Data

// ReceiptsManager logs are returned with their block and transaction metadata.
for (const { log, block, txIndex, logIndex } of setLogs) {
  const [address, topics, data] = log
  const value = BigInt('0x' + Buffer.from(data).toString('hex'))
 
  console.log(`Contract: 0x${Buffer.from(address).toString('hex')}`)
  console.log(`Topic: 0x${Buffer.from(topics[0]).toString('hex')}`)
  console.log(`Value: ${value}`)
  console.log(`Block: ${block.header.number}, TxIndex: ${txIndex}, LogIndex: ${logIndex}`)
}

Advanced Features

Complex Filtering

Ethereum logs support up to 4 topics:

const fromAddress = '0x1234567890123456789012345678901234567890'
const toAddress = '0x2345678901234567890123456789012345678901'
 
// undefined = wildcard
const topics = [
  hexToBytes('0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'), // Transfer
  hexToBytes('0x000000000000000000000000' + fromAddress.slice(2)),
  hexToBytes('0x000000000000000000000000' + toAddress.slice(2))
]
 
const filteredLogs = await receiptsManager.getLogs(
  fromBlock,
  toBlock,
  [contractAddress.toBytes()],
  topics
)

Multiple Addresses

const tokenAddress = createAddress('0x1234567890123456789012345678901234567890')
const marketplaceAddress = createAddress('0x2345678901234567890123456789012345678901')
 
const transferEvent = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'
 
const transfers = await receiptsManager.getLogs(
  fromBlock,
  toBlock,
  [tokenAddress.toBytes(), marketplaceAddress.toBytes()],
  [hexToBytes(transferEvent)]
)
 
const tokenEvents = transfers.filter(log =>
  Buffer.from(log.address).toString('hex') === Buffer.from(tokenAddress.toBytes()).toString('hex')
)
const marketplaceEvents = transfers.filter(log =>
  Buffer.from(log.address).toString('hex') === Buffer.from(marketplaceAddress.toBytes()).toString('hex')
)

Receipt Indexing

// Receipt by tx hash
const receipt = await receiptsManager.getReceiptByTxHash(txHash)
 
// All receipts in a block
const block = await vm.blockchain.getCanonicalHeadBlock()
const receipts = await receiptsManager.getReceipts(block.hash())
 
// Built-in limits
const GET_LOGS_LIMIT = 10000             // Max logs returned
const GET_LOGS_LIMIT_MEGABYTES = 150     // Max response size
const GET_LOGS_BLOCK_RANGE_LIMIT = 2500  // Max block range

Best Practices

  • Efficient Queries — use specific filters and limited block ranges
  • Handle Null Results — always check for null/undefined receipts
  • Type Safety — check receipt type before accessing fields
  • Pagination — paginate large log queries

Efficient Log Queries

const latestBlock = await vm.blockchain.getCanonicalHeadBlock()
const blockNumber = latestBlock.header.number
 
// Last 100 blocks only
const fromBlock = await vm.blockchain.getBlock(
  blockNumber - 100n > 0n ? blockNumber - 100n : 0n
)
 
const logs = await receiptsManager.getLogs(
  fromBlock,
  latestBlock,
  [contractAddress.toBytes()],
  [eventTopic]
)

Proper Error Handling

async function safeGetReceipt(txHash) {
  try {
    const receiptResult = await receiptsManager.getReceiptByTxHash(txHash)
    if (receiptResult === null) {
      console.log('Receipt not found - transaction may be pending or not exist')
      return null
    }
    const [receipt] = receiptResult
    return receipt
  } catch (error) {
    console.error('Error retrieving receipt:', error.message)
    return null
  }
}

Working with Receipt Types

function getTransactionStatus(receipt) {
  if (!receipt) return 'Unknown'
 
  if ('status' in receipt) {
    return receipt.status === 1 ? 'Success' : 'Failed'
  } else if ('stateRoot' in receipt) {
    const emptyRoot = '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421'
    const actualRoot = '0x' + Buffer.from(receipt.stateRoot).toString('hex')
    return actualRoot === emptyRoot ? 'Likely Failed' : 'Likely Success'
  }
 
  return 'Unknown Format'
}

Related Resources