Smart Contract Integration Testing

We design and develop full-cycle blockchain solutions: from smart contract architecture to launching DeFi protocols, NFT marketplaces and crypto exchanges. Security audits, tokenomics, integration with existing infrastructure.
Showing 1 of 1 servicesAll 1306 services
Smart Contract Integration Testing
Medium
~2-3 business days
FAQ
Blockchain Development Services
Blockchain Development Stages
Latest works
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1051
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    827
  • image_logo-aider_0.jpg
    AIDER company logo development
    762
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    850

Smart Contract Testing (Integration Tests)

Unit tests show that the transfer() function correctly updates balances. Integration tests show that this function combined with approve(), allowance() and the aggregator contract doesn't create a race condition during parallel calls in one block. The difference is fundamental, especially when a protocol interacts with Uniswap V3, Aave V3 and Chainlink in a single transaction.

Where Unit Tests End and Problems Begin

Classic scenario: a staking contract with reward distribution passed 100% unit tests. Each function works correctly in isolation. A week after mainnet deployment a problem is discovered: if a user calls compound() and withdraw() in the same block (via batch transaction or aggregator contract), the reward calculation takes a balance snapshot before compound() but applies it after — the user gets double rewards for one epoch.

This is an integration bug. It occurs only at a specific order of calls within one block. Unit tests won't catch it by definition, because they test functions in isolation.

Another pattern: a contract works correctly with ERC-20 tokens that follow the standard. But in the real DeFi landscape there are fee-on-transfer tokens (USDT on some chains), rebase tokens (stETH), tokens with blacklist (USDC). An integration test must verify that the contract doesn't make assumptions that amount in the Transfer event equals the actually received amount.

How Integration Testing is Structured

Mainnet Fork as the Testing Foundation

Realistic integration tests require real protocol states. We fork mainnet through Hardhat or Foundry at a specific block number and test interaction with live Uniswap, Aave, Curve contracts — not mocks.

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

Fixing the block number is critical. Without it, tests are non-deterministic: prices, liquidity, pool states change, and a test may pass today and fail tomorrow for reasons unrelated to code.

Interaction Scenarios to Test

Multi-step DeFi scenarios. For example, for a yield aggregator: deposit → approve LP token → stake in gauge → harvest → compound. Each step calls external contracts. The test verifies the final state, not intermediate steps.

Attacks via flash loan. We simulate a flash loan attack through Aave V3 flashLoanSimple() right in the test: borrow tokens, try to manipulate the price in the AMM, call the target contract, return the loan. If the contract uses spot price from DEX without TWAP — it's vulnerable. The test should catch this.

Reentrancy through callback. ERC-721 has onERC721Received(), ERC-1155 has onERC1155Received(). If the contract makes state change after transferFrom() and callback calls the contract again — this is reentrancy. We write a special attacker contract in the test that implements this callback maliciously.

Sandwich attacks and MEV. We test slippage protection: what happens if someone moves the price in the pool between approve() and swap(). We use hardhat_setStorageAt for direct pool state manipulation in the test.

Test Type Tool Coverage
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

Foundry vs Hardhat for Integration Tests

Foundry is faster: a 200-case integration test suite runs in 15-30 seconds vs 3-5 minutes in Hardhat. This matters for TDD and frequent iterations.

Hardhat is more convenient for complex scenarios with JavaScript logic: block manipulation via evm_mine, precise gas control via eth_estimateGas with overrides, integration with real protocol SDKs (Uniswap SDK, Aave SDK).

We use both. Foundry — for quick property-based test runs and fuzz. Hardhat — for complex multi-step scenarios with real protocols.

Typical Errors Found in Real Projects

Assumption about event order in one block. If a contract uses block.number for reward calculation and two calls happen in one block — block.number is the same for both. Contract must use block.timestamp or a separate counter.

Mocks instead of real tokens. A mock token always returns true on transfer(). USDT on Ethereum doesn't return a value at all (doesn't match ERC-20 standard). Test with mock passes, deployment on mainnet with USDT — fails.

Ignoring gas limit on functions. Integration test must measure gas consumption for each scenario. If an aggregator calls 10 Curve pools in one transaction, it could hit the block gas limit in certain pool states.

No edge case token tests. Fee-on-transfer (PAXG, STA), rebase (stETH, aTokens), pausable (USDC), with blacklist — each category requires a separate test suite.

Process and Timeline

Integration testing of an existing protocol: 2-3 business days. Includes: contract analysis, identifying critical interaction paths, writing test suite, running with mainnet fork, report on found issues.

Parallel integration test development with contract development — include in overall project estimate, usually 30-40% of development time. Cost is calculated individually.