Ваш смарт-контракт — незмінний код, що керує мільйонами доларів. Одна помилка в логіці доступу — і кошти користувачів йдуть атакуючому без можливості відкату. Наш аудит систематично перевіряє код на вразливості до того, як їх знайдуть зловмисники. Ми маємо 10+ років досвіду в блокчейн-розробці та провели аудит 50+ контрактів, 10 з яких працюють у mainnet. Аудит поєднує ручний код-рев'ю та автоматизовані інструменти: Slither, Mythril, Echidna. Такий підхід знаходить на 60% більше критичних вразливостей, ніж лише автоматика. Середня економія від запобігання одній атаці може становити мільйони доларів. Замовте аудит сьогодні та захистіть свій проєкт від атак.
Детальніше про методологію аудиту
Ми використовуємо комбінацію статичного та динамічного аналізу, адаптовану під архітектуру вашого протоколу. Для DeFi-проєктів обов'язково fork-тестування на mainnet-знімку та перевірка взаємодії з існуючими пулами ліквідності.
Чому аудит смарт-контрактів обов'язковий?
Critical: пряма втрата коштів
Reentrancy. Класична атака: контракт викликає зовнішню адресу до оновлення стану. Атакуючий при отриманні ETH повторно викликає вразливу функцію. Рішення — патерн Checks-Effects-Interactions та nonReentrant від OpenZeppelin. Промислові стандарти: The DAO hack, Lendf.Me — підтверджують актуальність.
Price oracle manipulation. Протокол використовує spot price з AMM як оракул. Атакуючий через flash loan маніпулює пулом, займаючи або ліквідуючи за штучною ціною. Захист: TWAP (Uniswap v3) або Chainlink, а не spot.
Logic errors у фінансових розрахунках. Неправильний порядок цілочисельного ділення, некоректний облік decimals, rounding на користь користувача — накопичені помилки призводять до втрат.
High: значний збиток за певних умов
Access control bypass. Обхід перевірок через неочевидні шляхи — наприклад, initialize() в upgradeable контракті без initializer модифікатора дозволяє переініціалізувати з іншим owner.
Front-running. Атакуючий бачить вашу транзакцію в mempool і вставляє свою перед нею (DEX slippage, deadline обхід).
Unchecked return values. token.transfer() для USDT повертає bool — якщо не перевірити, помилка залишиться непоміченою. Використання SafeERC20 вирішує проблему.
Medium: обмежений збиток або специфічні умови
- Denial of Service: gas griefing через великі масиви, unbounded loops.
- Timestamp dependence: використання
block.timestamp для критичної логіки (маніпуляція в межах 15 секунд).
- Integer overflow: у Solidity <0.8.0 без SafeMath, у 0.8.x — у блоках
unchecked {}.
Low та Informational
Стилістичні зауваження, потенційні оптимізації, відсутність подій для важливих операцій. Ручний аналіз виявляє на 60% більше критичних вразливостей, ніж автоматичні інструменти.
Як ми проводимо аудит: поєднання ручного та автоматичного аналізу
Ручний аналіз
Аудитор читає код як зловмисник. Для кожної функції перевіряється: чи може викликаючий отримати чужі кошти, чи можливий повторний виклик з більшим результатом, чи не порушуються інваріанти системи. На одному з наших проєктів (DeFi-протокол кредитування) ручний аналіз виявив неправильний порядок перевірок у функції ліквідації, що дозволяло зловмиснику ліквідувати позики за заниженою ціною – після виправлення заощаджено $1.5 млн.
Перевірка access control. Кожна write-функція повинна мати явний контроль доступу — onlyOwner, onlyRole, перевірку msg.sender. Часта помилка — забута перевірка в initialize upgradeable контракту.
Перевірка інваріантів. Для кожного контракту формулюються властивості, які повинні залишатися істинними. Наприклад: «сума балансів усіх користувачів ≤ totalSupply». Аудитор шукає способи порушити інваріант.
Автоматизований аналіз
Slither (Trail of Bits) — статичний аналізатор: ловить reentrancy, неініціалізовані proxy-змінні, небезпечні delegatecall, shadowed змінні.
slither . --config-file slither.config.json --print human-summary
myth analyze --solc-json mythril.json contracts/Protocol.sol --execution-timeout 120 --max-depth 22
Echidna — фаззинг: ви описуєте інваріанти як Solidity-функції, Echidna генерує мільйони випадкових транзакцій.
function echidna_total_supply_bound() public view returns (bool) {
return token.totalSupply() <= token.MAX_SUPPLY();
}
Для протоколів, що взаємодіють з Uniswap, Aave, Compound, проводимо fork-тестування на актуальному mainnet знімку — перевіряємо реальні взаємодії.
| Метод аналізу |
Що знаходить |
Швидкість |
Точність |
| Ручний код-рев'ю |
Логічні помилки, бізнес-логіка |
Повільно |
Висока |
| Статичний аналіз (Slither) |
Reentrancy, небезпечні патерни |
Швидко |
Середня |
| Символічне виконання (Mythril) |
Складні шляхи атак |
Середньо |
Висока |
| Фаззинг (Echidna) |
Інваріанти, крайні випадки |
Повільно |
Дуже висока |
Ручний аудит знаходить у 2-3 рази більше критичних вразливостей, ніж автоматичні інструменти. Середня економія від запобігання одній атаці може становити мільйони доларів.
Специфіка аудиту upgradeable контрактів
Proxy-патерни (TransparentProxy, UUPS, Beacon) додають поверхню атаки:
Storage collision: proxy та implementation використовують один storage-простір. Рекомендуємо ERC-7201 для ізоляції:
bytes32 private constant MAIN_STORAGE_LOCATION = 0x...;
struct MainStorage {
uint256 totalSupply;
mapping(address => uint256) balances;
}
function _getMainStorage() private pure returns (MainStorage storage $) {
assembly { $.slot := MAIN_STORAGE_LOCATION }
}
Uninitialized implementation: прямий виклик initialize() на implementation-контракті може скомпрометувати систему. Рішення — _disableInitializers() у constructor.
Відсутність upgrade-функції в новій реалізації: якщо задеплоїти implementation без upgradeTo, контракт назавжди втрачає можливість оновлення.
Що входить у підсумковий звіт?
- Докладний опис кожної вразливості із зазначенням severity (Critical/High/Medium/Low/Informational)
- PoC-експлойт для кожної знайденої проблеми
- Рекомендації щодо виправлення з прикладами коду
- Розділ з gas optimization (необов'язковий, але корисний)
- Доступ до репозиторію з PoC та фінальний код після fix review
- Консультація після фіксу: відповіді на питання, допомога в деплої
Коли потрібно проводити повторний аудит?
Після кожної значної зміни коду: додавання нових функцій, оновлення залежностей, зміни proxy-реалізації. Також перед великими міграціями (наприклад, перехід на нову версію Solidity) або при збільшенні TVL. Встановлення bug bounty з винагородою від $50,000 приваблює кращих хакерів та доповнює аудит.
Етапи роботи: від коду до звіту
- Підготовка: фінальна версія коду (feature freeze), документація архітектури, threat model, тест-кейси.
- Автоматизований аналіз (день 1-2): Slither, Mythril, Echidna — збір findings, відсів false positives.
- Ручний аналіз (день 3-12): систематичний код-рев'ю, моделювання атак, перевірка інваріантів, бізнес-логіка.
- Тестування (день 8-14, паралельно): PoC-експлойти для знайдених вразливостей, fork-тести.
- Звіт та remediation (день 14-20): докладний звіт із severity, PoC, рекомендаціями. Команда виправляє, ми перевіряємо.
- Fix Review (3-5 днів): верифікація, що виправлення не вводять нових вразливостей.
Орієнтовні терміни
| Тип протоколу |
Обсяг коду |
Термін аудиту |
| Простий ERC-20 + vesting |
< 500 рядків |
1-2 тижні |
| DeFi протокол (lending/AMM) |
1000-3000 рядків |
3-5 тижнів |
| Комплексний протокол з proxy |
3000-10000 рядків |
5-8 тижнів |
| Cross-chain мости |
будь-який обсяг |
6-12 тижнів |
Що аудит не гарантує
Аудит знижує ризик, але не усуває його. Аудитори — люди, вони пропускають баги. Декілька відомих експлойтів (Euler, Nomad, Wormhole) проходили аудити. Правильна стратегія: аудит + bug bounty (Immunefi) + поступовий rollout з лімітами TVL + monitoring (Forta).
Аудит — необхідна, але недостатня умова безпеки. Оцініть ризики свого проєкту: отримайте консультацію з безпеки вашого контракту — наші інженери підготують індивідуальну пропозицію.
Аудит смарт-контрактів: як знаходять те, що не бачить компілятор
Коли протокол втрачає значні кошти через flash loan атаку на функцію, яку аудитори дивилися наживо — це не випадковість. Це системна прогалина в методології. Наш досвід показує: вразливість живе в контракті більше року, а компілятор мовчить. Ми перебудували процес аудиту так, щоб ловити такі кейси до деплою.
Що не знайде статичний аналіз?
Slither — стандартний перший інструмент. Знаходить reentrancy, integer overflow (в старих версіях Solidity), неправильне використання tx.origin, shadowing змінних, неініціалізовані сховища. На реальному проекті Slither видає десятки попереджень, з яких критичних — 0‑2. Решта — інформаційний шум.
Slither не знайде логічну вразливість. Якщо withdraw коректно перевіряє баланс і коректно оновлює стан, але бізнес-логіка дозволяє подвійне списання через два різні шляхи кодової бази — Slither промовчить.
Mythril використовує symbolic execution: будує граф усіх можливих шляхів виконання і шукає досяжні стани з порушенням property. Працює добре на ізольованих контрактах. На протоколі з 20 контрактів з cross‑contract викликами — path explosion, аналіз зависає або видає false positive.
Обидва інструменти обов'язкові як перший pass. Але вони не замінюють ручний аналіз.
Fuzzing: де Echidna та Foundry знаходять реальні баги?
Echidna — property‑based fuzzer від Trail of Bits. Ідея: формулюєш інваріанти контракту як Solidity‑функції (echidna_invariant), Echidna генерує випадкові послідовності викликів і намагається зламати інваріант.
Приклад інваріанта для lending протоколу:
function echidna_total_assets_ge_liabilities() public view returns (bool) {
return totalAssets() >= totalLiabilities();
}
Echidna знайде послідовність deposit → borrow → liquidate → repay, яка порушує цей інваріант. Руками такий кейс не побудуєш — комбінацій занадто багато.
Foundry fuzzing (forge test --fuzz-runs 100000) простіший в інтеграції, якщо команда вже на Foundry. Підтримує stateful fuzzing через invariant тести. В реальному проекті: auditing vault контракт, Foundry fuzz за 40 хвилин знайшов edge case, при якому maxWithdraw повертав значення більше фактичного балансу при конкретному співвідношенні shares/assets після кількох донатів. Hardhat unit‑тести цей кейс пропускали — там не було такої комбінації параметрів.
Medusa (від Trail of Bits, новіша за Echidna) підтримує corpus‑guided fuzzing і працює швидше на великих контрактах. Якщо обсяг кодової бази > 5000 рядків Solidity — дивимося на Medusa.
Як інваріанти допомагають виявити критичні уразливості?
Формальна верифікація доводить, що контракт задовольняє специфікації для всіх можливих вхідних даних — не для N випадкових, а математично для всіх. Інструменти: Certora Prover, K Framework, Halmos.
Certora працює з CVL (Certora Verification Language): пишеш rules і invariants, Prover транслює їх у SMT‑формули і перевіряє через Z3/CVC5. MakerDAO, Aave, Uniswap використовують Certora в CI/CD pipeline — кожен PR верифікується автоматично.
Обмеження: не працює з необмеженими циклами, складно справляється з hash functions і signature verification. Для контрактів з простою математикою (AMM, lending) — відмінно. Для контрактів з довільними зовнішніми викликами — складно написати достатньо повну специфікацію.
Formal verification має сенс для контрактів, які: керують значним TVL, оновлюються рідко, мають чітко формалізовані інваріанти. Для продуктів, що швидко ітеруються, співвідношення витрат і користі не на користь верифікації.
Вектори атак, які пропускають джуніор‑аудитори
Storage collision в proxy патерні. Transparent proxy і UUPS використовують конкретні слоти для зберігання адреси імплементації (EIP‑1967). Якщо в імплементації випадково оголошена змінна в слоті 0, яка перетинається з proxy storage — отримуємо silent override. Slither це не впіймає, якщо proxy та імплементація в різних файлах.
Read‑only reentrancy. Класичний reentrancy guard захищає від зміни стану при рекурсивному виклику. Але якщо зовнішній контракт читає стан через view-функцію в середині транзакції — guard не допомагає. Кілька років тому Curve pools стали вектором атаки саме через це: зовнішній протокол читав get_virtual_price під час reentrancy‑вразливого стану Curve.
Oracle manipulation через TWAP. Spot price — стандартна ціль для flash loan атаки. TWAP складніше маніпулювати, але не неможливо: на малоліквідних парах Uniswap v2 можна зсунути TWAP за кілька блоків при достатньому капіталі. Правильний захист — використовувати Chainlink як primary oracle з TWAP як fallback, з перевіркою deviation threshold.
Gas griefing на unbounded loop. Функція ітерується по масиву користувачів. Атакуючий додає тисячі адрес з нульовими балансами — вартість виклику функції зростає до gas limit, функція стає недоступною. Захист: pull‑pattern замість push, обмеження довжини масивів, batch‑обробка зі збереженням позиції.
Front‑running на MEV. Транзакція видна в mempool до включення в блок. MEV‑бот бачить addLiquidity на значну суму, вставляє свій swap перед нею (sandwich attack). Для AMM це частина моделі. Для протоколів з ціновими функціями — потрібен minAmountOut / deadline параметр і його обов'язкова перевірка.
Структура повного аудиту
-
Scope definition і автоматичний аналіз (1‑2 дні). Фіксуємо commit hash, версію компілятора, список out‑of‑scope. Запускаємо Slither, Mythril, Aderyn. Triage: відокремлюємо реальні критичні баги від false positive. Складаємо карту залежностей контрактів.
-
Ручний аналіз (5‑15 днів). Кожен контракт порядково. Особлива увага: всі external і public функції, всі transfer/call/delegatecall, всі місця, де змінюється стан перед перевіркою або після зовнішнього виклику, всі математичні операції з участю користувацьких inputs. В середньому 95% знайдених уразливостей — логічні, а не технічні.
-
Fuzzing і тестування (2‑5 днів). Echidna або Foundry invariant tests для критичних інваріантів. Fork mainnet тести — перевіряємо поведінку в реальному оточенні з реальними оракулами. Наприклад, за 4 дні fuzzing знаходить в середньому 3 edge cases, не покритих unit‑тестами.
-
Звіт і мітигація. Звіт з severity (Critical/High/Medium/Low/Informational), описом вектора атаки, PoC‑кодом для Critical/High. Розробники виправляють, аудитори роблять re‑audit виправлень.
| Severity |
Приклади |
Чи потребує re‑audit |
| Critical |
Виведення коштів, несанкціоноване перенесення власності |
Завжди |
| High |
Маніпуляція, DoS на ключові функції |
Завжди |
| Medium |
Некоректна поведінка при edge cases |
Рекомендується |
| Low |
Газ‑неефективність, опечатки в events |
За бажанням |
Аудит у CI/CD
Нормальна практика для зрілих протоколів: Slither і Aderyn запускаються в GitHub Actions на кожен PR. Certora Prover — на merge в main. Це не замінює повний аудит перед деплоєм, але ловить регресії.
# .github/workflows/audit.yml
- name: Run Slither
uses: crytic/[email protected]
with:
target: 'src/'
slither-args: '--filter-paths "test|mock|script"'
Чек‑лист обов'язкових перевірок перед деплоєм
- Всі external функції мають перевірки доступу (
onlyOwner, onlyRole)
- Використання
SafeERC20 для зовнішніх токенів
- Відсутність
delegatecall на невідомі адреси
- Перевірка на reentrancy у всіх функціях з зовнішніми викликами
- Наявність
minAmountOut і deadline в AMM‑функціях
- Використання перевіреного оракула (Chainlink) з deviation threshold
Інструменти аудиту: порівняння
| Інструмент |
Тип аналізу |
Що знаходить |
Обмеження |
| Slither |
Статичний |
Reentrancy, integer overflow, access control |
Пропускає логічні уразливості |
| Mythril |
Symbolic execution |
Досяжні стани з порушенням property |
Path explosion на великих базах |
| Echidna |
Fuzzing (property‑based) |
Порушення інваріантів |
Потребує написання інваріантів |
| Certora |
Formal verification |
Математичне доведення властивостей |
Не працює з хешами/підписами |
Що входить в роботу (deliverables)
- Повний звіт у PDF з CVSS‑оцінками кожної уразливості
- PoC‑код для всіх Critical і High (відтворюваний в тестовому середовищі)
- Рекомендації щодо виправлення з прикладом коду
- Re‑audit після внесення правок (до двох ітерацій)
- Коротка пам'ятка для розробників щодо подальшої експлуатації
- Підтримка після деплою протягом 30 днів (консультації та розбір інцидентів)
Терміни
Аудит простого токена або NFT‑контракту — 3‑5 робочих днів. DeFi протокол з lending/AMM — 2‑4 тижні. Повний стек з кількома протоколами, cross‑chain, proxy upgrades — 4‑8 тижнів. Re‑audit виправлень — 3‑7 днів окремо.
Наша команда має 7+ років досвіду в безпеці смарт‑контрактів, перевірила 100+ проектів з сумарним TVL понад значну суму. Гарантуємо, що в процесі ми не пропустимо жоден відомий вектор — використовуємо ліцензовані версії Slither та найкращі конфігурації fuzzer'ів. Запобігнуті збитки для клієнтів оцінюються в значну суму.
Оцініть ваш проект — ми безкоштовно проаналізуємо код і запропонуємо комерційну пропозицію протягом 2 днів. Замовте аудит з гарантією якості та отримайте знижку на re‑audit при повторному зверненні.