Разработка системы incident response для мостов

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1 услугВсе 1306 услуг
Разработка системы incident response для мостов
Сложная
~1-2 недели
Часто задаваемые вопросы
Направления блокчейн-разработки
Этапы блокчейн-разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1221
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1163
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    855
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1056
  • image_logo-advance_0.png
    Разработка логотипа компании B2B Advance
    561
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    828

Разработка системы детекции аномалий TVL

TVL (Total Value Locked) — ключевая метрика здоровья DeFi протокола. Резкое падение TVL, аномальные депозиты/выводы, необъяснимые паттерны в движении средств — всё это может сигнализировать об эксплойте, rug pull или координированной атаке. Система детекции аномалий TVL работает как ранняя система предупреждения: протокол узнаёт о проблеме не из Twitter, а из автоматического алерта за несколько минут до того, как об этом напишут в новостях.

Что считается аномалией TVL

Нужно разделить: нормальная волатильность TVL (рыночные движения, крупный whale выход) и аномалия (эксплойт, flash loan атака, rug pull). Характеристики аномалий:

  • Скорость: нормальные изменения занимают часы/дни. Аномалии — секунды или один блок
  • Масштаб: 10–20% TVL изменение за несколько блоков — экстремально редко при нормальной работе
  • Паттерн: эксплойт часто сопровождается серией транзакций по одному адресу
  • Flash loan: резкий deposit → immediate withdrawal в том же или следующем блоке

Архитектура системы мониторинга

Blockchain Events (WebSocket)
    ↓
Event Processor (Node.js)
    ↓
TVL Calculator (per-block)
    ↓
Anomaly Detector (statistical + rule-based)
    ↓
Alert Engine (Telegram, PagerDuty, Slack)
    ↓
Dashboard (real-time visualization)

Индексация TVL per block

import { createPublicClient, webSocket, parseAbiItem } from 'viem';

interface TVLSnapshot {
  blockNumber: bigint;
  timestamp: number;
  totalTVL: bigint;        // в USD, scaled 1e6
  assetBreakdown: {
    asset: string;
    amount: bigint;
    usdValue: bigint;
  }[];
  deltaFromPrev: bigint;   // изменение от предыдущего блока
  deltaPct: number;        // процентное изменение
}

class TVLTracker {
  private client: ReturnType<typeof createPublicClient>;
  private snapshots: TVLSnapshot[] = [];
  private poolAddresses: string[];
  
  async onNewBlock(blockNumber: bigint) {
    const snapshot = await this.calculateTVL(blockNumber);
    
    if (this.snapshots.length > 0) {
      const prev = this.snapshots[this.snapshots.length - 1];
      snapshot.deltaFromPrev = snapshot.totalTVL - prev.totalTVL;
      snapshot.deltaPct = Number(snapshot.deltaFromPrev * 10000n / prev.totalTVL) / 100;
    }
    
    this.snapshots.push(snapshot);
    
    // Детектируем аномалии
    await this.detectAnomalies(snapshot);
    
    // Храним только последние 1000 блоков в памяти
    if (this.snapshots.length > 1000) this.snapshots.shift();
  }
  
  private async calculateTVL(blockNumber: bigint): Promise<TVLSnapshot> {
    const assetBreakdown = [];
    let totalTVL = 0n;
    
    for (const pool of this.poolAddresses) {
      const balance = await this.getPoolBalance(pool, blockNumber);
      const usdValue = await this.getUSDValue(balance.asset, balance.amount);
      assetBreakdown.push({ ...balance, usdValue });
      totalTVL += usdValue;
    }
    
    const block = await this.client.getBlock({ blockNumber });
    
    return {
      blockNumber,
      timestamp: Number(block.timestamp),
      totalTVL,
      assetBreakdown,
      deltaFromPrev: 0n,
      deltaPct: 0
    };
  }
}

Статистические методы детекции

Z-score детектор

Аномалия определяется как отклонение, превышающее N стандартных отклонений от исторического среднего:

class ZScoreDetector {
  private windowSize = 100;  // последние 100 блоков для rolling statistics
  
