Project on-chain metrics monitoring

We design and develop full-cycle blockchain solutions: from smart contract architecture to launching DeFi protocols, NFT marketplaces and crypto exchanges. Security audits, tokenomics, integration with existing infrastructure.
Showing 1 of 1 servicesAll 1306 services
Project on-chain metrics monitoring
Medium
from 1 business day to 3 business days
FAQ
Blockchain Development Services
Blockchain Development Stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1217
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    852
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1046
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    823

Monitoring On-Chain Metrics of Your Project

Your smart contract is deployed. Does the team know what's happening with it in real-time? How many unique users interacted in the last 24 hours? Is transaction volume growing abnormally right now? Are there patterns indicating exploitation attempts? On-chain monitoring answers these questions before problems appear, not after.

What to Monitor and Why

I divide metrics into three categories by purpose:

Operations — for DevOps and team:

  • Call frequency of key contract functions
  • Gas consumption by method (abnormal growth = expensive operations or attack)
  • Failed transaction count (growth in failed/total ratio = possible attack or bug)
  • TVL (Total Value Locked) if contract holds assets

Business — for product:

  • DAU/MAU on-chain (unique addresses per period)
  • Retention — addresses returning after N days
  • Volume in USD through specific functions
  • Top-N addresses by activity (whale monitoring)

Security — for Security team:

  • Large withdrawals (> threshold) in short period
  • Unusual call patterns (flash loan + contract in one block)
  • Changes in ownership/admin role
  • Calls from new addresses with large balance

Data Sources

Event logs — primary source. Well-designed contract emits event for each significant action. If missing — add them (if upgradeable) or use traces.

Trace calls — internal calls between contracts. Need debug_traceTransaction or trace_transaction. Used for tracking funds through multiple contracts in one transaction.

Storage slots — direct state reading. eth_getStorageAt(address, slot, blockNumber). Useful for metrics not emitted as events: current TVL, pool sizes.

Dune Analytics / Flipside — SQL queries to indexed blockchain data. Fast start for analytics, but vendor lock-in and delayed data (not real-time).

Monitoring Architecture

                  ┌─────────────────┐
                  │  Blockchain RPC  │
                  │  (Alchemy/own)   │
                  └────────┬────────┘
                           │ eth_getLogs / WebSocket
              ┌────────────▼──────────────┐
              │      Event Collector       │
              │  (contract subscription)   │
              └────────────┬──────────────┘
                           │
              ┌────────────▼──────────────┐
              │    Metrics Processor       │
              │  ABI decoding,             │
              │  aggregation, enrichment   │
              └──────┬────────────┬────────┘
                     │            │
        ┌────────────▼──┐  ┌──────▼──────────────┐
        │  TimescaleDB  │  │  Prometheus/VictoriaDB│
        │  (history)    │  │  (real-time metrics)  │
        └───────────────┘  └──────────────────────┘
                                     │
                           ┌─────────▼──────────┐
                           │   Grafana Dashboard │
                           │   + Alertmanager    │
                           └────────────────────┘

Event Collector Implementation

import { createPublicClient, webSocket, parseAbiItem, decodeEventLog } from 'viem';
import { mainnet } from 'viem/chains';

const client = createPublicClient({
  chain: mainnet,
  transport: webSocket('wss://eth-mainnet.g.alchemy.com/v2/YOUR_KEY'),
});

const CONTRACT_ABI = [
  parseAbiItem('event Deposit(address indexed user, uint256 amount)'),
  parseAbiItem('event Withdraw(address indexed user, uint256 amount)'),
  parseAbiItem('event Swap(address indexed user, address tokenIn, address tokenOut, uint256 amountIn, uint256 amountOut)'),
];

// WebSocket subscription to contract events
const unwatch = client.watchContractEvent({
  address: CONTRACT_ADDRESS,
  abi: CONTRACT_ABI,
  onLogs: async (logs) => {
    for (const log of logs) {
      await processEvent(log);
    }
  },
  onError: (error) => {
    console.error('WS error, reconnecting...', error);
    // viem auto-reconnects
  },
});

async function processEvent(log: any) {
  const decoded = decodeEventLog({ abi: CONTRACT_ABI, ...log });
  
  // Write to TimescaleDB
  await db.query(`
    INSERT INTO contract_events 
      (time, block_number, tx_hash, event_name, user_address, amount_usd)
    VALUES (NOW(), $1, $2, $3, $4, $5)
  `, [log.blockNumber, log.transactionHash, decoded.eventName, 
      decoded.args.user, await convertToUSD(decoded.args.amount)]);
  
  // Increment Prometheus counter
  eventCounter.labels(decoded.eventName).inc();
}

