Forking
A Tevm fork reads historical chain data from an upstream EIP-1193 transport and stores local changes in an in-memory overlay. It does not copy the entire remote chain and never writes upstream.
Create a Reproducible Fork
import { createMemoryClient, http } from 'tevm'
import { mainnet } from 'tevm/common'
const client = createMemoryClient({
common: mainnet,
fork: {
transport: http('https://ethereum-rpc.publicnode.com')({}),
blockTag: 20_000_000n,
},
})
await client.tevmReady()
const block = await client.getBlock({ blockNumber: 20_000_000n })
console.log(block.hash)fork.transport is an EIP-1193 request function. viem's http(url) returns a transport factory, so invoke it with ({}) before passing it to Tevm.
Set common when the chain is known. This avoids an extra chain-ID request and selects the correct hardfork and chain behavior. Pin blockTag for tests; omit it only when following the current upstream head is intended.
Read Remote State, Write Locally
Missing accounts, code, storage, and blocks are fetched lazily and cached. Test actions modify only the overlay.
import { createMemoryClient, http } from 'tevm'
import { mainnet } from 'tevm/common'
const client = createMemoryClient({
common: mainnet,
fork: {
transport: http('https://ethereum-rpc.publicnode.com')({}),
blockTag: 20_000_000n,
},
})
const account = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'
const remoteBalance = await client.getBalance({
address: account,
blockNumber: 20_000_000n,
})
await client.setBalance({
address: account,
value: remoteBalance + 1n,
})
console.log(await client.getBalance({ address: account }))Historical reads at or before the fork block can still resolve remotely. New local blocks, receipts, and state roots are owned by Tevm.
Fork Through a Viem Transport
The fork.transport option accepts the transport object returned by a viem
transport factory:
import { createMemoryClient, http } from 'tevm'
import { mainnet } from 'tevm/common'
const upstream = http('https://ethereum-rpc.publicnode.com')({})
const client = createMemoryClient({
common: mainnet,
fork: {
transport: upstream,
blockTag: 20_000_000n,
},
})
await client.tevmReady()
console.log(await client.getBlockNumber())Pass the transport object, not an already-created viem client.
Test a Fork with Snapshots
Snapshots isolate local changes without refetching the fork:
import { createMemoryClient, http } from 'tevm'
import { mainnet } from 'tevm/common'
const client = createMemoryClient({
common: mainnet,
fork: {
transport: http('https://ethereum-rpc.publicnode.com')({}),
blockTag: 20_000_000n,
},
})
const snapshotId = await client.snapshot()
await client.setCode({
address: '0x1111111111111111111111111111111111111111',
bytecode: '0x6001600055',
})
await client.revert({ id: snapshotId })Failure and Cache Behavior
- The first uncached read can fail if the upstream transport is unavailable.
- Cached fork data and all local state remain usable without another network request.
- An invalid
blockTagfails during initialization or the first read. - rc.151 does not provide an automatic rebase mode. Create a new client when a test needs a different fork block.

