Тестування смарт-контрактів (інтеграційні тести)

Проєктуємо та розробляємо блокчейн-рішення повного циклу: від архітектури смарт-контрактів до запуску DeFi-протоколів, NFT-маркетплейсів та криптобірж. Аудит безпеки, токеноміка, інтеграція з наявною інфраструктурою.
Показано 1 з 1Усі 1306 послуг
Тестування смарт-контрактів (інтеграційні тести)
Середній
~2-3 дні
Часті запитання

Напрямки блокчейн-розробки

Етапи блокчейн-розробки

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1308
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1221
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    921
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1149
  • image_logo-advance_0.webp
    Розробка логотипу компанії B2B Advance
    612
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    886

Тестування смарт-контрактів (інтеграційні тесты)

Unit-тесты показують, що функція transfer() коректно оновлює балансів. Інтеграційні тесты показують, що ця функція в зв'язці з approve(), allowance() і контрактом-агрегатором не створює race condition при паралельних викликах в одному блоці. Різниця принципова, особливо коли протокол взаємодіє з Uniswap V3, Aave V3 і Chainlink в одній транзакції.

Де unit-тесты закінчуються і розпочинаються проблеми

Класичний сценарій: контракт стейкингу з reward-дистрибьюцією пройшов unit-тесты на 100%. Кожна функція працює коректно в ізоляції. На mainnet через тиждень після деплоєю виявляється: якщо користувач викликає compound() і withdraw() в одному блоці (через batch-транзакцію або через контракт-агрегатор), reward-розрахунок бере snapshot балансу до compound(), але застосовує його після — користувач отримує подвійне вознаграждення за один epoch.

Це інтеграційний баг. Він виникає тільки при конкретному порядку викликів у рамках одного блоку. Unit-тесты його не впіймають за визначенням, тому що тестують функції ізольовано.

Ще один паттерн: контракт коректно працює з ERC-20 токенами, які слідують стандарту. Але в реальному DeFi-ландшафті зустрічаються fee-on-transfer токени (USDT на деяких чейнах), rebase-токени (stETH), токени з blacklist (USDC). Інтеграційний тест повинен перевірити, що контракт не робить припущень, що amount у Transfer event дорівнює фактично отриманому кількості.

Як будується інтеграційне тестування

Mainnet fork як основа середи

Реалістичні інтеграційні тесты вимагають реальних станів протоколів. Ми форкаємо mainnet через Hardhat або Foundry з конкретним block number і тестуємо взаємодію з живими контрактами Uniswap, Aave, Curve — не з моками.

// hardhat.config.ts
networks: {
  hardhat: {
    forking: {
      url: process.env.ALCHEMY_URL,
      blockNumber: 19500000, // фіксуємо блок для воспроизводимості
    }
  }
}

Фіксація block number критична. Без неї тесты недетерміновані: ціни, ліквідність, стан пулів змінюються, і тест може проходити сьогодні і падати завтра з причин, не пов'язаних з кодом.

Сценарії взаємодії, які тестуємо

Multi-step DeFi сценарії. Наприклад, для yield aggregator: deposit → approve LP-токен → stake у gauge → harvest → compound. Кожний крок викликає зовнішні контракти. Тест перевіряє фінальне стан, а не проміжні кроки.

Атаки через flash loan. Імітуємо flash loan attack через Aave V3 flashLoanSimple() прямо в тесте: займаємо токени, намагаємся маніпулювати ціною в AMM, викликаємо цільовий контракт, повертаємо займ. Якщо контракт використовує spot price з DEX без TWAP — він уразливий. Тест повинен це зафіксувати.

Reentrancy через callback. ERC-721 має onERC721Received(), ERC-1155 — onERC1155Received(). Якщо контракт робить state change після transferFrom(), а callback викликає контракт знову — це reentrancy. Пишемо спеціальний контракт-атакер у тесте, який реалізує цей callback зловісно.

Sandwich атаки й MEV. Тестуємо слипедж-захист: що буває, якщо між approve() і swap() хтось рухає ціну в пулі. Використовуємо hardhat_setStorageAt для прямої маніпуляції станом пулу у тесте.

Тип тесту Інструмент Що покриває
Unit Hardhat / Foundry Ізольована логіка функцій
Integration (local mock) Hardhat Взаємодія між своїми контрактами
Integration (mainnet fork) Hardhat / Foundry Взаємодія з реальними протоколами
Fuzzing Echidna / Foundry forge fuzz Інваріантні порушення
Formal verification Certora Prover Математичні властивості

Foundry проти Hardhat для інтеграційних тестів

Foundry швидше: інтеграційний тест-сьют на 200 кейсів виконується за 15-30 секунд проти 3-5 хвилин у Hardhat. Це важливо при TDD і частих ітераціях.

Hardhat зручніше для складних сценаріїв з JavaScript-логікою: маніпуляція блоками через evm_mine, точний контроль газу через eth_estimateGas з overrides, інтеграція з реальними SDK протоколів (Uniswap SDK, Aave SDK).

Ми використовуємо обидва. Foundry — для швидкого прогону property-based тестів і fuzz. Hardhat — для складних багатокрокових сценаріїв з реальними протоколами.

Типові помилки, які виловили в реальних проектах

Припущення про порядок подій в одному блоці. Якщо контракт використовує block.number для розрахунку нагород, а два виклики відбуваються в одному блоці — block.number однаків для обох. Контракт має використовувати block.timestamp або окремий лічильник.

Моки замість реальних токенів. Мок-токен завжди повертає true на transfer(). USDT на Ethereum не повертає значення взагалі (не відповідає ERC-20 стандарту). Тест з моком пройде, деплой на mainnet з USDT — ні.

Ігнорування gas limit на функції. Інтеграційний тест повинен замірювати gas consumption для кожного сценарію. Якщо агрегатор викликає 10 пулів Curve в одній транзакції, це може упертися в block gas limit при певних станах пулів.

Немає тестів на edge case токенів. Fee-on-transfer (PAXG, STA), rebase (stETH, aTokens), pausable (USDC), з blacklist — кожна категорія вимагає окремого тест-сьюту.

Процес і сроки

Інтеграційне тестування існуючого протоколу: 2-3 робочих дня. Включає: аналіз контрактів, визначення критичних шляхів взаємодії, написання тест-сьюту, прогон з форком mainnet, звіт за знайденими проблемами.

Паралельна розробка інтеграційних тестів разом з контрактами — закладаємо в загальну смету проекту, зазвичай 30-40% від часу розробки. Вартість розраховується індивідуально.