feat: create v2-prep branch with comprehensive planning
Restructured project for V2 refactor: **Structure Changes:** - Moved all V1 code to orig/ folder (preserved with git mv) - Created docs/planning/ directory - Added orig/README_V1.md explaining V1 preservation **Planning Documents:** - 00_V2_MASTER_PLAN.md: Complete architecture overview - Executive summary of critical V1 issues - High-level component architecture diagrams - 5-phase implementation roadmap - Success metrics and risk mitigation - 07_TASK_BREAKDOWN.md: Atomic task breakdown - 99+ hours of detailed tasks - Every task < 2 hours (atomic) - Clear dependencies and success criteria - Organized by implementation phase **V2 Key Improvements:** - Per-exchange parsers (factory pattern) - Multi-layer strict validation - Multi-index pool cache - Background validation pipeline - Comprehensive observability **Critical Issues Addressed:** - Zero address tokens (strict validation + cache enrichment) - Parsing accuracy (protocol-specific parsers) - No audit trail (background validation channel) - Inefficient lookups (multi-index cache) - Stats disconnection (event-driven metrics) Next Steps: 1. Review planning documents 2. Begin Phase 1: Foundation (P1-001 through P1-010) 3. Implement parsers in Phase 2 4. Build cache system in Phase 3 5. Add validation pipeline in Phase 4 6. Migrate and test in Phase 5 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
291
orig/pkg/execution/alerts.go
Normal file
291
orig/pkg/execution/alerts.go
Normal file
@@ -0,0 +1,291 @@
|
||||
package execution
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/fraktal/mev-beta/internal/logger"
|
||||
"github.com/fraktal/mev-beta/pkg/types"
|
||||
)
|
||||
|
||||
// AlertLevel defines the severity of an alert
|
||||
type AlertLevel int
|
||||
|
||||
const (
|
||||
InfoLevel AlertLevel = iota
|
||||
WarningLevel
|
||||
CriticalLevel
|
||||
)
|
||||
|
||||
func (al AlertLevel) String() string {
|
||||
switch al {
|
||||
case InfoLevel:
|
||||
return "INFO"
|
||||
case WarningLevel:
|
||||
return "WARNING"
|
||||
case CriticalLevel:
|
||||
return "CRITICAL"
|
||||
default:
|
||||
return "UNKNOWN"
|
||||
}
|
||||
}
|
||||
|
||||
// Alert represents a system alert
|
||||
type Alert struct {
|
||||
Level AlertLevel
|
||||
Title string
|
||||
Message string
|
||||
Opportunity *types.ArbitrageOpportunity
|
||||
Timestamp time.Time
|
||||
}
|
||||
|
||||
// AlertConfig holds configuration for the alert system
|
||||
type AlertConfig struct {
|
||||
EnableConsoleAlerts bool
|
||||
EnableFileAlerts bool
|
||||
EnableWebhook bool
|
||||
WebhookURL string
|
||||
MinProfitForAlert *big.Int // Minimum profit to trigger alert (wei)
|
||||
MinROIForAlert float64 // Minimum ROI to trigger alert (0.05 = 5%)
|
||||
AlertCooldown time.Duration // Minimum time between alerts
|
||||
}
|
||||
|
||||
// AlertSystem handles opportunity alerts and notifications
|
||||
type AlertSystem struct {
|
||||
config *AlertConfig
|
||||
logger *logger.Logger
|
||||
lastAlertTime time.Time
|
||||
alertCount uint64
|
||||
}
|
||||
|
||||
// NewAlertSystem creates a new alert system
|
||||
func NewAlertSystem(config *AlertConfig, logger *logger.Logger) *AlertSystem {
|
||||
return &AlertSystem{
|
||||
config: config,
|
||||
logger: logger,
|
||||
lastAlertTime: time.Time{},
|
||||
alertCount: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// SendOpportunityAlert sends an alert for a profitable opportunity
|
||||
func (as *AlertSystem) SendOpportunityAlert(opp *types.ArbitrageOpportunity) {
|
||||
// Check cooldown
|
||||
if time.Since(as.lastAlertTime) < as.config.AlertCooldown {
|
||||
as.logger.Debug("Alert cooldown active, skipping alert")
|
||||
return
|
||||
}
|
||||
|
||||
// Check minimum thresholds
|
||||
if opp.NetProfit.Cmp(as.config.MinProfitForAlert) < 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if opp.ROI < as.config.MinROIForAlert {
|
||||
return
|
||||
}
|
||||
|
||||
// Determine alert level
|
||||
level := as.determineAlertLevel(opp)
|
||||
|
||||
// Create alert
|
||||
alert := &Alert{
|
||||
Level: level,
|
||||
Title: fmt.Sprintf("Profitable Arbitrage Opportunity Detected"),
|
||||
Message: as.formatOpportunityMessage(opp),
|
||||
Opportunity: opp,
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
|
||||
// Send alert via configured channels
|
||||
as.sendAlert(alert)
|
||||
|
||||
as.lastAlertTime = time.Now()
|
||||
as.alertCount++
|
||||
}
|
||||
|
||||
// SendExecutionAlert sends an alert for execution results
|
||||
func (as *AlertSystem) SendExecutionAlert(result *ExecutionResult) {
|
||||
var level AlertLevel
|
||||
var title string
|
||||
|
||||
if result.Success {
|
||||
level = InfoLevel
|
||||
title = "Arbitrage Executed Successfully"
|
||||
} else {
|
||||
level = WarningLevel
|
||||
title = "Arbitrage Execution Failed"
|
||||
}
|
||||
|
||||
alert := &Alert{
|
||||
Level: level,
|
||||
Title: title,
|
||||
Message: as.formatExecutionMessage(result),
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
|
||||
as.sendAlert(alert)
|
||||
}
|
||||
|
||||
// SendSystemAlert sends a system-level alert
|
||||
func (as *AlertSystem) SendSystemAlert(level AlertLevel, title, message string) {
|
||||
alert := &Alert{
|
||||
Level: level,
|
||||
Title: title,
|
||||
Message: message,
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
|
||||
as.sendAlert(alert)
|
||||
}
|
||||
|
||||
// determineAlertLevel determines the appropriate alert level
|
||||
func (as *AlertSystem) determineAlertLevel(opp *types.ArbitrageOpportunity) AlertLevel {
|
||||
// Critical if ROI > 10% or profit > 1 ETH
|
||||
oneETH := new(big.Int).Mul(big.NewInt(1), big.NewInt(1e18))
|
||||
if opp.ROI > 0.10 || opp.NetProfit.Cmp(oneETH) > 0 {
|
||||
return CriticalLevel
|
||||
}
|
||||
|
||||
// Warning if ROI > 5% or profit > 0.1 ETH
|
||||
pointOneETH := new(big.Int).Mul(big.NewInt(1), big.NewInt(1e17))
|
||||
if opp.ROI > 0.05 || opp.NetProfit.Cmp(pointOneETH) > 0 {
|
||||
return WarningLevel
|
||||
}
|
||||
|
||||
return InfoLevel
|
||||
}
|
||||
|
||||
// sendAlert sends an alert via all configured channels
|
||||
func (as *AlertSystem) sendAlert(alert *Alert) {
|
||||
// Console alert
|
||||
if as.config.EnableConsoleAlerts {
|
||||
as.sendConsoleAlert(alert)
|
||||
}
|
||||
|
||||
// File alert
|
||||
if as.config.EnableFileAlerts {
|
||||
as.sendFileAlert(alert)
|
||||
}
|
||||
|
||||
// Webhook alert
|
||||
if as.config.EnableWebhook && as.config.WebhookURL != "" {
|
||||
as.sendWebhookAlert(alert)
|
||||
}
|
||||
}
|
||||
|
||||
// sendConsoleAlert prints alert to console
|
||||
func (as *AlertSystem) sendConsoleAlert(alert *Alert) {
|
||||
emoji := "ℹ️"
|
||||
switch alert.Level {
|
||||
case WarningLevel:
|
||||
emoji = "⚠️"
|
||||
case CriticalLevel:
|
||||
emoji = "🚨"
|
||||
}
|
||||
|
||||
as.logger.Info(fmt.Sprintf("%s [%s] %s", emoji, alert.Level, alert.Title))
|
||||
as.logger.Info(alert.Message)
|
||||
}
|
||||
|
||||
// sendFileAlert writes alert to file
|
||||
func (as *AlertSystem) sendFileAlert(alert *Alert) {
|
||||
// TODO: Implement file-based alerts
|
||||
// Write to logs/alerts/alert_YYYYMMDD_HHMMSS.json
|
||||
}
|
||||
|
||||
// sendWebhookAlert sends alert to webhook (Slack, Discord, etc.)
|
||||
func (as *AlertSystem) sendWebhookAlert(alert *Alert) {
|
||||
// TODO: Implement webhook alerts
|
||||
// POST JSON to configured webhook URL
|
||||
as.logger.Debug(fmt.Sprintf("Would send webhook alert to: %s", as.config.WebhookURL))
|
||||
}
|
||||
|
||||
// formatOpportunityMessage formats an opportunity alert message
|
||||
func (as *AlertSystem) formatOpportunityMessage(opp *types.ArbitrageOpportunity) string {
|
||||
profitETH := new(big.Float).Quo(
|
||||
new(big.Float).SetInt(opp.NetProfit),
|
||||
big.NewFloat(1e18),
|
||||
)
|
||||
|
||||
gasEstimate := "N/A"
|
||||
if opp.GasEstimate != nil {
|
||||
gasEstimate = opp.GasEstimate.String()
|
||||
}
|
||||
|
||||
return fmt.Sprintf(`
|
||||
🎯 Arbitrage Opportunity Details:
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
• ID: %s
|
||||
• Path: %v
|
||||
• Protocol: %s
|
||||
• Amount In: %s wei
|
||||
• Estimated Profit: %.6f ETH
|
||||
• ROI: %.2f%%
|
||||
• Gas Estimate: %s wei
|
||||
• Confidence: %.1f%%
|
||||
• Price Impact: %.2f%%
|
||||
• Expires: %s
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
`,
|
||||
opp.ID,
|
||||
opp.Path,
|
||||
opp.Protocol,
|
||||
opp.AmountIn.String(),
|
||||
profitETH,
|
||||
opp.ROI*100,
|
||||
gasEstimate,
|
||||
opp.Confidence*100,
|
||||
opp.PriceImpact*100,
|
||||
opp.ExpiresAt.Format("15:04:05"),
|
||||
)
|
||||
}
|
||||
|
||||
// formatExecutionMessage formats an execution result message
|
||||
func (as *AlertSystem) formatExecutionMessage(result *ExecutionResult) string {
|
||||
status := "✅ SUCCESS"
|
||||
if !result.Success {
|
||||
status = "❌ FAILED"
|
||||
}
|
||||
|
||||
profitETH := "N/A"
|
||||
if result.ActualProfit != nil {
|
||||
p := new(big.Float).Quo(
|
||||
new(big.Float).SetInt(result.ActualProfit),
|
||||
big.NewFloat(1e18),
|
||||
)
|
||||
profitETH = fmt.Sprintf("%.6f ETH", p)
|
||||
}
|
||||
|
||||
errorMsg := ""
|
||||
if result.Error != nil {
|
||||
errorMsg = fmt.Sprintf("\n• Error: %v", result.Error)
|
||||
}
|
||||
|
||||
return fmt.Sprintf(`
|
||||
%s Arbitrage Execution
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
• Opportunity ID: %s
|
||||
• Tx Hash: %s
|
||||
• Actual Profit: %s
|
||||
• Gas Used: %d
|
||||
• Slippage: %.2f%%
|
||||
• Execution Time: %v%s
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
`,
|
||||
status,
|
||||
result.OpportunityID,
|
||||
result.TxHash.Hex(),
|
||||
profitETH,
|
||||
result.GasUsed,
|
||||
result.SlippagePercent*100,
|
||||
result.ExecutionTime,
|
||||
errorMsg,
|
||||
)
|
||||
}
|
||||
|
||||
// GetAlertCount returns the total number of alerts sent
|
||||
func (as *AlertSystem) GetAlertCount() uint64 {
|
||||
return as.alertCount
|
||||
}
|
||||
Reference in New Issue
Block a user