  detectAnomaly(snapshots: TVLSnapshot[]): AnomalyAlert | null {
    if (snapshots.length < this.windowSize) return null;
    
    const recent = snapshots.slice(-this.windowSize);
    const deltas = recent.map(s => s.deltaPct);
    
    // Вычисляем rolling mean и std
    const mean = deltas.reduce((a, b) => a + b, 0) / deltas.length;
    const variance = deltas.reduce((sum, d) => sum + Math.pow(d - mean, 2), 0) / deltas.length;
    const std = Math.sqrt(variance);
    
    const latest = snapshots[snapshots.length - 1];
    const zScore = std > 0 ? (latest.deltaPct - mean) / std : 0;
    
    if (Math.abs(zScore) > 4) {  // 4 sigma — экстремально редко (~1 в 31,500 блоков)
      return {
        type: 'STATISTICAL_ANOMALY',
        severity: Math.abs(zScore) > 6 ? 'critical' : 'high',
        message: `TVL change ${latest.deltaPct.toFixed(2)}% is ${Math.abs(zScore).toFixed(1)}σ from mean`,
        blockNumber: latest.blockNumber,
        tvlDelta: latest.deltaFromPrev
      };
    }
    
    return null;
  }
}

Rule-based детектор: жёсткие пороги

interface AnomalyRule {
  name: string;
  check: (current: TVLSnapshot, history: TVLSnapshot[]) => AnomalyAlert | null;
}

const ANOMALY_RULES: AnomalyRule[] = [
  {
    name: 'RAPID_DRAIN',
    check: (current, history) => {
      // TVL упал более чем на 20% за последние 10 блоков
      if (history.length < 10) return null;
      const tenBlocksAgo = history[history.length - 10];
      const changePct = Number(
        (current.totalTVL - tenBlocksAgo.totalTVL) * 10000n / tenBlocksAgo.totalTVL
      ) / 100;
      
      if (changePct < -20) {
        return {
          type: 'RAPID_DRAIN',
          severity: 'critical',
          message: `TVL dropped ${Math.abs(changePct).toFixed(1)}% in 10 blocks`,
          blockNumber: current.blockNumber,
          tvlDelta: current.totalTVL - tenBlocksAgo.totalTVL
        };
      }
      return null;
    }
  },
  {
    name: 'SINGLE_BLOCK_ANOMALY',
    check: (current) => {
      // Изменение за один блок превышает 10%
      if (Math.abs(current.deltaPct) > 10) {
        return {
          type: 'SINGLE_BLOCK_ANOMALY',
          severity: current.deltaPct < -10 ? 'critical' : 'high',
          message: `Single block TVL change: ${current.deltaPct.toFixed(2)}%`,
          blockNumber: current.blockNumber,
          tvlDelta: current.deltaFromPrev
        };
      }
      return null;
    }
  },
  {
    name: 'FLASH_LOAN_PATTERN',
    check: (current, history) => {
      // Большой deposit в предыдущем блоке + большой withdrawal сейчас
      if (history.length < 2) return null;
      const prev = history[history.length - 1];
      
      const prevIncrease = prev.deltaPct > 15;   // +15% в предыдущем блоке
      const currentDecrease = current.deltaPct < -10;  // -10% сейчас
      
      if (prevIncrease && currentDecrease) {
        return {
          type: 'FLASH_LOAN_PATTERN',
          severity: 'critical',
          message: `Flash loan pattern: +${prev.deltaPct.toFixed(1)}% then -${Math.abs(current.deltaPct).toFixed(1)}%`,
          blockNumber: current.blockNumber,
          tvlDelta: current.deltaFromPrev
        };
      }
      return null;
    }
  }
];

Анализ крупных транзакций

Помимо aggregate TVL, важно отслеживать индивидуальные крупные транзакции:

class LargeTransactionMonitor {
  // Порог для "whale" транзакции — 1% от TVL
  private whaleTvlThreshold = 0.01;
  
