Monitoring Setup

A comprehensive guide to setting up monitoring and alerting for your liquid staking protocol on Solana. Learn about metrics collection, alerting systems, performance tracking, and incident response.

Last updated: 2024-03-21
Edit on GitHub

Monitoring Overview

Comprehensive monitoring is crucial for maintaining a secure and efficient liquid staking protocol. This guide covers monitoring setup, metrics collection, alerting systems, and incident response procedures.

Key Metrics

  • Validator performance
  • Stake distribution
  • Protocol health
  • User operations

Alert Types

  • Security incidents
  • Performance issues
  • System anomalies
  • Threshold breaches

Implementation Guide

Learn how to implement comprehensive monitoring systems.

Monitoring Service

1import { Connection, PublicKey } from '@solana/web3.js'
2import { MetricsClient } from './metrics'
3import { AlertSystem } from './alerts'
4
5export class MonitoringService {
6    private connection: Connection
7    private metricsClient: MetricsClient
8    private alertSystem: AlertSystem
9    private stakePool: PublicKey
10    private config: MonitoringConfig
11
12    constructor(config: MonitoringConfig) {
13        this.connection = new Connection(config.rpcEndpoint)
14        this.metricsClient = new MetricsClient(config.metricsEndpoint)
15        this.alertSystem = new AlertSystem(config.alertEndpoint)
16        this.stakePool = config.stakePool
17        this.config = config
18    }
19
20    async monitorValidatorPerformance() {
21        const validators = await this.getValidators()
22        
23        for (const validator of validators) {
24            // Check uptime
25            const uptime = await this.checkValidatorUptime(validator)
26            if (uptime < this.config.minUptime) {
27                await this.alertSystem.sendAlert({
28                    type: 'ValidatorUptime',
29                    severity: 'high',
30                    validator: validator.toString(),
31                    metric: uptime,
32                    threshold: this.config.minUptime,
33                })
34            }
35
36            // Check skip rate
37            const skipRate = await this.checkValidatorSkipRate(validator)
38            if (skipRate > this.config.maxSkipRate) {
39                await this.alertSystem.sendAlert({
40                    type: 'ValidatorSkipRate',
41                    severity: 'high',
42                    validator: validator.toString(),
43                    metric: skipRate,
44                    threshold: this.config.maxSkipRate,
45                })
46            }
47
48            // Record metrics
49            await this.metricsClient.recordValidatorMetrics({
50                validator: validator.toString(),
51                uptime,
52                skipRate,
53                commission: await this.getValidatorCommission(validator),
54                stake: await this.getValidatorStake(validator),
55            })
56        }
57    }
58
59    async monitorProtocolHealth() {
60        // Check total stake
61        const totalStake = await this.getTotalStake()
62        await this.metricsClient.recordMetric('total_stake', totalStake)
63
64        if (totalStake < this.config.minTotalStake) {
65            await this.alertSystem.sendAlert({
66                type: 'LowTotalStake',
67                severity: 'medium',
68                metric: totalStake,
69                threshold: this.config.minTotalStake,
70            })
71        }
72
73        // Check stake concentration
74        const concentration = await this.checkStakeConcentration()
75        if (concentration > this.config.maxConcentration) {
76            await this.alertSystem.sendAlert({
77                type: 'HighStakeConcentration',
78                severity: 'high',
79                metric: concentration,
80                threshold: this.config.maxConcentration,
81            })
82        }
83
84        // Monitor exchange rate
85        const exchangeRate = await this.getExchangeRate()
86        await this.metricsClient.recordMetric('exchange_rate', exchangeRate)
87
88        // Check reward rate
89        const rewardRate = await this.getRewardRate()
90        if (rewardRate < this.config.minRewardRate) {
91            await this.alertSystem.sendAlert({
92                type: 'LowRewardRate',
93                severity: 'medium',
94                metric: rewardRate,
95                threshold: this.config.minRewardRate,
96            })
97        }
98    }
99
100    async monitorUserOperations() {
101        // Monitor deposits
102        const deposits = await this.getRecentDeposits()
103        await this.metricsClient.recordMetric('deposits_count', deposits.length)
104        await this.metricsClient.recordMetric(
105            'deposits_volume',
106            deposits.reduce((sum, d) => sum + d.amount, 0)
107        )
108
109        // Monitor withdrawals
110        const withdrawals = await this.getRecentWithdrawals()
111        await this.metricsClient.recordMetric('withdrawals_count', withdrawals.length)
112        await this.metricsClient.recordMetric(
113            'withdrawals_volume',
114            withdrawals.reduce((sum, w) => sum + w.amount, 0)
115        )
116
117        // Check for unusual patterns
118        if (this.detectAnomalies(deposits, withdrawals)) {
119            await this.alertSystem.sendAlert({
120                type: 'UnusualActivityPattern',
121                severity: 'high',
122                details: 'Unusual deposit/withdrawal pattern detected',
123            })
124        }
125    }
126
127    async start() {
128        // Start monitoring loops
129        setInterval(
130            () => this.monitorValidatorPerformance(),
131            this.config.validatorCheckInterval
132        )
133        setInterval(
134            () => this.monitorProtocolHealth(),
135            this.config.protocolCheckInterval
136        )
137        setInterval(
138            () => this.monitorUserOperations(),
139            this.config.operationsCheckInterval
140        )
141
142        // Start metrics collection
143        await this.metricsClient.startCollection()
144
145        // Initialize alert handlers
146        await this.alertSystem.initialize()
147    }
148}