Prometheus Metrics and Alerts

import { Counter, Gauge, Histogram, Registry } from 'prom-client';

const registry = new Registry();

const eventCounter = new Counter({
  name: 'contract_events_total',
  help: 'Total contract events by type',
  labelNames: ['event_name'],
  registers: [registry],
});

const tvlGauge = new Gauge({
  name: 'contract_tvl_usd',
  help: 'Total Value Locked in USD',
  registers: [registry],
});

const largeWithdrawalCounter = new Counter({
  name: 'contract_large_withdrawals_total',
  help: 'Withdrawals above threshold',
  labelNames: ['threshold_category'],
  registers: [registry],
});

// HTTP endpoint for Prometheus scraping
app.get('/metrics', async (req, res) => {
  res.set('Content-Type', registry.contentType);
  res.send(await registry.metrics());
});

Alertmanager rules:

groups:
  - name: contract_security
    rules:
      # Large withdrawal — immediate alert
      - alert: LargeWithdrawal
        expr: rate(contract_large_withdrawals_total[5m]) > 0
        for: 0m
        labels:
          severity: critical
        annotations:
          summary: "Large withdrawal detected from contract"

      # Abnormal failed transaction growth
      - alert: HighFailureRate
        expr: |
          rate(contract_failed_txns_total[10m]) / 
          rate(contract_total_txns_total[10m]) > 0.1
        for: 5m
        annotations:
          summary: "More than 10% of transactions failing"

      # TVL drops fast
      - alert: TVLDrop
        expr: |
          (contract_tvl_usd - contract_tvl_usd offset 1h) / 
          contract_tvl_usd offset 1h < -0.2
        for: 2m
        annotations:
          summary: "TVL dropped by more than 20% in 1 hour"

Whale and Anomaly Detector

import asyncio
from web3 import AsyncWeb3

WHALE_THRESHOLD_USD = 100_000

async def monitor_large_transactions(contract, event_name: str):
    async for event in contract.events[event_name].get_logs(fromBlock='latest'):
        amount_usd = await get_usd_value(event.args.amount, event.args.token)
        
        if amount_usd > WHALE_THRESHOLD_USD:
            await send_telegram_alert(
                f"🐋 Whale {event_name}: ${amount_usd:,.0f}\n"
                f"Address: {event.args.user}\n"
                f"Tx: https://etherscan.io/tx/{event.transactionHash.hex()}"
            )
            
        # Check flash loan pattern: deposit + withdraw in one block
        if await is_flash_loan_pattern(event):
            await send_telegram_alert(
                f"⚠️ Flash loan pattern detected in block {event.blockNumber}",
                level='WARNING'
            )

Grafana Dashboard

Key panels for DeFi protocol:

Overview: TVL (gauge + time series), 24h Volume, DAU, Total Users (cumulative)

Activity: Events per minute (time series, by type), Gas used per block, Failed tx ratio

Security: Large transactions (table with recent whale txns), New whale addresses (first txn in contract + large amount), Flash loan detection events

Economics: Fee revenue over time, Token price correlation with activity

Dashboard as JSON — versioned in git with contract code.

Historical Data and Retrospective Analysis

Real-time monitoring catches anomalies now. For retrospective analysis need history:

-- Daily active users
SELECT 
    date_trunc('day', time) AS day,
    COUNT(DISTINCT user_address) AS dau,
    SUM(amount_usd) AS volume_usd
FROM contract_events
WHERE event_name IN ('Deposit', 'Swap')
GROUP BY 1
ORDER BY 1 DESC;

-- Retention: users returning after 7 days
WITH first_use AS (
    SELECT user_address, MIN(time) AS first_time
    FROM contract_events GROUP BY 1
),
return_use AS (
    SELECT DISTINCT e.user_address
    FROM contract_events e
    JOIN first_use f ON e.user_address = f.user_address
    WHERE e.time > f.first_time + INTERVAL '7 days'
      AND e.time < f.first_time + INTERVAL '14 days'
)
SELECT 
    COUNT(r.user_address)::float / COUNT(f.user_address) AS week1_retention
FROM first_use f
LEFT JOIN return_use r ON f.user_address = r.user_address;

Deployment Process

Day 1: event collector setup, RPC connection, raw event writing to TimescaleDB. Test on several blocks with real transactions.

Day 2: Prometheus metrics, first Grafana dashboard, setup basic alerts (TVL drop, high failure rate).

Day 3: whale detector, security alerts, test alerts via transaction simulation, team runbook.

Total 1-3 days depending on contract complexity and number of metrics monitored.