Setting Up Tenderly Fork for Smart Contract Testing

A smart contract failure on mainnet is costly: transactions cannot be reversed, and users see the error. We set up an isolated Tenderly Fork environment turnkey — with a copy of the real network state, test scenarios, and a risk report. This reduces release preparation time and eliminates an entire class of mainnet failures.

Blockchain Development Services

Frequently Asked Questions

Latest works

  • Development of a web application for FEEDME
    Development of a web application for FEEDME
    1376
  • Development of an online store for the company FURNORO
    Development of an online store for the company FURNORO
    1350
  • B2B Advance company logo design
    B2B Advance company logo design
    796
  • Development of a web application for Enviok
    Development of a web application for Enviok
    1088
  • AIDER company logo development
    AIDER company logo development
    1034
  • CRM development for Chasseurs
    CRM development for Chasseurs
    1130

Test Bench on Tenderly Fork: Where Smart Contracts Break

Classic failure in smart contract testing looks like this: test suite is green, contract deployed, and the first large transaction on mainnet returns an error or debits the wrong amount.

The cause is usually not the code but the environment — the test network lives with clean balances, someone else's addresses, and simplified oracles, so it does not reproduce the state at the time of the call.

Tenderly Fork removes this difference: the bench is spun up as a copy of mainnet at a selected block.

Inside, real contracts, storage, rates, and balances of the needed addresses are available, and transaction replay reproduces both the call order, gas usage, and final state — something an isolated sandbox with artificial data cannot provide.

Then contract debugging begins. A failed block is replayed, and step by step you can see which call changed storage, where a revert occurred, at which step gas ran out.

This exposes scenarios that unit tests miss: nested calls, permission races, dependence of the result on another contract's state.

Without such a bench, these errors are found already on mainnet — based on the fact of debits, with loss of state and no ability to reproduce the scenario. Below we break down how this is covered.

What an Isolated Copy of the Network Provides Instead of a Shared Testnet

An isolated copy of the network is a snapshot of mainnet state at a specific block, spun up in a separate environment: the same addresses, the same balances, the same contract code, but without other people's traffic and transactions.

In Tenderly, such a fork is deployed per task and lives exactly as long as the check takes, and you can work with it using familiar development tools — no separate stack to learn.

The main consequence is reproducibility of checks.

The scenario is tightly bound to the block number, so balances, oracle prices, and liquidity depth are identical in every run: a defect found in review is reproduced by any engineer on the team, rather than "not reproducing the second time."

Scenario check time drops due to block management: transactions are confirmed instantly, waiting for the network and the queue for a shared testnet disappear, and returning to the initial state is done in one operation. The run takes minutes and fits into the normal development cycle instead of overnight waiting.

The shared testnet is also relieved: other people's experiments and drained faucets no longer break your runs, and yours do not interfere with other teams.

Release failures decrease because branches that cannot be reproduced on a shared testnet are covered: an external data source going down, forced liquidation of a position, limit exhaustion.

Network Fork, Public Testnet, and Local Node: What to Choose

The choice of mode is determined by what exactly we are checking: pure contract logic, integration with external protocols, or behavior under production data. These tasks should not be mixed in one environment — run time grows and false failures appear.

Option When it fits Limitations
State fork Real mainnet data is needed: balances, oracle responses, pool liquidity — integration scenarios The snapshot reflects state at startup and does not update itself; calls outside the fork have to be mocked
Public testnet Deployment, wallet operation checks, shared team scenarios State is shared and unstable, data does not match production, failure is hard to reproduce
Local node Unit checks and fast runs in the developer loop No external contracts or market data, environment is brought up manually

A state fork in Tenderly is spun up from the production network: contracts see real balances, oracle rates, and pool depth. A state snapshot rolls the chain back to the needed point in seconds — the scenario repeats under identical conditions without redeploying contracts.

A public testnet is convenient for demos and wallet checks, but not for regression: other participants change state there. In practice, modes are combined — a fork for integration, a local node for checks on every code change.

How We Deploy the Bench: Stages of Engineering Work

The implementation stages are arranged sequentially: first we capture how contracts behave on mainnet, then reproduce it in an isolated environment where the error can be repeated without consequences.

Each stage ends with an artifact — a scenario description, bench configuration, or run report — so the result is verifiable.

  1. Audit of contracts and scenarios. We examine contracts, external calls, roles, and dependencies, and compile a list of scenarios that must work after changes.
  2. Capturing the reference state. We select a mainnet block and record balances, reserves, oracle rates, and access rights on it — the bench reproduces real conditions.
  3. Bench setup. We spin up a Tenderly fork, connect the node and signer, deploy contract copies, and fund test addresses with assets.
  4. Run and debug. We go through scenarios step by step, look at call traces and gas usage, and catch discrepancies between expected and actual state.
  5. Negative checks. We reproduce failures: insufficient funds, expired deadlines, repeated calls, permission bypass — the contract must stop the operation, not lose data.
  6. Acceptance check. We compare the result against requirements and record a report: addresses, transactions, verdict for each scenario.
  7. Handover to the team. We hand over the bench configuration, scenario set, and launch instructions — your engineers reproduce the check themselves.

Changes reach mainnet already verified against a copy of real conditions. Describe the task — we will come back with an estimate and work plan.

What Remains in the Repository After Bench Handover

A bench on Tenderly Fork lives as long as the repository contains everything needed to spin it up without the author.

