Developing DeFi Positions Monitoring System
Health factor 1.05 in Aave is 5% from liquidation. During high market volatility, from 1.05 to liquidation may be just minutes. User without monitoring system learns about problem after: position liquidated, liquidation bonus (8-15%) went to liquidator. On large positions this is thousands of dollars that could be saved by adding collateral in time.
What and How to Monitor: Technical Details
Health Factor: Formula and Alert Thresholds
In Aave v3, health factor is calculated as:
HF = (sum(collateral_i * liquidationThreshold_i * price_i)) / (sum(debt_j * price_j))
Via smart contract: IPool.getUserAccountData(address) returns healthFactor in format 1e18 = 1.0. Value below 1e18 — position is liquidatable.
Alert thresholds:
- HF < 1.5 — warning: add collateral
- HF < 1.2 — critical: immediate action
- HF < 1.05 — emergency: liquidation inevitable
Polling via multicall (MakerDAO Multicall3 at 0xcA11bde05977b3631167028862bE2a173976CA11) — single RPC call queries HF for all tracked addresses. For 100 addresses — one eth_call instead of 100. Polling interval: every 30 seconds normally, every 5 seconds when HF < 1.3.
Compound v3 and Euler: Mechanics Differences
Compound v3 has no single health factor — there's isLiquidatable(address) bool method and separately borrowBalanceOf + collateralBalanceOf. To understand liquidation proximity: borrowBalance / (collateralBalance * collateralFactor * price).
Euler (before 2023 pause) had subaccount system — one address = 256 independent subaccounts. Monitoring system must iterate subaccounts via eulerAccount = address(uint160(mainAddress) ^ subAccountId). This is non-obvious and many Euler monitoring systems ignored subaccounts.
Uniswap v3 LP Positions: Fee Accumulation and Out-of-Range
For LP monitoring need three metrics: current position USD value, accumulated fees, in/out-of-range status.
Current value via NonfungiblePositionManager.positions(tokenId) + calculate amount0/amount1 by current pool tick. Can do via static call to view function or via The Graph subgraph (query every 5 minutes).
Fees — periodically via NonfungiblePositionManager.collect with amount0Max = type(uint128).max in simulation mode (eth_call, not transaction). Or via feeGrowthInside calculation per Uniswap v3 whitepaper.
Out-of-range: subscribe to pool Swap events, compare currentTick with position tickLower/tickUpper.
System Architecture
Components
Indexer — service reading on-chain data and saving to database. Two modes: polling (every N seconds via multicall) and event-driven (WebSocket subscriptions). For HF monitoring — polling. For LP out-of-range events — event-driven.
Alert engine — applies rules to current data. Supports custom thresholds: each user configures own HF thresholds, minimum fees for notification, preferred notification channels.
Notification dispatcher — sends notifications. Channels: Telegram Bot (most popular in DeFi), Email via SendGrid/Resend, Webhook for custom integrations, Push notifications via Web Push API.
API/Frontend — dashboard for users: current positions, HF change history, liquidation history, APY for period.
Data Storage
PostgreSQL for time-series position data. Schema:
CREATE TABLE position_snapshots (
id BIGSERIAL PRIMARY KEY,
address VARCHAR(42) NOT NULL,
protocol VARCHAR(20) NOT NULL, -- 'aave_v3', 'compound_v3', 'uniswap_v3'
chain_id INTEGER NOT NULL,
health_factor NUMERIC,
collateral_usd NUMERIC,
debt_usd NUMERIC,
snapshot_at TIMESTAMPTZ NOT NULL
);
CREATE INDEX ON position_snapshots (address, protocol, snapshot_at DESC);
For high-frequency time series — TimescaleDB extension for PostgreSQL with automatic partitioning by time.
The Graph subgraph for Uniswap v3 positions reduces RPC load: instead of calling positions() for each tokenId — one GraphQL query by owner address. But The Graph has ~1-5 minute lag, so for real-time out-of-range monitoring — WebSocket subscriptions.
Multi-Chain and Gas Alerts
Modern DeFi users work on Ethereum, Arbitrum, Optimism, Polygon simultaneously. System should aggregate positions from all chains in single dashboard. For each chain — separate WebSocket connection to node.
Additionally useful: monitor gas price (Ethereum base fee via eth_gasPrice) + notify "it's cheap now, good time for rebalancing". Ethereum base fee below 10 gwei during off-hours — rare event, but users appreciate such notifications.
Development Stack
Backend: Node.js (TypeScript) + viem for on-chain interactions. Redis for alert queue and deduplication. PostgreSQL + TimescaleDB for storage. Bull.js for job scheduling (polling jobs).
Frontend: React + wagmi for wallet connection (user logs in via wallet, not email/password). TanStack Query for data fetching. Recharts or TradingView Lightweight Charts for HF timeline.
Work Process
Design (2-3 days). List of protocols, chains, metrics. Database schema.
Indexer + alert engine development (1 week). Multicall polling, WebSocket subscriptions, alert rules.
Notification dispatcher (2-3 days). Telegram bot, email, webhook.
Frontend dashboard (3-5 days). Positions overview, HF timeline, alert configuration.
Deployment. VPS + managed PostgreSQL. Monitor the service itself via Uptime Robot.
Timeline Guidelines
Basic system (Aave + Compound, Telegram alerts): 1 week. Multi-chain system with LP monitoring, dashboard, and custom rules: 2-3 weeks.