Metrics Collection

Implement comprehensive metrics collection and storage.

Metrics Client

1export class MetricsClient {
2    private endpoint: string
3    private buffer: MetricPoint[] = []
4    private flushInterval: number = 60000 // 1 minute
5
6    constructor(endpoint: string) {
7        this.endpoint = endpoint
8    }
9
10    async recordMetric(name: string, value: number) {
11        const point = {
12            name,
13            value,
14            timestamp: Date.now(),
15        }
16        
17        this.buffer.push(point)
18        
19        if (this.buffer.length >= 100) {
20            await this.flush()
21        }
22    }
23
24    async recordValidatorMetrics(metrics: ValidatorMetrics) {
25        await this.recordMetric(
26            `validator.${metrics.validator}.uptime`,
27            metrics.uptime
28        )
29        await this.recordMetric(
30            `validator.${metrics.validator}.skip_rate`,
31            metrics.skipRate
32        )
33        await this.recordMetric(
34            `validator.${metrics.validator}.commission`,
35            metrics.commission
36        )
37        await this.recordMetric(
38            `validator.${metrics.validator}.stake`,
39            metrics.stake
40        )
41    }
42
43    private async flush() {
44        if (this.buffer.length === 0) return
45
46        try {
47            await fetch(this.endpoint, {
48                method: 'POST',
49                headers: {
50                    'Content-Type': 'application/json',
51                },
52                body: JSON.stringify({
53                    metrics: this.buffer,
54                }),
55            })
56            
57            this.buffer = []
58        } catch (error) {
59            console.error('Failed to flush metrics:', error)
60        }
61    }
62
63    async startCollection() {
64        setInterval(() => this.flush(), this.flushInterval)
65    }
66}

Alert System

Implement an alert system for incident detection and response.

Alert System

1export class AlertSystem {
2    private endpoint: string
3    private handlers: Map<AlertType, AlertHandler> = new Map()
4    private throttle: Map<string, number> = new Map()
5
6    constructor(endpoint: string) {
7        this.endpoint = endpoint
8    }
9
10    async initialize() {
11        // Register alert handlers
12        this.handlers.set('ValidatorUptime', this.handleUptimeAlert)
13        this.handlers.set('ValidatorSkipRate', this.handleSkipRateAlert)
14        this.handlers.set('LowTotalStake', this.handleStakeAlert)
15        this.handlers.set('HighStakeConcentration', this.handleConcentrationAlert)
16        this.handlers.set('UnusualActivityPattern', this.handleActivityAlert)
17    }
18
19    async sendAlert(alert: Alert) {
20        // Check throttle
21        const key = `${alert.type}:${alert.validator || 'protocol'}`
22        const lastAlert = this.throttle.get(key) || 0
23        
24        if (Date.now() - lastAlert < 3600000) { // 1 hour
25            return // Skip alert
26        }
27        
28        // Update throttle
29        this.throttle.set(key, Date.now())
30
31        // Handle alert
32        const handler = this.handlers.get(alert.type)
33        if (handler) {
34            await handler(alert)
35        }
36
37        // Send to alert endpoint
38        try {
39            await fetch(this.endpoint, {
40                method: 'POST',
41                headers: {
42                    'Content-Type': 'application/json',
43                },
44                body: JSON.stringify(alert),
45            })
46        } catch (error) {
47            console.error('Failed to send alert:', error)
48        }
49    }
50
51    private async handleUptimeAlert(alert: Alert) {
52        if (alert.severity === 'high') {
53            // Consider emergency unstake
54            await this.considerEmergencyAction(alert)
55        }
56    }
57
58    private async handleSkipRateAlert(alert: Alert) {
59        if (alert.severity === 'high') {
60            // Consider reducing stake
61            await this.considerStakeReduction(alert)
62        }
63    }
64
65    private async handleStakeAlert(alert: Alert) {
66        // Notify stakeholders
67        await this.notifyStakeholders(alert)
68    }
69
70    private async handleConcentrationAlert(alert: Alert) {
71        // Trigger rebalancing
72        await this.triggerRebalancing(alert)
73    }
74
75    private async handleActivityAlert(alert: Alert) {
76        // Investigate unusual activity
77        await this.investigateActivity(alert)
78    }
79}
Monitoring Best Practices
  • Implement comprehensive logging for all operations
  • Set up proper alert thresholds and escalation procedures
  • Monitor system resources and performance metrics
  • Maintain historical data for trend analysis
  • Have clear incident response procedures
  • Regularly review and update monitoring parameters