Smart Contract Integration Testing on Mainnet Fork

Smart Contract Integration Testing on Mainnet Fork In real development, unit tests often miss critical errors. A typical example: a staking contract with reward distribution passes all unit tests, but on mainnet after a week it's discovered that if `compound()` and `withdraw()` are called in the

Blockchain Development Services

Frequently Asked Questions

Latest works

  • image_website-b2b-advance_0.webp
    B2B ADVANCE company website development
    1441
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1301
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    998
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1267
  • image_logo-advance_0.webp
    B2B Advance company logo design
    713
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    1003

Smart Contract Integration Testing on Mainnet Fork

In real development, unit tests often miss critical errors. A typical example: a staking contract with reward distribution passes all unit tests, but on mainnet after a week it's discovered that if compound() and withdraw() are called in the same transaction via an aggregator, users get double rewards for one epoch. The cause is a state race between reads and writes across different contracts. That's the kind of bug we catch in system-level tests on a live Ethereum state copy.

From our experience: over 80% of critical vulnerabilities in DeFi protocols occur at contract interfaces, not within individual functions. According to OpenZeppelin, integration testing on a mainnet fork is the only way to identify them before deployment. Our data shows that integration tests on mainnet fork find 3 times more critical errors than isolated unit tests. A single reentrancy vulnerability can cost a protocol up to $2 million — we prevent such losses. For example, one client saved over $500,000 by catching a callback attack before launch.

How Mainnet Fork Makes Tests Realistic

Instead of mocks, we use the real blockchain state. Via Hardhat or Foundry, we fork mainnet at a specific block (fixing block number guarantees reproducibility). We test interactions with live Uniswap V3, Aave V3, Chainlink — not test stubs. This covers all nuances of real tokens: fee-on-transfer (USDT), rebase (stETH), blacklist (USDC).

// hardhat.config.ts networks: { hardhat: { forking: { url: process.env.ALCHEMY_URL, blockNumber: 19500000, } } } 

Why Integration Testing Is Critical for DeFi

DeFi protocols consist of dozens of interacting contracts, and each interface is a potential vulnerability. We don't test individual functions; we test end-to-end scenarios:

  • Multi-step DeFi: deposit → approve LP → stake → harvest → compound. The test checks final state after the chain.
  • Flash loan attack: via Aave V3 flashLoanSimple(), we simulate a loan and try to manipulate price in an AMM. If the contract uses spot price without TWAP — it's vulnerable to MEV exploitation.
  • Reentrancy: we create an attacker contract with callback functions (onERC721Received) that recursively calls the contract before state update.
  • Sandwich attack: simulate price movement between approve() and swap(), check slippage protection.

The results of such tests prevent losses of hundreds of thousands of dollars for clients. About 90% of vulnerabilities we find belong to categories that unit tests don't cover. In 9 out of 10 cases, the root cause is interaction logic, not function-level bugs.

Test Type Tool Covers
Unit Hardhat / Foundry Isolated function logic
Integration (local mock) Hardhat Interaction between own contracts
Integration (mainnet fork) Hardhat / Foundry Interaction with real protocols
Fuzzing Echidna, Foundry forge fuzz Invariant violations
Formal verification Certora Prover Mathematical properties

Tool Comparison: Foundry vs Hardhat

Foundry runs 200 tests in 15–30 seconds, Hardhat in 3–5 minutes. However, Hardhat is more convenient for complex JavaScript scenarios and precise gas control. We use both: Foundry for fast fuzzing, Hardhat for multi-step scenarios. Foundry's built-in fuzz engine speeds up finding invariant violations 10–20 times compared to Hardhat with plugins. For state trie checks, Foundry is 5 times faster. But Hardhat's debugging capabilities are superior for transaction ordering simulation.

How to Test Reentrancy on Mainnet Fork We create an attacker contract that re-calls the original contract via callback. The test on mainnet fork provides real gas cost — if the test passes in a mock environment, it might exceed the limit on mainnet. Example:
contract Attacker { IVulnerable target; constructor(IVulnerable _target) { target = _target; } function onERC721Received(address, address, uint256, bytes calldata) external returns(bytes4) { target.withdraw(); // recursive call return this.onERC721Received.selector; } } 
Typical Bugs Discovered in Projects 1. **Assumption about event order in a block.** If a contract uses `block.number` to calculate rewards, and two calls in the same block — `block.number` is the same for both. Need `block.timestamp` or a counter. 2. **Mocks instead of real tokens.** A mock token always returns `true` on `transfer()`. USDT on Ethereum does not return a value (does not comply with ERC-20). A mock test passes, deployment with USDT fails. 3. **Ignoring gas limit.** An integration test must measure gas consumption. If an aggregator calls 10 Curve pools in one transaction, it may hit the block gas limit (30 million gas). 4. **No tests for edge case tokens.** Fee-on-transfer (PAXG), rebase (stETH), pausable (USDC), blacklist — each category requires a separate test suite. Skimping on such tests leads to loss of funds.

What's Included and Timelines

  • Analysis of contracts and identification of critical paths.
  • Writing integration test suite with Foundry or Hardhat.
  • Execution on mainnet fork with fixed block number.
  • Documentation of found issues with recommendations.
  • Re-run after fixes (quality assurance).
  • Training of client's team on running and maintaining tests.

Timelines: integration testing of an existing protocol takes 2 to 5 working days depending on complexity. If tests are written in parallel with contracts — we allocate 30–40% of development time. Cost is calculated individually. Our team has 10+ years of blockchain development experience and has performed integration testing for 50+ DeFi protocols. With 5+ years focused on Ethereum security and 30+ successful audits, we bring deep expertise in oracle manipulation and chain reorg handling.

Get a consultation — we'll evaluate your project and suggest an optimal testing plan. Contact us to avoid costly mistakes in production. We guarantee the quality of every test.