  async monitorWithdrawals(
    protocolAddress: string,
    currentTVL: bigint
  ) {
    const client = createPublicClient({ transport: webSocket(WS_RPC) });
    
    client.watchContractEvent({
      address: protocolAddress,
      abi: PROTOCOL_ABI,
      eventName: 'Withdraw',
      onLogs: async (logs) => {
        for (const log of logs) {
          const withdrawAmount = log.args.amount as bigint;
          const usdValue = await getUSDValue(log.args.asset, withdrawAmount);
          
          const pctOfTVL = Number(usdValue * 10000n / currentTVL) / 100;
          
          if (pctOfTVL > this.whaleTvlThreshold * 100) {
            await this.alertWhaleWithdrawal({
              address: log.args.user,
              amount: withdrawAmount,
              usdValue,
              pctOfTVL,
              txHash: log.transactionHash,
              blockNumber: log.blockNumber
            });
          }
        }
      }
    });
  }
}

Интеграция с DeFiLlama

DeFiLlama предоставляет исторические TVL данные для сравнения:

async function checkTVLConsistency(
  protocolSlug: string,
  ourTVL: bigint
): Promise<{ consistent: boolean; defiLlamaTVL: number; deviation: number }> {
  const response = await fetch(`https://api.llama.fi/protocol/${protocolSlug}`);
  const data = await response.json();
  
  // Последнее значение из DeFiLlama (в USD)
  const chainTvls = data.chainTvls;
  const latestTVL = Object.values(chainTvls)[0] as any;
  const defiLlamaTVL = latestTVL.tvl[latestTVL.tvl.length - 1].totalLiquidityUSD;
  
  const ourTVLUSD = Number(ourTVL) / 1e6;  // если scaled 1e6
  const deviation = Math.abs(ourTVLUSD - defiLlamaTVL) / defiLlamaTVL * 100;
  
  return {
    consistent: deviation < 5,  // допустимо 5% расхождение
    defiLlamaTVL,
    deviation
  };
}

Alert система

interface AlertChannel {
  send(alert: AnomalyAlert): Promise<void>;
}

class TelegramAlertChannel implements AlertChannel {
  async send(alert: AnomalyAlert) {
    const emoji = alert.severity === 'critical' ? '🚨' : '⚠️';
    const message = `
${emoji} *TVL ANOMALY DETECTED*
Protocol: \`${PROTOCOL_NAME}\`
Type: \`${alert.type}\`
Block: \`${alert.blockNumber}\`
Message: ${alert.message}
TVL Delta: \`$${formatUSD(alert.tvlDelta)}\`
Time: ${new Date().toISOString()}
    `;
    
    await fetch(`https://api.telegram.org/bot${BOT_TOKEN}/sendMessage`, {
      method: 'POST',
      body: JSON.stringify({
        chat_id: ALERT_CHAT_ID,
        text: message,
        parse_mode: 'Markdown',
        disable_web_page_preview: true
      })
    });
  }
}

class PagerDutyChannel implements AlertChannel {
  async send(alert: AnomalyAlert) {
    if (alert.severity !== 'critical') return;  // PD только для critical
    
    await fetch('https://events.pagerduty.com/v2/enqueue', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        routing_key: PAGERDUTY_KEY,
        event_action: 'trigger',
        payload: {
          summary: `TVL Anomaly: ${alert.type} on ${PROTOCOL_NAME}`,
          severity: 'critical',
          source: 'TVL Monitor',
          custom_details: alert
        }
      })
    });
  }
}

Метрики для dashboard

Кроме алертов — real-time dashboard с ключевыми метриками:

Метрика Описание Update frequency
Current TVL Текущий TVL в USD Per block
24h TVL Change Изменение за 24 часа Per block
TVL Velocity Rate of change ($/block) Per block
Largest single withdrawal (24h) Самый крупный вывод Per block
Anomaly score Composite score (0–100) Per block
Alert history Последние N алертов Per alert

Система детекции аномалий — не замена аудиту, а дополнительный слой. Аудит находит уязвимости до запуска. Мониторинг TVL даёт шанс среагировать на атаку в процессе — остановить контракт через emergency pause, уведомить пользователей, связаться с биржами для freeze аккаунтов атакующего.