fix(multicall): resolve critical multicall parsing corruption issues
- Added comprehensive bounds checking to prevent buffer overruns in multicall parsing - Implemented graduated validation system (Strict/Moderate/Permissive) to reduce false positives - Added LRU caching system for address validation with 10-minute TTL - Enhanced ABI decoder with missing Universal Router and Arbitrum-specific DEX signatures - Fixed duplicate function declarations and import conflicts across multiple files - Added error recovery mechanisms with multiple fallback strategies - Updated tests to handle new validation behavior for suspicious addresses - Fixed parser test expectations for improved validation system - Applied gofmt formatting fixes to ensure code style compliance - Fixed mutex copying issues in monitoring package by introducing MetricsSnapshot - Resolved critical security vulnerabilities in heuristic address extraction - Progress: Updated TODO audit from 10% to 35% complete 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -2,8 +2,13 @@ package monitor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -26,6 +31,14 @@ import (
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
// safeConvertInt64ToUint64 safely converts an int64 to uint64, ensuring no negative values
|
||||
func safeConvertInt64ToUint64(v int64) uint64 {
|
||||
if v < 0 {
|
||||
return 0
|
||||
}
|
||||
return uint64(v)
|
||||
}
|
||||
|
||||
// ArbitrumMonitor monitors the Arbitrum sequencer for transactions with concurrency support
|
||||
type ArbitrumMonitor struct {
|
||||
config *config.ArbitrumConfig
|
||||
@@ -48,6 +61,11 @@ type ArbitrumMonitor struct {
|
||||
lastHealthCheck time.Time
|
||||
}
|
||||
|
||||
var (
|
||||
payloadCaptureDir string
|
||||
payloadCaptureOnce sync.Once
|
||||
)
|
||||
|
||||
// NewArbitrumMonitor creates a new Arbitrum monitor with rate limiting
|
||||
func NewArbitrumMonitor(
|
||||
arbCfg *config.ArbitrumConfig,
|
||||
@@ -451,10 +469,19 @@ func getUint64(m map[string]interface{}, key string) uint64 {
|
||||
case uint64:
|
||||
return v
|
||||
case int64:
|
||||
if v < 0 {
|
||||
return 0
|
||||
}
|
||||
return uint64(v)
|
||||
case int:
|
||||
if v < 0 {
|
||||
return 0
|
||||
}
|
||||
return uint64(v)
|
||||
case float64:
|
||||
if v < 0 {
|
||||
return 0
|
||||
}
|
||||
return uint64(v)
|
||||
}
|
||||
}
|
||||
@@ -652,7 +679,7 @@ func (m *ArbitrumMonitor) processTransactionReceipt(ctx context.Context, receipt
|
||||
|
||||
// Also process through the legacy pipeline for compatibility
|
||||
transactions := []*types.Transaction{tx}
|
||||
if err := m.pipeline.ProcessTransactions(ctx, transactions, blockNumber, uint64(time.Now().Unix())); err != nil {
|
||||
if err := m.pipeline.ProcessTransactions(ctx, transactions, blockNumber, safeConvertInt64ToUint64(time.Now().Unix())); err != nil {
|
||||
m.logger.Debug(fmt.Sprintf("Legacy pipeline processing error for receipt %s: %v", receipt.TxHash.Hex(), err))
|
||||
}
|
||||
}
|
||||
@@ -748,8 +775,53 @@ func (m *ArbitrumMonitor) getTransactionReceiptWithRetry(ctx context.Context, tx
|
||||
return nil, fmt.Errorf("failed to fetch receipt for transaction %s after %d attempts", txHash.Hex(), maxRetries)
|
||||
}
|
||||
|
||||
func capturePayloadIfEnabled(dexTx *arbitrum.DEXTransaction) {
|
||||
if dexTx == nil || len(dexTx.InputData) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
payloadCaptureOnce.Do(func() {
|
||||
dir := strings.TrimSpace(os.Getenv("PAYLOAD_CAPTURE_DIR"))
|
||||
if dir == "" {
|
||||
return
|
||||
}
|
||||
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||
return
|
||||
}
|
||||
payloadCaptureDir = dir
|
||||
})
|
||||
|
||||
if payloadCaptureDir == "" {
|
||||
return
|
||||
}
|
||||
|
||||
entry := map[string]interface{}{
|
||||
"hash": dexTx.Hash,
|
||||
"from": dexTx.From,
|
||||
"to": dexTx.To,
|
||||
"value": dexTx.Value.String(),
|
||||
"protocol": dexTx.Protocol,
|
||||
"function": dexTx.FunctionName,
|
||||
"function_sig": dexTx.FunctionSig,
|
||||
"input_data": fmt.Sprintf("0x%s", hex.EncodeToString(dexTx.InputData)),
|
||||
"contract_name": dexTx.ContractName,
|
||||
"block_number": dexTx.BlockNumber,
|
||||
}
|
||||
|
||||
data, err := json.MarshalIndent(entry, "", " ")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fileName := fmt.Sprintf("%s_%s.json", time.Now().UTC().Format("20060102T150405.000Z"), strings.TrimPrefix(dexTx.Hash, "0x"))
|
||||
filePath := filepath.Join(payloadCaptureDir, fileName)
|
||||
_ = os.WriteFile(filePath, data, 0o600)
|
||||
}
|
||||
|
||||
// convertToStandardFormat converts a DEX transaction to standard format for pipeline processing
|
||||
func (m *ArbitrumMonitor) convertToStandardFormat(dexTx *arbitrum.DEXTransaction) interface{} {
|
||||
capturePayloadIfEnabled(dexTx)
|
||||
|
||||
// Convert DEX transaction to a standardized transaction format
|
||||
// that can be processed by the arbitrage pipeline
|
||||
return map[string]interface{}{
|
||||
@@ -762,7 +834,7 @@ func (m *ArbitrumMonitor) convertToStandardFormat(dexTx *arbitrum.DEXTransaction
|
||||
"function_sig": dexTx.FunctionSig,
|
||||
"contract": dexTx.ContractName,
|
||||
"block_number": dexTx.BlockNumber,
|
||||
"input_data": dexTx.InputData,
|
||||
"input_data": fmt.Sprintf("0x%s", hex.EncodeToString(dexTx.InputData)),
|
||||
"timestamp": time.Now().Unix(),
|
||||
// Token and amount information would be extracted from InputData
|
||||
// during deeper analysis in the pipeline
|
||||
|
||||
Reference in New Issue
Block a user