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:
Krypto Kajun
2025-10-17 00:12:55 -05:00
parent f358f49aa9
commit 850223a953
8621 changed files with 79808 additions and 7340 deletions

View File

@@ -2,20 +2,23 @@ package swap
import (
"context"
"errors"
"fmt"
"math/big"
"strings"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/holiman/uint256"
"github.com/fraktal/mev-beta/internal/logger"
"github.com/fraktal/mev-beta/pkg/events"
"github.com/fraktal/mev-beta/pkg/marketdata"
"github.com/fraktal/mev-beta/pkg/profitcalc"
scannercommon "github.com/fraktal/mev-beta/pkg/scanner/common"
"github.com/fraktal/mev-beta/pkg/scanner/market"
stypes "github.com/fraktal/mev-beta/pkg/types"
"github.com/fraktal/mev-beta/pkg/uniswap"
"github.com/holiman/uint256"
)
// SwapAnalyzer handles analysis of swap events for price movement opportunities
@@ -26,6 +29,36 @@ type SwapAnalyzer struct {
opportunityRanker *profitcalc.OpportunityRanker
}
var (
factoryProtocolMap = map[common.Address]string{
common.HexToAddress("0x1F98431c8aD98523631AE4a59f267346ea31F984"): "UniswapV3",
common.HexToAddress("0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f"): "UniswapV2",
common.HexToAddress("0xf1D7CC64Fb4452F05c498126312eBE29f30Fbcf9"): "UniswapV2",
common.HexToAddress("0xc35DADB65012eC5796536bD9864eD8773aBc74C4"): "SushiSwap",
common.HexToAddress("0xBA12222222228d8Ba445958a75a0704d566BF2C8"): "Balancer",
common.HexToAddress("0xF18056Bbd320E96A48e3Fbf8bC061322531aac99"): "Curve",
common.HexToAddress("0x5ffe7FB82894076ECB99A30D6A32e969e6e35E98"): "Curve",
common.HexToAddress("0x5F1dddbf348aC2fbe22a163e30F99F9ECE3DD50a"): "KyberElastic",
common.HexToAddress("0x1a3c9B1d2F0529D97f2afC5136Cc23e58f1FD35B"): "Camelot",
}
protocolDefaultFactory = map[string]common.Address{
"UniswapV3": common.HexToAddress("0x1F98431c8aD98523631AE4a59f267346ea31F984"),
"UniswapV2": common.HexToAddress("0xf1D7CC64Fb4452F05c498126312eBE29f30Fbcf9"),
"SushiSwap": common.HexToAddress("0xc35DADB65012eC5796536bD9864eD8773aBc74C4"),
"Balancer": common.HexToAddress("0xBA12222222228d8Ba445958a75a0704d566BF2C8"),
"Curve": common.HexToAddress("0xF18056Bbd320E96A48e3Fbf8bC061322531aac99"),
"KyberElastic": common.HexToAddress("0x5F1dddbf348aC2fbe22a163e30F99F9ECE3DD50a"),
"Camelot": common.HexToAddress("0x1a3c9B1d2F0529D97f2afC5136Cc23e58f1FD35B"),
}
protocolSpecialByAddress = map[common.Address]string{
common.HexToAddress("0xBA12222222228d8Ba445958a75a0704d566BF2C8"): "Balancer",
common.HexToAddress("0xF18056Bbd320E96A48e3Fbf8bC061322531aac99"): "Curve",
common.HexToAddress("0x5F1dddbf348aC2fbe22a163e30F99F9ECE3DD50a"): "KyberElastic",
}
)
// NewSwapAnalyzer creates a new swap analyzer
func NewSwapAnalyzer(
logger *logger.Logger,
@@ -45,6 +78,22 @@ func NewSwapAnalyzer(
func (s *SwapAnalyzer) AnalyzeSwapEvent(event events.Event, marketScanner *market.MarketScanner) {
s.logger.Debug(fmt.Sprintf("Analyzing swap event in pool %s", event.PoolAddress))
// Validate pool address before attempting expensive lookups
if event.PoolAddress == (common.Address{}) {
s.logger.Warn(fmt.Sprintf("Skipping swap analysis: empty pool address (tx: %s)", event.TransactionHash.Hex()))
return
}
if event.PoolAddress == event.Token0 || event.PoolAddress == event.Token1 {
s.logger.Warn(fmt.Sprintf("Skipping swap analysis: pool address %s matches token address (tx: %s)", event.PoolAddress.Hex(), event.TransactionHash.Hex()))
return
}
if strings.HasPrefix(strings.ToLower(event.PoolAddress.Hex()), "0x000000") {
s.logger.Warn(fmt.Sprintf("Skipping swap analysis: suspicious pool address %s (tx: %s)", event.PoolAddress.Hex(), event.TransactionHash.Hex()))
return
}
// Get comprehensive pool data to determine factory and fee
poolInfo, poolExists := s.marketDataLogger.GetPoolInfo(event.PoolAddress)
factory := common.Address{}
@@ -108,7 +157,42 @@ func (s *SwapAnalyzer) AnalyzeSwapEvent(event events.Event, marketScanner *marke
// Calculate price impact based on pool liquidity and swap amounts
swapData.PriceImpact = s.calculateSwapPriceImpact(event, swapData)
// Log comprehensive swap event to market data logger
// Get pool data with caching
poolData, err := marketScanner.GetPoolData(event.PoolAddress.Hex())
if err != nil {
if errors.Is(err, market.ErrInvalidPoolCandidate) {
s.logger.Debug("Skipping pool data fetch for invalid candidate",
"pool", event.PoolAddress,
"tx", event.TransactionHash,
"error", err)
} else {
// Enhanced error logging with context - check if this is an ERC-20 token misclassified as a pool
errorMsg := fmt.Sprintf("Error getting pool data for %s: %v", event.PoolAddress, err)
contextMsg := fmt.Sprintf("event_type:%s protocol:%s block:%d tx:%s",
event.Type.String(), event.Protocol, event.BlockNumber, event.TransactionHash.Hex())
s.logger.Error(fmt.Sprintf("%s [context: %s]", errorMsg, contextMsg))
}
return
}
finalProtocol := s.detectSwapProtocol(event, poolInfo, poolData, factory)
if finalProtocol == "" || strings.EqualFold(finalProtocol, "unknown") {
if fallback := canonicalProtocolName(event.Protocol); fallback != "" {
finalProtocol = fallback
} else {
finalProtocol = "Unknown"
}
}
event.Protocol = finalProtocol
swapData.Protocol = finalProtocol
if poolData != nil {
poolData.Protocol = finalProtocol
}
factory = s.resolveFactory(factory, finalProtocol, marketScanner)
swapData.Factory = factory
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := s.marketDataLogger.LogSwapEvent(ctx, event, swapData); err != nil {
@@ -118,11 +202,12 @@ func (s *SwapAnalyzer) AnalyzeSwapEvent(event events.Event, marketScanner *marke
// Log the swap event to database (legacy)
marketScanner.LogSwapEvent(event)
// Get pool data with caching
poolData, err := marketScanner.GetPoolData(event.PoolAddress.Hex())
if err != nil {
s.logger.Error(fmt.Sprintf("Error getting pool data for %s: %v", event.PoolAddress, err))
return
s.marketDataLogger.UpdatePoolMetadata(event.PoolAddress, finalProtocol, factory)
// DEBUG: Log zero address events to trace their source
if event.PoolAddress == (common.Address{}) {
s.logger.Error(fmt.Sprintf("ZERO ADDRESS DEBUG [ANALYZER]: Received Event with zero PoolAddress - TxHash: %s, Protocol: %s, Token0: %s, Token1: %s, Type: %v",
event.TransactionHash.Hex(), event.Protocol, event.Token0.Hex(), event.Token1.Hex(), event.Type))
}
// Calculate price impact
@@ -373,7 +458,11 @@ func (s *SwapAnalyzer) findArbitrageOpportunities(event events.Event, movement *
// Get pool data
poolData, err := marketScanner.GetPoolData(pool.Address.Hex())
if err != nil {
s.logger.Error(fmt.Sprintf("Error getting pool data for related pool %s: %v", pool.Address.Hex(), err))
// Enhanced error logging with context for related pool analysis
errorMsg := fmt.Sprintf("Error getting pool data for related pool %s: %v", pool.Address.Hex(), err)
contextMsg := fmt.Sprintf("original_pool:%s related_pool:%s token_pair:%s-%s",
event.PoolAddress.Hex(), pool.Address.Hex(), pool.Token0.Hex(), pool.Token1.Hex())
s.logger.Error(fmt.Sprintf("%s [context: %s]", errorMsg, contextMsg))
continue
}
@@ -509,48 +598,18 @@ func (s *SwapAnalyzer) calculateSwapPriceImpact(event events.Event, swapData *ma
// getTokenPriceUSD gets the USD price of a token using various price sources
func (s *SwapAnalyzer) getTokenPriceUSD(tokenAddr common.Address) float64 {
// Known token prices (in a production system, this would query price oracles)
knownPrices := map[common.Address]float64{
common.HexToAddress("0x82af49447d8a07e3bd95bd0d56f35241523fbab1"): 2000.0, // WETH
common.HexToAddress("0xaf88d065e77c8cc2239327c5edb3a432268e5831"): 1.0, // USDC
common.HexToAddress("0xff970a61a04b1ca14834a43f5de4533ebddb5cc8"): 1.0, // USDC.e
common.HexToAddress("0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9"): 1.0, // USDT
common.HexToAddress("0x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f"): 43000.0, // WBTC
common.HexToAddress("0x912ce59144191c1204e64559fe8253a0e49e6548"): 0.75, // ARB
common.HexToAddress("0xfc5a1a6eb076a2c7ad06ed22c90d7e710e35ad0a"): 45.0, // GMX
common.HexToAddress("0xf97f4df75117a78c1a5a0dbb814af92458539fb4"): 12.0, // LINK
common.HexToAddress("0xfa7f8980b0f1e64a2062791cc3b0871572f1f7f0"): 8.0, // UNI
common.HexToAddress("0xba5ddd1f9d7f570dc94a51479a000e3bce967196"): 85.0, // AAVE
}
if price, exists := knownPrices[tokenAddr]; exists {
if price, exists := scannercommon.GetTokenPriceUSD(tokenAddr); exists {
return price
}
// For unknown tokens, return 0 (in production, would query price oracle or DEX)
return 0.0
}
// getTokenDecimals returns the decimal places for a token
func (s *SwapAnalyzer) getTokenDecimals(tokenAddr common.Address) uint8 {
// Known token decimals
knownDecimals := map[common.Address]uint8{
common.HexToAddress("0x82af49447d8a07e3bd95bd0d56f35241523fbab1"): 18, // WETH
common.HexToAddress("0xaf88d065e77c8cc2239327c5edb3a432268e5831"): 6, // USDC
common.HexToAddress("0xff970a61a04b1ca14834a43f5de4533ebddb5cc8"): 6, // USDC.e
common.HexToAddress("0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9"): 6, // USDT
common.HexToAddress("0x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f"): 8, // WBTC
common.HexToAddress("0x912ce59144191c1204e64559fe8253a0e49e6548"): 18, // ARB
common.HexToAddress("0xfc5a1a6eb076a2c7ad06ed22c90d7e710e35ad0a"): 18, // GMX
common.HexToAddress("0xf97f4df75117a78c1a5a0dbb814af92458539fb4"): 18, // LINK
common.HexToAddress("0xfa7f8980b0f1e64a2062791cc3b0871572f1f7f0"): 18, // UNI
common.HexToAddress("0xba5ddd1f9d7f570dc94a51479a000e3bce967196"): 18, // AAVE
}
if decimals, exists := knownDecimals[tokenAddr]; exists {
if decimals, exists := scannercommon.GetTokenDecimals(tokenAddr); exists {
return decimals
}
// Default to 18 for unknown tokens
return 18
}
@@ -583,3 +642,117 @@ func (s *SwapAnalyzer) sqrtPriceX96ToPrice(sqrtPriceX96 *uint256.Int) float64 {
priceFloat, _ := price.Float64()
return priceFloat
}
func canonicalProtocolName(raw string) string {
raw = strings.TrimSpace(raw)
if raw == "" {
return ""
}
lower := strings.ToLower(raw)
if idx := strings.Index(lower, "_fee"); idx != -1 {
lower = lower[:idx]
}
replacer := strings.NewReplacer(" ", "", "-", "", ".", "", ":", "")
lower = replacer.Replace(lower)
lower = strings.ReplaceAll(lower, "_", "")
switch {
case strings.Contains(lower, "kyberelastic"):
return "KyberElastic"
case strings.Contains(lower, "kyberclassic"):
return "KyberClassic"
case strings.Contains(lower, "kyberswap"):
return "Kyber"
case strings.Contains(lower, "kyber"):
return "Kyber"
case strings.Contains(lower, "balancer"):
return "Balancer"
case strings.Contains(lower, "curve"):
return "Curve"
case strings.Contains(lower, "camelot"):
return "Camelot"
case strings.Contains(lower, "traderjoe"):
return "TraderJoe"
case strings.Contains(lower, "sushiswap") || strings.Contains(lower, "sushi"):
return "SushiSwap"
case strings.Contains(lower, "pancake"):
return "PancakeSwap"
case strings.Contains(lower, "ramses"):
return "Ramses"
case strings.Contains(lower, "uniswap") && strings.Contains(lower, "v3"):
return "UniswapV3"
case strings.Contains(lower, "uniswap") && strings.Contains(lower, "v2"):
return "UniswapV2"
case strings.Contains(lower, "uniswap"):
return "Uniswap"
}
return strings.Title(strings.ReplaceAll(raw, "_", ""))
}
func protocolFromFactory(factory common.Address) string {
if factory == (common.Address{}) {
return ""
}
if protocol, ok := factoryProtocolMap[factory]; ok {
return protocol
}
return ""
}
func factoryForProtocol(protocol string) common.Address {
canonical := canonicalProtocolName(protocol)
if canonical == "" {
return common.Address{}
}
if addr, ok := protocolDefaultFactory[canonical]; ok {
return addr
}
return common.Address{}
}
func (s *SwapAnalyzer) resolveFactory(existing common.Address, protocol string, marketScanner *market.MarketScanner) common.Address {
if existing != (common.Address{}) {
return existing
}
if addr := factoryForProtocol(protocol); addr != (common.Address{}) {
return addr
}
if marketScanner != nil {
if addr := marketScanner.GetFactoryForProtocol(protocol); addr != (common.Address{}) {
return addr
}
}
return existing
}
func (s *SwapAnalyzer) detectSwapProtocol(event events.Event, poolInfo *marketdata.PoolInfo, poolData *market.CachedData, factory common.Address) string {
candidates := []string{event.Protocol}
if poolInfo != nil {
candidates = append(candidates, poolInfo.Protocol)
if factory == (common.Address{}) && poolInfo.Factory != (common.Address{}) {
factory = poolInfo.Factory
}
}
if poolData != nil {
candidates = append(candidates, poolData.Protocol)
}
if proto := protocolFromFactory(factory); proto != "" {
candidates = append(candidates, proto)
}
for _, candidate := range candidates {
if canonical := canonicalProtocolName(candidate); canonical != "" && !strings.EqualFold(canonical, "unknown") {
return canonical
}
}
if proto, ok := protocolSpecialByAddress[event.PoolAddress]; ok {
return proto
}
return "Unknown"
}