Forking Mainnet Example
Basic Fork Setup
Unknown accounts/contracts fetch from the remote on demand and cache locally.
import { createTevmNode, http } from 'tevm'
const rpcUrl = process.env.MAINNET_RPC_URL
if (!rpcUrl) throw new Error('MAINNET_RPC_URL is required')
const node = createTevmNode({
fork: {
transport: http(rpcUrl)({}),
},
loggingLevel: 'debug',
})
await node.ready()Account Impersonation
import { callHandler } from 'tevm/actions'
const result = await callHandler(node)({
from: '0x28C6c06298d514Db089934071355E5743bf21d60',
to: '0x1234567890123456789012345678901234567890',
value: 1000000000000000000n, // 1 ETH
skipBalance: true,
throwOnFail: false,
})Working with Forked Contracts
import { callHandler } from 'tevm/actions'
import { encodeFunctionData, parseAbi } from 'viem'
const USDC_ADDRESS = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'
const HOLDER_ADDRESS = '0x47ac0fb4f2d84898e4d9e7b4dab3c24507a6d503'
const RECIPIENT_ADDRESS = '0x1234567890123456789012345678901234567890'
const ERC20_ABI = parseAbi([
'function balanceOf(address) view returns (uint256)',
'function transfer(address to, uint256 amount) returns (bool)',
])
const balance = await callHandler(node)({
to: USDC_ADDRESS,
data: encodeFunctionData({
abi: ERC20_ABI,
functionName: 'balanceOf',
args: [HOLDER_ADDRESS],
}),
})
// Simulate a local write; the remote chain is not affected.
const transfer = await callHandler(node)({
from: HOLDER_ADDRESS,
to: USDC_ADDRESS,
data: encodeFunctionData({
abi: ERC20_ABI,
functionName: 'transfer',
args: [RECIPIENT_ADDRESS, 1n],
}),
skipBalance: true,
throwOnFail: false,
})Fork at Specific Block
const node = createTevmNode({
fork: {
transport: http(rpcUrl)({}),
blockTag: 23_483_670n,
},
})
const vm = await node.getVm()
const block = await vm.blockchain.getBlock(23_483_670n)Multiple Network Support
const optimismNode = createTevmNode({
fork: { transport: http(process.env.OPTIMISM_RPC_URL!)({}) },
})
const arbitrumNode = createTevmNode({
fork: { transport: http(process.env.ARBITRUM_RPC_URL!)({}) },
})
