Разработка системы мониторинга контрактов на нескольких сетях
Многосетевой мониторинг — это не просто "запустить несколько Etherscan вкладок". Когда протокол работает одновременно на Ethereum, Arbitrum, Polygon, Base и Optimism, и между ними ходят bridged активы, задержка в обнаружении аномалии в одной сети может стоить средств пользователей в другой. Проблема не в получении данных — данные доступны. Проблема в правильной агрегации, корреляции событий между сетями и скорости реакции.
Архитектура мониторинговой системы
Источники данных
RPC nodes — прямые вызовы к EVM-нодам через WebSocket для real-time событий. Для каждой сети нужен надёжный RPC с поддержкой eth_subscribe:
// Подписка на события контракта через WebSocket
const provider = new ethers.WebSocketProvider(RPC_WS_URL);
const contract = new ethers.Contract(address, abi, provider);
contract.on('Transfer', (from, to, value, event) => {
emitEvent({
network: 'arbitrum',
block: event.log.blockNumber,
txHash: event.log.transactionHash,
type: 'Transfer',
data: { from, to, value }
});
});
Проблема single RPC: публичные ноды ненадёжны, пропускают события при высокой нагрузке. Решение: минимум 2 независимых провайдера на сеть (Alchemy + QuickNode, или собственная нода). Дедупликация событий по (chainId, txHash, logIndex).
The Graph / Subgraph — для исторических данных и сложных запросов. Mediation layer поверх raw RPC. Задержка 1–3 блока, но идеально для аналитических запросов и cross-network сверки балансов.
Дефолтный стек по сетям:
| Сеть | Block time | Рекомендуемый RPC | Finality |
|---|---|---|---|
| Ethereum | ~12 сек | Alchemy/Infura | ~64 блока (~13 мин) |
| Arbitrum One | ~0.25 сек | Arbitrum RPC / Alchemy | L1 finality |
| Polygon PoS | ~2 сек | Polygon RPC / QuickNode | ~256 блоков |
| Base | ~2 сек | Base RPC / Alchemy | L1 finality |
| Optimism | ~2 сек | Optimism RPC / Alchemy | L1 finality |
| BNB Chain | ~3 сек | BSC RPC / NodeReal | ~75 блоков |
Event Processing Pipeline
Сырые события нельзя сразу анализировать — нужна нормализация и enrichment:
RPC Listener → Message Queue (Kafka/Redis Streams) → Event Processor → Alert Engine → Notification
↓
Time-series DB (InfluxDB/TimescaleDB)
↓
Analytics Dashboard
Message Queue — буферизация при спайках. При резком росте on-chain активности (например, большой liquidation cascade) events могут приходить быстрее чем их можно обработать. Kafka с retention 24h позволяет replay при падении процессора.
Event Processor — нормализация событий с разных сетей в единый формат, декодирование ABI, enrichment (цены токенов, metadata аккаунтов), детектирование аномалий.
Alert Engine — правила на нормализованных событиях. Stateful правила требуют state store (Redis). Примеры правил:
# Пример: обнаружение нетипично большой транзакции
class LargeTransferAlert(AlertRule):
def evaluate(self, event: NormalizedEvent) -> Optional[Alert]:
if event.type != 'Transfer':
return None
usd_value = event.data['value'] * get_token_price(event.data['token'])
threshold = self.get_dynamic_threshold(
token=event.data['token'],
window='24h',
multiplier=10.0 # 10x от среднего за 24 часа
)
if usd_value > threshold:
return Alert(
severity='HIGH',
message=f'Large transfer: ${usd_value:,.0f} on {event.network}',
context=event
)
Cross-Chain Correlation
Самая ценная функциональность для мультисетевых протоколов — связывание событий между сетями. Типичные сценарии:
Bridge monitoring — токен заблокирован на Ethereum, должен появиться на Arbitrum. Если за N минут не появился — алерт. Для этого нужен correlation engine:
class BridgeCorrelator:
def __init__(self, redis_client):
self.pending = {} # bridge_tx_hash -> pending event
def on_bridge_initiated(self, event):
key = f"bridge:{event.src_chain}:{event.tx_hash}"
self.redis.setex(key, 3600, json.dumps(event.to_dict()))
def on_bridge_completed(self, event):
# Ищем pending initiation по bridge nonce
key = f"bridge:{event.src_chain}:{event.bridge_nonce}"
pending = self.redis.get(key)
if not pending:
alert(f"Bridge completion without initiation: {event}")
return
initiation = json.loads(pending)
latency = event.timestamp - initiation['timestamp']
if latency > EXPECTED_BRIDGE_LATENCY[event.bridge_protocol]:
alert(f"Bridge latency anomaly: {latency}s")
TVL consistency check — суммарный TVL на L2s не должен превышать locked amount на L1. Периодическая проверка через subgraph queries с алертом при расхождении > X%.
Что мониторить: практический список
Security-critical события
-
Ownership transfers —
OwnershipTransferred,RoleGrantedна любом контракте протокола - Upgrade proposals — события от Timelock (новые proposals, исполнение)
- Large withdrawals — вывод > N% TVL за короткий период
- Flash loan usage — получение flash loan + взаимодействие с контрактом протокола в одной tx
- Oracle price deviations — цена в протоколе отклоняется от рыночной > X%
- Pause events — кто-то паузит контракт
Operational метрики
- Gas usage аномалии (резкий рост может означать inefficient execution или атаку)
- Failed transactions доля (рост failed tx у роутера может означать UI/API баг)
- Block inclusion latency для собственных транзакций (keeper bots, liquidation bots)
Business метрики
- TVL динамика по сетям
- Volume per network
- Unique active addresses
- Protocol revenue (fees collected)
Технический стек
OpenZeppelin Defender — если протокол уже использует OZ, Defender Sentinel покрывает базовый мониторинг с минимальной настройкой. Ограничение: слабая cross-chain корреляция, нет кастомной аналитики.
Tenderly Alerts — хорошо для development и staging, покрывает основные сети. Для production критичных систем — надстраивать поверх.
Собственная система — оправдана когда: нужна cross-chain корреляция, специфическая бизнес-логика в алертах, интеграция с внутренними системами, >10 контрактов на >3 сетях.
Стек для кастомной системы:
- Event ingestion: Node.js + ethers.js WebSocket listeners
- Message queue: Redis Streams (для небольших проектов) или Kafka (для высокой нагрузки)
- Storage: TimescaleDB для time-series, PostgreSQL для event metadata
- Alert rules: Python с rule engine
- Notifications: PagerDuty/OpsGenie для критических, Telegram/Discord для операционных
- Dashboard: Grafana над TimescaleDB
Реакция на алерты: не только уведомления
Мониторинг без автоматической реакции — половина системы. OpenZeppelin Defender Autotask или кастомный keeper бот:
- Аномально большой вывод → автоматическая пауза контракта (если паузер настроен на keeper)
- Oracle deviation → переключение на fallback oracle
- Bridge stuck > 2 часов → уведомление bridge operator + создание тикета
Автоматическая реакция требует тщательного аудита самого keeper бота — он становится критическим элементом безопасности.