We hand over not an archive of files but a working environment: configurations, deployment schemes, access rights, and regulations by which the customer's team updates the bench independently. Below is what exactly remains in the repository.

  • Fork configurations: launch files, network binding, list of contract addresses, and state reproduction parameters at the needed block.
  • Deployment schemes: contract release order, dependencies between them, points where data warm-up is needed, and the procedure for rolling back to the previous state.
  • Access policies: role separation (administrator, deployment, log reading), keys with scope and lifetime descriptions, and rotation instructions.
  • Update regulations: how to pull mainnet changes, when to rebuild the fork, which checks to run before and after an update.
  • Check set: base scenarios, migration and rollback tests, reference expected results for comparison.
  • Bench documentation: architecture description, service map, local startup instructions, and common errors with ready-made solutions.
  • Transfer of responsibility: customer team accesses are issued to their accounts, and our role is reduced to on-call support under an agreed regulation.

The main effect is reproducibility: the bench is spun up on a clean machine according to instructions, not from the memory of the engineer who assembled it.

Case: Eliminated a Class of Release Failures in a Protocol

The protocol released updates every two weeks, and checks ran on a local node — some scenarios were finished directly on the test network, whose state did not match mainnet.

Result: regressions reached users in six out of ten releases, and preparing a version stretched to three weeks, half of which was manual scenario checking.

What Changed

We built permanent environments on Tenderly forks: each reproduces mainnet state at a selected block, so contracts see the same balances, limits, and prices as in production.

Scenarios were converted into reproducible sets — before release we run them on a snapshot, simulate every transaction before sending, and compare the result with the expected one.

The run was built into the build check chain: without green scenarios, the version does not move forward.

What Happened

  • regression reaching mainnet: 6 releases out of 10 → 1 out of 10;
  • release preparation time: three weeks → four days;
  • share of scenarios under automated tests: about 40% → more than 90%.

A side effect — incident analysis. A snapshot at the needed block reproduces the failure one-to-one, so we find the cause in hours, not days.

The same technique works during rollback: the previous version is checked on the same state as the problematic one, and the decision is made on facts, not guesses.

How Tenderly Fork Differs from a Local Node

Tenderly Fork is a managed copy of public network state: contracts, balances, and storage are captured at a selected block, and then you work with them as with a live network. A local node spins up a clean chain in memory, where state appears only from your transactions.

Hence the main difference: on a fork, a contract immediately sees external protocols, pools, and oracles, while on a local node they must be deployed or mocked manually.

The second difference is working with history. A fork replays an already completed transaction by its hash, shows the trace and events, allows changing call parameters and rerunning the scenario.

A local node cannot do this: it has no external history, and state export does not guarantee the result matches.

Criterion Tenderly Fork Local Node
Initial state snapshot of the real network at a selected block empty chain
Replay of past transactions by hash, with trace unavailable
External dependencies work as on the network stubs required
Offline operation and determinism depend on an external service full reproducibility

The limitations of the bench are also different. A fork depends on an external service: there is a cap on the number of parallel copies, and requests go outside your perimeter.

A local node is deterministic and works without a network, but does not reproduce real dependencies — divergence from production surfaces only after rollout.

Therefore, approaches are combined: integrations with external protocols are checked on a fork, mass runs — on a local node.

Common Technical Doubts Before Starting Work

Before spinning up Tenderly Fork, an engineering team asks the same questions: will existing contracts have to be rewritten, what to do with already deployed instances, and how accesses are transferred. Below are substantive answers, without general wording.

Will existing contracts have to be adapted?

No. The fork takes a snapshot of mainnet state and works in an isolated environment with the same addresses, bytecode, and interface descriptions. Scenarios are checked on the same code that will go to mainnet — no divergence arises between the test and working environments. There is one difference: transactions do not leave the fork and do not spend funds on gas.

What to do with contracts already deployed on the network?

They do not need to be rebuilt. The snapshot fixes the current state, and contracts respond at their usual addresses.

We can substitute balances, roles, and external data sources, call methods on behalf of any address — this allows reproducing a rare failure before it repeats for users.

What accesses are needed, and will this block current development?

Read access to the repository and a project key in the service are enough — signing keys and private keys are not transferred, and mainnet access remains read-only.

Work proceeds in a separate environment in parallel with your tasks: the release cycle does not stop, and checks connect to the build pipeline and run automatically on every change.

Let's Discuss the Task and Come Back with an Estimate and Plan

For a bench on Tenderly Fork to appear quickly and without rework, we need only input data — a ready-made specification is not required. Below is a checklist: if some items are not fully covered, we start with what is available and clarify the rest along the way.

  • Repository with contracts and the branch being worked on — with read access.
  • List of networks and scenarios: mainnet fork, test network, local bench — what exactly needs to be reproduced.
  • State we start from: block, addresses of already deployed contracts, roles and limits that matter in checks.
  • List of checks: deployment, migrations, access rights, boundary amounts, failure behavior.
  • Access to the network node or your provider if the fork is spun up on your infrastructure.
  • Isolation requirements: which data must not leave the perimeter and what must remain inside the contour.
  • A responsible person on your side who will accept the bench and use it after handover.

In response, we come back with a work plan: stages, start order, what needs to be synchronized before the first run, and timelines.

As a separate block — acceptance procedure and bench handover: accesses, instructions for launching forks, configuration scheme, and a short demo so that your team can then work without us. Describe the task — we will come back with an estimate and plan.