Більшість експлойтів на крос-чейн мостах тривають від однієї транзакції до кількох хвилин. Атака на Wormhole ($326M) — одна підписана транзакція, 0 часу на реакцію. Ronin Bridge ($620M) — 5 з 9 валідаторів скомпрометовано, міст жив 6 днів до виявлення. Якщо система моніторингу не аналізує як on-chain активність, так і стан валідаторів — кошти губляться безповоротно. Наше завдання: побудувати систему, яка ловить атаку до або під час, даючи час на паузу протоколу.
Ми розробляємо не просто дашборд із графіками. Це продуктивна система з alert-логікою, circuit breaker-ами та чіткими playbook реагування. Наш досвід включає інтеграцію з OpenZeppelin Defender та Tenderly Web3 Actions для автоматизованої зупинки мосту при аномаліях. За 5+ років ми реалізували 15+ систем моніторингу для DeFi-протоколів, що дозволяє нам гарантувати надійність рішення.
Основні загрози для крос-чейн мостів
Мости — критична інфраструктура DeFi. Основні вектори атак:
-
Reentrancy у смарт-контрактах — класична атака, посилена cross-chain викликами.
- Компрометація валідаторів / relayer — зловмисник отримує більшість підписів і підтверджує неправдиві транзакції.
- Oracle manipulation — маніпуляція ціновим фідом для нечесного обміну.
- Flash loan атаки — миттєва позика коштів для створення дисбалансу пулу.
Система моніторингу повинна відстежувати кожен із цих сценаріїв як на вихідному ланцюзі, так і на цільовому.
Як система детектує аномалії?
Система будується на трьох рівнях, кожен з різним часом реакції:
Рівень 1 — on-chain real-time (< 1 блок). Моніторинг pending транзакцій у mempool на підозрілі патерни: множинні cross-chain виклики, незвичні обсяги, взаємодія з новими контрактами. Технічно складно (потрібен доступ до private mempool через Flashbots або Eden), але дає найраніше попередження.
Рівень 2 — on-chain per-block (< 12 секунд на Ethereum). Аналіз кожного нового блоку: події мосту (BridgeInitiated, BridgeFinalized), зміни балансів у пулах ліквідності, аномальні накопичення голосів валідаторів.
Рівень 3 — off-chain aggregated (хвилини-години). Агрегація даних за період, trend analysis, cross-chain кореляції. Виявляє повільно розвиваючі атаки: дрейф лімітів, накопичення підписів неактивними валідаторами.
Як працює on-chain circuit breaker для мостів?
Найцінніший компонент — можливість автоматично або напівавтоматично паузити міст при виявленні атаки. На відміну від стандартного Pausable (OZ), ми використовуємо rate limiting на рівні контракту, який спрацьовує автоматично при аномальному обсязі переказу:
contract BridgeWithCircuitBreaker is Pausable, AccessControl {
bytes32 public constant GUARDIAN_ROLE = keccak256("GUARDIAN_ROLE");
uint256 public maxTransferPerBlock;
uint256 public maxTransferPerHour;
uint256 private _transferredThisBlock;
uint256 private _transferredThisHour;
uint256 private _lastBlockNumber;
uint256 private _lastHourTimestamp;
modifier withinRateLimit(uint256 amount) {
_updateRateLimitCounters();
require(_transferredThisBlock + amount <= maxTransferPerBlock, "Block rate limit exceeded");
require(_transferredThisHour + amount <= maxTransferPerHour, "Hour rate limit exceeded");
_transferredThisBlock += amount;
_transferredThisHour += amount;
_;
}
function transfer(address token, uint256 amount, address to) external whenNotPaused withinRateLimit(amount) {
// ... логіка переказу
}
function emergencyPause() external onlyRole(GUARDIAN_ROLE) {
_pause();
emit EmergencyPause(msg.sender, block.timestamp);
}
function _updateRateLimitCounters() private {
if (block.number > _lastBlockNumber) {
_transferredThisBlock = 0;
}
if (block.timestamp >= _lastHourTimestamp + 1 hours) {
_transferredThisHour = 0;
_lastHourTimestamp = block.timestamp;
}
}
}
Rate limits не блокують нормальну роботу, але зупиняють drain-атаку з великими обсягами. Встановити ліміти на рівні 2-3x типового обсягу мосту — баланс між UX та безпекою.
Чому інваріантний моніторинг ефективний проти експлойтів?
Для кожного крос-чейн мосту існують математичні інваріанти, які завжди повинні дотримуватися. Моніторинг інваріантів — елегантний спосіб зловити експлойт:
- Bridge з mint/burn:
sum(totalSupplyOnSource) + sum(totalSupplyOnDestination) == constant (без урахування комісій).
- AMM міст:
reserve0 * reserve1 >= k для кожної пари.
Приклад Python-функції для перевірки:
async def check_invariants(block_number: int):
total_locked = await bridge.functions.totalLocked().call(block_identifier=block_number)
total_minted = await bridge.functions.totalMinted().call(block_identifier=block_number)
if total_locked != total_minted:
await alert_critical(f"INVARIANT VIOLATED at block {block_number}: locked ({total_locked}) != minted ({total_minted})")
Порушення інваріанту — вірна ознака багу або активної атаки.
Автоматична пауза через off-chain Keeper
OpenZeppelin Defender Actions — serverless функції, що реагують на on-chain події. Приклад: моніторинг TVL пулу мосту та автоматична пауза при різкому падінні:
const { ethers } = require("ethers");
module.exports = async function(credentials) {
const provider = new ethers.providers.JsonRpcProvider(credentials.secrets.ALCHEMY_URL);
const vault = new ethers.Contract(VAULT_ADDRESS, VAULT_ABI, provider);
const currentTVL = await vault.totalAssets();
const previousTVL = await storage.get('previousTVL') || currentTVL;
const dropPercent = (previousTVL - currentTVL) * 100n / previousTVL;
if (dropPercent > 15n) {
const signer = credentials.relayer.getSigner();
const guardian = new ethers.Contract(GUARDIAN_ADDRESS, GUARDIAN_ABI, signer);
await guardian.emergencyPause();
await notifySlack(`CRITICAL: TVL dropped ${dropPercent}% — bridge paused`);
}
await storage.put('previousTVL', currentTVL.toString());
};
Alerting та incident response
Severity алертів налаштована так:
| Severity |
Критерій |
Реакція |
Час реакції |
| P1 Critical |
Активна атака, втрата funds |
Негайна пауза + дзвінки команді |
< 2 хвилини |
| P2 High |
Порушення інваріанту, oracle маніпуляція |
Пауза + рев'ю протягом години |
< 15 хвилин |
| P3 Medium |
Аномальний обсяг, незвична поведінка |
Рев'ю наступного дня |
< 4 години |
| P4 Low |
Статистичне відхилення |
Щотижневе рев'ю |
Async |
P1/P2 алерти відправляються в PagerDuty з on-call ротацією, P3 — у Telegram чат, P4 — у Slack daily digest.
Приклад Incident Response Playbook
Сценарій: TVL drop > 15% в одному блоці.
- On-call черговий отримує PagerDuty alert (< 2 хв).
- Перевірити Etherscan: знайти транзакцію, причину TVL drop.
- Якщо експлойт — викликати
emergencyPause() через Defender Relayer.
- Повідомити команду в Signal (не в публічний Telegram).
- Через 15 хвилин: публічне повідомлення користувачам про паузу.
- Post-mortem аналіз: як атака пройшла, як fix, коли unpause.
Playbook тестуємо на drill exercises у тестовому середовищі.
Що входить в нашу роботу
- Аудит архітектури вашого мосту та виявлення критичних інваріантів
- Розробка смарт-контрактів circuit breaker з rate limiting (Solidity)
- Розгортання event indexer та детектора аномалій (Python / TypeScript)
- Налаштування alerting pipeline (PagerDuty, Telegram, Slack)
- ML-модель Isolation Forest для невідомих атак
- Документація та протестовані playbook реагування на інциденти
- Навчання вашої команди роботі з системою
- Підтримка після запуску
Терміни розробки
| Компонент |
Технологія |
Час розробки |
| Event indexer |
web3.py / viem subscriptions |
1-2 тижні |
| Rule-based detector |
Python rules engine |
1-2 тижні |
| Invariant monitor |
Python + contract calls |
1 тиждень |
| Circuit breaker contract |
Solidity + OZ Pausable |
1 тиждень |
| Alerting pipeline |
PagerDuty + Telegram |
3-5 днів |
| ML anomaly detection |
scikit-learn |
2-3 тижні |
| Defender Autotasks |
JavaScript + Defender SDK |
1 тиждень |
| Dashboard |
Grafana + InfluxDB |
1-2 тижні |
MVP система (rule-based + alerting + базовий circuit breaker) — 4-6 тижнів. Повна система з ML, автоматичною паузою, dashboard та playbook-ами — 10-14 тижнів.
Вартість розраховується після аналізу архітектури мосту. Враховуючи, що вартість атаки може сягати сотень мільйонів доларів, інвестиції в моніторинг окупаються з першою запобігнутою атакою. Зв'яжіться з нами для консультації — оцінимо ваш проект і запропонуємо оптимальне рішення. Замовте розробку системи моніторингу вже сьогодні.
Аудит смарт-контрактів: як знаходять те, що не бачить компілятор
Коли протокол втрачає значні кошти через 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 при повторному зверненні.