> ## Documentation Index
> Fetch the complete documentation index at: https://voltaire.tevm.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Viem Test Client

> A viem-compatible test client abstraction for Anvil, Hardhat, and Ganache

<Info>
  **Skill** — Copyable reference implementation. Use as-is or customize. See [Skills Philosophy](/concepts/skills).
</Info>

# Viem Test Client

A copyable implementation of viem's test client abstraction for local test nodes.

## Overview

This Skill provides a complete viem-compatible test client including:

* **`createTestClient`** - Factory returning test client with bound actions
* **`mine`** - Mine blocks
* **`setBalance`** - Set account balance
* **`setCode`** - Set contract bytecode
* **`setStorageAt`** - Set storage slot
* **`setNonce`** - Set account nonce
* **`impersonateAccount`** - Send transactions as any address
* **`stopImpersonatingAccount`** - Stop impersonation
* **`snapshot`** / **`revert`** - Save and restore EVM state
* **`increaseTime`** / **`setNextBlockTimestamp`** - Time manipulation
* **`reset`** - Reset fork state
* **`dumpState`** / **`loadState`** - Serialize and restore state

## Quick Start with Anvil

```typescript theme={null}
import { createTestClient } from './examples/viem-testclient';

// Create provider (minimal EIP-1193 implementation)
const provider = {
  request: async ({ method, params }) => {
    const response = await fetch('http://localhost:8545', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ jsonrpc: '2.0', id: 1, method, params }),
    });
    const json = await response.json();
    if (json.error) throw new Error(json.error.message);
    return json.result;
  },
};

// Create test client
const client = createTestClient({
  mode: 'anvil',
  provider,
});

// Fund an account
await client.setBalance({
  address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
  value: 10n ** 18n, // 1 ETH
});

// Mine a block
await client.mine({ blocks: 1 });
```

## Snapshot and Revert

Save and restore EVM state:

```typescript theme={null}
// Take snapshot before tests
const snapshotId = await client.snapshot();

// Run test operations
await client.setBalance({ address: '0x...', value: 100n ** 18n });
await client.mine({ blocks: 10 });

// Restore original state
await client.revert({ id: snapshotId });
```

<Tip>
  Snapshots are consumed when reverted. Take a new snapshot after reverting if you need to revert again.
</Tip>

## Account Impersonation

Send transactions from any address without the private key:

```typescript theme={null}
const whale = '0xBE0eB53F46cd790Cd13851d5EFf43D12404d33E8'; // Binance

// Impersonate whale account
await client.impersonateAccount({ address: whale });

// Fund the whale for gas (impersonation doesn't give ETH)
await client.setBalance({ address: whale, value: 10n ** 18n });

// Now you can send transactions as the whale
// (using your regular provider for eth_sendTransaction)

// Stop impersonating when done
await client.stopImpersonatingAccount({ address: whale });
```

## Time Manipulation

Control block timestamps for testing time-dependent contracts:

```typescript theme={null}
// Advance time by 1 hour
await client.increaseTime({ seconds: 3600 });
await client.mine({ blocks: 1 });

// Set exact timestamp for next block
await client.setNextBlockTimestamp({ timestamp: 1700000000n });
await client.mine({ blocks: 1 });

// Advance multiple blocks with interval
await client.mine({ blocks: 100, interval: 12 }); // 12 seconds between blocks
```

## State Manipulation

Modify accounts directly:

```typescript theme={null}
// Set balance
await client.setBalance({
  address: '0x...',
  value: 1000000000000000000n, // 1 ETH in wei
});

// Set nonce
await client.setNonce({
  address: '0x...',
  nonce: 5,
});

// Set contract code
await client.setCode({
  address: '0x...',
  bytecode: '0x608060405260006003...',
});

// Set storage slot
await client.setStorageAt({
  address: '0x...',
  index: 0,
  value: '0x0000000000000000000000000000000000000000000000000000000000000001',
});
```

## Fork Management

Reset and manage forked state:

```typescript theme={null}
// Reset to original fork state
await client.reset();

// Reset to specific block
await client.reset({ blockNumber: 18000000n });

// Reset with new fork URL
await client.reset({
  jsonRpcUrl: 'https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY',
  blockNumber: 18500000n,
});
```

## State Serialization

Save and restore entire state:

```typescript theme={null}
// Serialize current state
const state = await client.dumpState();

// Store state somewhere (file, database, etc.)
localStorage.setItem('testState', state);

// Later, restore state
await client.loadState({ state });
```

## Automine Control

Control automatic block mining:

```typescript theme={null}
// Disable automine
await client.setAutomine(false);

// Send multiple transactions...
// They accumulate in mempool

// Mine them all at once
await client.mine({ blocks: 1 });

// Re-enable automine
await client.setAutomine(true);
```

## Mode Differences

Different test nodes use different RPC methods:

| Action       | Anvil                      | Hardhat                      | Ganache                 |
| ------------ | -------------------------- | ---------------------------- | ----------------------- |
| mine         | `anvil_mine`               | `hardhat_mine`               | `evm_mine`              |
| setBalance   | `anvil_setBalance`         | `hardhat_setBalance`         | `evm_setAccountBalance` |
| setCode      | `anvil_setCode`            | `hardhat_setCode`            | `evm_setAccountCode`    |
| impersonate  | `anvil_impersonateAccount` | `hardhat_impersonateAccount` | N/A                     |
| automine on  | `evm_setAutomine(true)`    | `evm_setAutomine(true)`      | `miner_start`           |
| automine off | `evm_setAutomine(false)`   | `evm_setAutomine(false)`     | `miner_stop`            |

The test client handles these differences automatically based on the `mode` parameter.

## Hardhat Example

```typescript theme={null}
const client = createTestClient({
  mode: 'hardhat',
  provider,
});

// Same API, different RPC methods under the hood
await client.mine({ blocks: 1 }); // Uses hardhat_mine
```

## Ganache Example

```typescript theme={null}
const client = createTestClient({
  mode: 'ganache',
  provider,
});

// Ganache has some limitations
await client.mine({ blocks: 1 }); // Uses evm_mine with different params
await client.setBalance({ address: '0x...', value: 100n }); // Uses evm_setAccountBalance
```

<Warning>
  Ganache does not support `impersonateAccount`, `setNonce`, or some other test actions. Use Anvil or Hardhat for full functionality.
</Warning>

## Files

Copy these files into your codebase:

* `createTestClient.js` - Main factory function
* `mine.js` - Mine blocks
* `setBalance.js` - Set account balance
* `setCode.js` - Set contract code
* `setStorageAt.js` - Set storage slot
* `setNonce.js` - Set account nonce
* `impersonateAccount.js` - Impersonate address
* `stopImpersonatingAccount.js` - Stop impersonating
* `snapshot.js` - Create EVM snapshot
* `revert.js` - Revert to snapshot
* `increaseTime.js` - Advance time
* `setNextBlockTimestamp.js` - Set next block timestamp
* `dropTransaction.js` - Remove tx from mempool
* `reset.js` - Reset fork
* `dumpState.js` - Serialize state
* `loadState.js` - Load state
* `setAutomine.js` - Control automine
* `TestClientTypes.ts` - TypeScript type definitions
* `errors.ts` - Custom error classes
* `index.ts` - Module exports

## Differences from Viem

This implementation:

1. Uses Voltaire's `Address` and `Hex` primitives
2. Requires explicit provider (no transport abstraction)
3. Does not include chain configuration
4. Simplified error handling

For production use, consider adding:

* Transport abstraction (HTTP, WebSocket, IPC)
* Chain configuration with native currency
* Request batching
* Retry logic for RPC calls
