多链 Gas 价格监控,历史趋势分析,最佳交易时机推荐
┌──────────────┐ ┌─────────────┐ ┌──────────────┐
│ Clients │────▶│ Echo API │────▶│ Blockchain │
│ (REST/HTTP) │ │ Service │ │ (go-ethereum)│
└──────────────┘ └─────────────┘ └──────────────┘
│
┌────────────┼────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ InfluxDB │ │ Grafana │ │ Alerts │
│(TimeSeries)│ │(Dashboard)│ │ (Notify) │
└──────────┘ └──────────┘ └──────────┘
1type GasPrice struct {2 Chain string `json:"chain"`3 Timestamp time.Time `json:"timestamp"`4 BaseFee *big.Int `json:"base_fee"`5 MaxPriority *big.Int `json:"max_priority"`6 GasPrice *big.Int `json:"gas_price"` // Legacy7 8 // 预估费用等级9 Slow *GasEstimate `json:"slow"`10 Standard *GasEstimate `json:"standard"`11 Fast *GasEstimate `json:"fast"`12 Instant *GasEstimate `json:"instant"`13}14 15func (s *GasService) GetGasPrice(ctx context.Context, chain string) (*GasPrice, error) {16 client := s.clients[chain]17 block, err := client.BlockByNumber(ctx, nil)18 if err != nil {19 return nil, err20 }21 22 baseFee := block.BaseFee()23 priorityFee, _ := client.SuggestGasTipCap(ctx)24 25 return &GasPrice{26 Chain: chain,27 Timestamp: time.Now(),28 BaseFee: baseFee,29 MaxPriority: priorityFee,30 Slow: s.estimateGas(baseFee, 0.8),31 Standard: s.estimateGas(baseFee, 1.0),32 Fast: s.estimateGas(baseFee, 1.25),33 Instant: s.estimateGas(baseFee, 1.5),34 }, nil35}1func (s *GasStorage) SaveGasPrice(ctx context.Context, gas *GasPrice) error {2 writeAPI := s.client.WriteAPIBlocking(s.org, s.bucket)3 4 p := influxdb2.NewPoint(5 "gas_price",6 map[string]string{"chain": gas.Chain},7 map[string]interface{}{8 "base_fee": gas.BaseFee.Int64(),9 "priority_fee": gas.MaxPriority.Int64(),10 "slow_gwei": weiToGwei(gas.Slow.MaxFeePerGas),11 "standard_gwei": weiToGwei(gas.Standard.MaxFeePerGas),12 "fast_gwei": weiToGwei(gas.Fast.MaxFeePerGas),13 },14 gas.Timestamp,15 )16 17 return writeAPI.WritePoint(ctx, p)18}1func (a *GasAnalyzer) GetBestTradingTimes(ctx context.Context, chain string) ([]*TimeRecommendation, error) {2 history, err := a.storage.GetHistory(ctx, chain, 7*24*time.Hour)3 if err != nil {4 return nil, err5 }6 7 // 按小时和星期几分组统计8 hourlyStats := make(map[int]map[int][]float64)9 10 for _, gas := range history {11 dow := int(gas.Timestamp.Weekday())12 hour := gas.Timestamp.Hour()13 14 if hourlyStats[dow] == nil {15 hourlyStats[dow] = make(map[int][]float64)16 }17 hourlyStats[dow][hour] = append(hourlyStats[dow][hour], weiToGwei(gas.Standard.MaxFeePerGas))18 }19 20 var recommendations []*TimeRecommendation21 overallAvg := a.calculateOverallAverage(history)22 23 for dow, hours := range hourlyStats {24 for hour, prices := range hours {25 avg := average(prices)26 savings := (overallAvg - avg) / overallAvg * 10027 recommendations = append(recommendations, &TimeRecommendation{28 Hour: hour, DayOfWeek: dow, AvgGas: avg, Savings: savings,29 })30 }31 }32 33 sort.Slice(recommendations, func(i, j int) bool {34 return recommendations[i].Savings > recommendations[j].Savings35 })36 return recommendations[:10], nil37}