Skip to content

Call API

The Call API is one of the most important APIs in Tevm, covering 90% of use cases along with mining. It provides a powerful interface for executing EVM calls with extensive configuration options.

Basic Usage

Tevm offers two API styles for using the Call API:

Client-based API (batteries included)

import { createMemoryClient } from 'tevm'
 
const client = createMemoryClient()
 
// Use tevmCall directly on the client object
const result = await client.tevmCall({
  from: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', // Default account
  to: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // WETH on mainnet
  data: '0x' // Empty call
})

Tree-shakable API

import { tevmCall } from 'tevm/actions'
import { createClient, custom } from 'viem'
import { createTevmNode } from 'tevm/node'
import { requestEip1193 } from 'tevm/decorators'
 
// Create Tevm Node with EIP-1193 support
const node = createTevmNode().extend(requestEip1193())
 
// Create Viem client
const client = createClient({
  transport: custom(node),
})
 
// Use the tree-shakable action
const result = await tevmCall(client, {
  from: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', // Default account
  to: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // WETH on mainnet
  data: '0x' // Empty call
})

Parameters

The CallParams type includes:

type CallParams = {
  // Required for most calls (except contract deployment)
  to?: Address
  // Input data for the call
  data?: Hex
  // Value in wei to send
  value?: bigint
  // Gas limit for the call
  gas?: bigint
  // Block tag to execute against
  blockTag?: 'latest' | 'pending' | 'earliest' | number
  // Whether to create a transaction
  createTransaction?: 'on-success' | 'always' | 'never' | boolean
  // Whether to skip balance checks
  skipBalance?: boolean
  // Whether to create an access list
  createAccessList?: boolean
  // Whether to create a debug trace
  createTrace?: boolean
  // From address (defaults to first account)
  from?: Address
  // Gas price settings
  maxFeePerGas?: bigint
  maxPriorityFeePerGas?: bigint
  // State overrides
  stateOverrideSet?: StateOverrideSet
  // Block overrides
  blockOverrideSet?: BlockOverrideSet
  // Event handlers for EVM execution
  onStep?: (data: InterpreterStep, next?: () => void) => void
  onNewContract?: (data: NewContractEvent, next?: () => void) => void
  onBeforeMessage?: (data: Message, next?: () => void) => void
  onAfterMessage?: (data: EVMResult, next?: () => void) => void
}

Return Type

The CallResult includes:

type CallResult = {
  // Return data from the call
  rawData: Hex
  // Gas used by the EVM
  executionGasUsed: bigint
  // Total gas including intrinsic costs
  totalGasSpent?: bigint
  // Transaction hash if created
  txHash?: Hex
  // Logs emitted
  logs?: Log[]
  // Created contract address
  createdAddress?: Address
  // Access list if requested
  accessList?: Record<Address, Set<Hex>>
  // Debug trace if requested
  trace?: DebugTraceCallResult
  // Any errors that occurred
  errors?: TevmCallError[]
}

Examples

1. Simple Contract Call

Using client-based API:

import { createMemoryClient } from 'tevm'
import { encodeFunctionData, decodeFunctionResult, parseAbi } from 'viem'
 
// Example ERC20 ABI for 'balanceOf'
const abi = parseAbi([
  'function balanceOf(address account) view returns (uint256 balance)'
])
 
const client = createMemoryClient()
 
const result = await client.tevmCall({
  to: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // WETH
  from: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
  data: encodeFunctionData({
    abi,
    functionName: 'balanceOf',
    args: ['0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266']
  })
})
 
const balance = decodeFunctionResult({
  abi,
  functionName: 'balanceOf',
  data: result.rawData
})

2. Contract Deployment

Using client-based API:

import { createMemoryClient } from 'tevm'
 
// Simple contract bytecode (returns 42)
const bytecode = '0x6080604052348015600f57600080fd5b50602a60808190526040516100929190810190830190829052565b604051601f19601f830116810160405280815292829060208401853c80601f830112156100c057600080fd5b505b50505050610047806100d36000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c80632096525514602d575b600080fd5b60336047565b604051603e91906059565b60405180910390f35b602a81565b6000819050919050565b6053816040565b82525050565b6000602082019050606c6000830184604c565b9291505056fea2646970667358221220f1c69e125f1a9f0c5e22a6fb4f9cb134c5b43496922c563e13731844a6e4d12d64736f6c63430008130033'
 
const client = createMemoryClient()
 
const result = await client.tevmCall({
  from: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
  data: bytecode,
  createTransaction: true
})
 
console.log('Contract deployed at:', result.createdAddress)

3. State Override

Using client-based API:

import { createMemoryClient } from 'tevm'
 
const client = createMemoryClient()
 
const result = await client.tevmCall({
  from: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
  to: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // WETH
  data: '0x',
  stateOverrideSet: {
    ['0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266']: {
      balance: 4096n, // 0x1000 as bigint
      nonce: 2n,
      code: '0x',
      state: {}
    }
  }
})

4. Debug Trace

Using client-based API:

import { createMemoryClient } from 'tevm'
 
const client = createMemoryClient()
 
const result = await client.tevmCall({
  from: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
  to: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // WETH
  data: '0x',
  createTrace: true
})
 
// Analyze the execution trace
if (result.trace) {
  result.trace.structLogs.forEach(log => {
    console.log(log.op, log.stack, log.memory)
  })
}

5. EVM Event Handlers

You can attach event handlers to monitor EVM execution in real-time.

Using client-based API:

import { createMemoryClient } from 'tevm'
import { encodeFunctionData } from 'viem'
 
const client = createMemoryClient()
 
// Use EVM event handlers to monitor execution
const result = await client.tevmCall({
  from: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
  to: contractAddress,
  data: encodeFunctionData({
    abi,
    functionName: 'myFunction',
    args: [arg1, arg2]
  }),
  // Monitor each EVM step
  onStep: (step, next) => {
    console.log(
      `Executing ${step.opcode.name} at PC=${step.pc}`,
      `Gas left: ${step.gasLeft.toString()}`,
      `Stack depth: ${step.stack.length}`
    )
    // Important: Call next to continue execution
    next?.()
  },
  // Monitor contract creation events
  onNewContract: (data, next) => {
    console.log(`New contract created at: ${data.address.toString()}`)
    next?.()
  },
  // Monitor messages (calls) before execution
  onBeforeMessage: (message, next) => {
    console.log(`Call to: ${message.to?.toString()}`, 
                `Value: ${message.value.toString()}`, 
                `Gas limit: ${message.gasLimit.toString()}`)
    next?.()
  },
  // Monitor messages (calls) after execution
  onAfterMessage: (result, next) => {
    console.log(`Call result: ${result.execResult.returnValue.toString('hex')}`,
                `Gas used: ${result.execResult.executionGasUsed.toString()}`)
    if (result.execResult.exceptionError) {
      console.error(`Error: ${result.execResult.exceptionError.error}`)
    }
    next?.()
  }
})

Higher Level APIs

While tevmCall