> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://v3.viem.sh/api/mcp` to find what you need.

# Test Contract Interactions

## Overview

A fork test combines test Actions for arranging state with the same typed public and wallet Actions
used by the application.

This workflow catches ABI encoding, simulation, signing, and receipt handling.

## Recipes

These recipes assume Anvil is [forking mainnet](/docs/guides/testing/anvil) and your test Account is
funded.

### Simulate and Write a Contract Call

Read the precondition, use [`contract.simulate`](/docs/actions/public/contract/simulate) to validate
the write, then submit it with [`contract.writeSync`](/docs/actions/wallet/contract/write).

:::code-group
```ts twoslash [example.ts]
import { Abi, Value } from 'viem/utils'
import { client } from './viem.config'

const abi = Abi.from([
  'function balanceOf(address owner) view returns (uint256)',
  'function deposit() payable',
])
const address = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'

const before = await client.contract.read({ // [!code focus]
  abi, // [!code focus]
  address, // [!code focus]
  args: [client.account.address], // [!code focus]
  functionName: 'balanceOf', // [!code focus]
}) // [!code focus]

const { request } = await client.contract.simulate({ // [!code focus]
  abi, // [!code focus]
  address, // [!code focus]
  functionName: 'deposit', // [!code focus]
  value: Value.fromEther('1'), // [!code focus]
}) // [!code focus]

const receipt = await client.contract.writeSync(request) // [!code focus]
const after = await client.contract.read({ // [!code focus]
  abi, // [!code focus]
  address, // [!code focus]
  args: [client.account.address], // [!code focus]
  functionName: 'balanceOf', // [!code focus]
}) // [!code focus]
```

```ts twoslash [viem.config.ts] filename="viem.config.ts"
// [!include ~/snippets/docs/test.config.ts:setup]
```
:::

### Isolate the Contract Test

Snapshot before the write and revert afterward so the same fork state can serve every test.

:::code-group
```ts twoslash [example.ts]
import { Abi, Value } from 'viem/utils'
import { client } from './viem.config'

const abi = Abi.from(['function deposit() payable'])
const id = await client.state.snapshot() // [!code focus]

try {
  await client.contract.writeSync({ // [!code focus]
    abi, // [!code focus]
    address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', // [!code focus]
    functionName: 'deposit', // [!code focus]
    value: Value.fromEther('1'), // [!code focus]
  }) // [!code focus]
} finally {
  await client.state.revert({ id }) // [!code focus]
}
```

```ts twoslash [viem.config.ts] filename="viem.config.ts"
// [!include ~/snippets/docs/test.config.ts:setup]
```
:::

## Best Practices

### Assert State and Receipts

Check the receipt status and the resulting contract state. Either assertion alone can miss a bug in
event decoding or downstream state assumptions.

### Keep Unit Tests Too

Fork tests cover integration boundaries but are slower and depend on upstream RPC data. Keep pure
encoding and transformation behavior in focused unit tests.

## See More

<Cards>
  <Card icon="lucide:pen-line" title="Write and Simulate Contracts" description="Use the production contract workflow exercised by the test." to="/docs/guides/contracts/write-simulate" />

  <Card icon="lucide:undo-2" title="Snapshot and Restore" description="Keep fork tests deterministic and independent." to="/docs/guides/testing/snapshots" />
</Cards>
