Docs
Testing

Fork testing

Learn how to test contracts against blockchain state by forking testnet or mainnet

Fork testing runs tests against blockchain state by loading accounts and libraries from TON testnet or TON mainnet. Use it to test contract interactions with deployed contracts without complex local environment setup.

Acton resolves one masterchain block when the fork starts. Account state and the network configuration come from that block. With an explicit --fork-block-number, Unix time also comes from the selected block. A latest fork uses the current system Unix time.

Available since: Acton 1.2

Fork tests stay in the local emulator. For a persistent fork shared by scripts and frontends, use a simulator network in the Acton Studio.

What fork testing is

Fork testing keeps a local view (fork) of remote chain state. When test code reads an account that exists on testnet or mainnet while fork mode is on, the runner:

  1. Fetches the account's state from the network
  2. Caches it locally for the test run
  3. Uses that remote state inside local execution

Fork testing is useful for:

  • Testing integrations with existing contracts
  • Verifying behavior against real contract code
  • Debugging issues with deployed contracts
  • Testing edge cases with real on-chain data

It combines real on-chain data with local test execution speed.

Enable fork testing

Basic usage

Pass --fork-net when running tests:

# Fork from testnet
acton test --fork-net testnet

# Fork from mainnet
acton test --fork-net mainnet

Pin a historical state by passing a block sequence number:

acton test --fork-net mainnet --fork-block-number 54321000

Pin the fork block for faster reruns

When in need to rerun the same fork tests often, pass --fork-block-number instead of using a live fork. Pinned fork runs persist fetched accounts under build/cache/<network>/<seqno>/, so the next CLI run can load the same accounts from disk before calling the remote API. Fork runs without a block number resolve the latest block on every CLI run and reuse persisted accounts only when that block has the same sequence number.

Alternatively, set defaults in Acton.toml:

Acton.toml
[test]
fork-net = "mainnet"
fork-block-number = 54321000

Configure API access

Remote state is fetched through the TON Center APIs. Obtain the API key in the Telegram bot to access higher RPS limits.

Set TONCENTER_MAINNET_API_KEY or TONCENTER_TESTNET_API_KEY environment variables in the .env file or export them on the shell:

# Testnet API key
TONCENTER_TESTNET_API_KEY=YOUR_API_KEY acton test --fork-net testnet

# Mainnet API key
TONCENTER_MAINNET_API_KEY=YOUR_API_KEY acton test --fork-net mainnet

Custom networks

Use custom:<name> to fork from a network defined under [networks]:

Acton.toml
[networks.archive-rpc]
api = { v2 = "https://your-rpc.example/api/v2/jsonRPC" }

[test]
fork-net = "custom:archive-rpc"

With this default in Acton.toml, run tests normally and Acton will fork from archive-rpc:

acton test

Additionally, it is possible to select the custom fork network explicitly for a single run:

acton test --fork-net custom:archive-rpc
acton test tests/wallet.test.tolk --fork-net custom:archive-rpc

Execution model

When fork testing is enabled, Acton first resolves the selected or latest masterchain block and loads the network configuration for the same sequence number. An explicit historical fork also loads that block's time. A latest fork uses the current system time and does not request the block header. When a test touches an account:

  1. The system first checks if the account was already loaded in the local cache.
  2. It checks the persistent fork account cache.
  3. If the account is still not cached and --fork-net is specified, it queries the configured API endpoint.
  4. Account state, i.e., balance, code, data, status, is downloaded and deserialized.
  5. The state is cached for subsequent accesses.
  6. Tests execute using this real state in the local emulator.

Network latency

The first fetch of each distinct account hits the network and adds latency. Subsequent fetches reuse the cache. Plan accordingly when many distinct accounts appear in one run.

Cache scope and refresh behavior

Available since: Acton 1.2

Fork testing uses two layers of account state:

  • Local account changes are scoped to the current test.
  • The remote snapshot cache is shared by tests in the same acton test run.
  • Fetched remote snapshots are also saved to disk under build/cache/<network>/<seqno>/<workchain>_<address-hash>.json.

Acton separately caches the masterchain configuration and, for explicit historical forks, block time under build/cache/masterchain-snapshots/<network>/<seqno>.json for 24 hours. An explicit block number can use a fresh entry without a network request. A fork without a block number first resolves the latest sequence number and then checks for a matching configuration entry.

The shared remote cache stores only fetched remote snapshots:

  • The cache key is the tuple of fork network, pinned or resolved block number, and account address.
  • The second test that touches the same remote account reuses the cached remote snapshot instead of re-fetching it.
  • A fresh acton test --fork-net ... run starts with an empty in-memory cache.
  • A fresh run can reuse the persistent account cache before making a network request when its resolved block number matches.
  • Pass --no-fork-cache to skip persistent account and library cache reads and writes for the current run. This option does not disable the masterchain snapshot TTL cache.
  • Pass --clear-cache to remove persistent fork account entries together with the rest of build/cache.
  • Failed lookups are not cached. If a request errors, the next access can try the network again.

Local changes are scoped to the current test. If a test fetches a remote account and then changes it locally, later reads in that same test use the changed local account. Other tests do not inherit that local mutation; they start from a fresh local state and, if they touch the same account, reuse the shared remote snapshot that was fetched earlier in the run.

To refetch remote account and library snapshots, run with --no-fork-cache once. To also refetch the masterchain snapshot, clear build/cache. Changing --fork-net or --fork-block-number also uses a different cache key.

Fork testing only loads accounts that are not already present in the current test's local account state. Accounts deployed or changed during a test are not replaced by remote snapshots.

Library lookup precedence

Library lookup follows the same "local first" rule.

When code asks to resolve a library hash, Acton first checks libraries already registered in the current emulated blockchain state, for example via testing.registerLibrary(...). Only if the library is still missing does it fall back to the configured fork network.

That ordering matters when a forked test registers a different library cell than the remote network would supply: the locally registered library overrides its remote counterpart for the rest of the run.

Last updated on

On this page