Files
mev-beta/internal/recovery/fallback_provider.go
Krypto Kajun 850223a953 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>
2025-10-17 00:12:55 -05:00

385 lines
12 KiB
Go

package recovery
import (
"context"
"fmt"
"sync"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/fraktal/mev-beta/internal/logger"
"github.com/fraktal/mev-beta/internal/registry"
)
// DefaultFallbackProvider implements FallbackDataProvider with multiple data sources
type DefaultFallbackProvider struct {
mu sync.RWMutex
logger *logger.Logger
contractRegistry *registry.ContractRegistry
staticTokenData map[common.Address]*FallbackTokenInfo
staticPoolData map[common.Address]*FallbackPoolInfo
cacheTimeout time.Duration
enabled bool
}
// NewDefaultFallbackProvider creates a new fallback data provider
func NewDefaultFallbackProvider(logger *logger.Logger, contractRegistry *registry.ContractRegistry) *DefaultFallbackProvider {
provider := &DefaultFallbackProvider{
logger: logger,
contractRegistry: contractRegistry,
staticTokenData: make(map[common.Address]*FallbackTokenInfo),
staticPoolData: make(map[common.Address]*FallbackPoolInfo),
cacheTimeout: 5 * time.Minute,
enabled: true,
}
// Initialize with known safe data
provider.initializeStaticData()
return provider
}
// initializeStaticData populates the provider with known good data for critical Arbitrum contracts
func (fp *DefaultFallbackProvider) initializeStaticData() {
fp.mu.Lock()
defer fp.mu.Unlock()
// Major Arbitrum tokens with verified addresses
fp.staticTokenData[common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1")] = &FallbackTokenInfo{
Address: common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"),
Symbol: "WETH",
Name: "Wrapped Ether",
Decimals: 18,
IsVerified: true,
Source: "static_fallback",
Confidence: 1.0,
}
fp.staticTokenData[common.HexToAddress("0xaf88d065e77c8cC2239327C5EDb3A432268e5831")] = &FallbackTokenInfo{
Address: common.HexToAddress("0xaf88d065e77c8cC2239327C5EDb3A432268e5831"),
Symbol: "USDC",
Name: "USD Coin",
Decimals: 6,
IsVerified: true,
Source: "static_fallback",
Confidence: 1.0,
}
fp.staticTokenData[common.HexToAddress("0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9")] = &FallbackTokenInfo{
Address: common.HexToAddress("0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9"),
Symbol: "USDT",
Name: "Tether USD",
Decimals: 6,
IsVerified: true,
Source: "static_fallback",
Confidence: 1.0,
}
fp.staticTokenData[common.HexToAddress("0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f")] = &FallbackTokenInfo{
Address: common.HexToAddress("0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f"),
Symbol: "WBTC",
Name: "Wrapped BTC",
Decimals: 8,
IsVerified: true,
Source: "static_fallback",
Confidence: 1.0,
}
fp.staticTokenData[common.HexToAddress("0x912CE59144191C1204E64559FE8253a0e49E6548")] = &FallbackTokenInfo{
Address: common.HexToAddress("0x912CE59144191C1204E64559FE8253a0e49E6548"),
Symbol: "ARB",
Name: "Arbitrum",
Decimals: 18,
IsVerified: true,
Source: "static_fallback",
Confidence: 1.0,
}
// High-volume Uniswap V3 pools with verified addresses and token pairs
fp.staticPoolData[common.HexToAddress("0xC6962004f452bE9203591991D15f6b388e09E8D0")] = &FallbackPoolInfo{
Address: common.HexToAddress("0xC6962004f452bE9203591991D15f6b388e09E8D0"),
Token0: common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"), // WETH
Token1: common.HexToAddress("0xaf88d065e77c8cC2239327C5EDb3A432268e5831"), // USDC
Protocol: "UniswapV3",
Fee: 500, // 0.05%
IsVerified: true,
Source: "static_fallback",
Confidence: 1.0,
}
fp.staticPoolData[common.HexToAddress("0x641C00A822e8b671738d32a431a4Fb6074E5c79d")] = &FallbackPoolInfo{
Address: common.HexToAddress("0x641C00A822e8b671738d32a431a4Fb6074E5c79d"),
Token0: common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"), // WETH
Token1: common.HexToAddress("0xaf88d065e77c8cC2239327C5EDb3A432268e5831"), // USDC
Protocol: "UniswapV3",
Fee: 3000, // 0.3%
IsVerified: true,
Source: "static_fallback",
Confidence: 1.0,
}
fp.staticPoolData[common.HexToAddress("0x17c14D2c404D167802b16C450d3c99F88F2c4F4d")] = &FallbackPoolInfo{
Address: common.HexToAddress("0x17c14D2c404D167802b16C450d3c99F88F2c4F4d"),
Token0: common.HexToAddress("0xaf88d065e77c8cC2239327C5EDb3A432268e5831"), // USDC
Token1: common.HexToAddress("0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9"), // USDT
Protocol: "UniswapV3",
Fee: 100, // 0.01%
IsVerified: true,
Source: "static_fallback",
Confidence: 1.0,
}
fp.staticPoolData[common.HexToAddress("0x2f5e87C032bc4F8526F320c012A4e678F1fa6cAB")] = &FallbackPoolInfo{
Address: common.HexToAddress("0x2f5e87C032bc4F8526F320c012A4e678F1fa6cAB"),
Token0: common.HexToAddress("0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f"), // WBTC
Token1: common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"), // WETH
Protocol: "UniswapV3",
Fee: 500, // 0.05%
IsVerified: true,
Source: "static_fallback",
Confidence: 1.0,
}
fp.logger.Info("Initialized fallback provider with static data",
"tokens", len(fp.staticTokenData),
"pools", len(fp.staticPoolData))
}
// GetFallbackTokenInfo provides fallback token information
func (fp *DefaultFallbackProvider) GetFallbackTokenInfo(ctx context.Context, address common.Address) (*FallbackTokenInfo, error) {
if !fp.enabled {
return nil, fmt.Errorf("fallback provider disabled")
}
fp.mu.RLock()
defer fp.mu.RUnlock()
// First, try static data
if tokenInfo, exists := fp.staticTokenData[address]; exists {
fp.logger.Debug("Fallback token info from static data",
"address", address.Hex(),
"symbol", tokenInfo.Symbol,
"source", tokenInfo.Source)
return tokenInfo, nil
}
// Second, try contract registry if available
if fp.contractRegistry != nil {
if contractInfo, err := fp.contractRegistry.GetContractInfo(ctx, address); err == nil && contractInfo != nil {
tokenInfo := &FallbackTokenInfo{
Address: address,
Symbol: contractInfo.Symbol,
Name: contractInfo.Name,
Decimals: contractInfo.Decimals,
IsVerified: contractInfo.IsVerified,
Source: "contract_registry",
Confidence: contractInfo.Confidence,
}
fp.logger.Debug("Fallback token info from registry",
"address", address.Hex(),
"symbol", tokenInfo.Symbol,
"confidence", tokenInfo.Confidence)
return tokenInfo, nil
}
}
// Third, provide minimal safe fallback for unknown tokens
tokenInfo := &FallbackTokenInfo{
Address: address,
Symbol: fmt.Sprintf("UNK_%s", address.Hex()[:8]),
Name: "Unknown Token",
Decimals: 18, // Safe default
IsVerified: false,
Source: "generated_fallback",
Confidence: 0.1,
}
fp.logger.Warn("Using generated fallback token info",
"address", address.Hex(),
"symbol", tokenInfo.Symbol)
return tokenInfo, nil
}
// GetFallbackPoolInfo provides fallback pool information
func (fp *DefaultFallbackProvider) GetFallbackPoolInfo(ctx context.Context, address common.Address) (*FallbackPoolInfo, error) {
if !fp.enabled {
return nil, fmt.Errorf("fallback provider disabled")
}
fp.mu.RLock()
defer fp.mu.RUnlock()
// First, try static data
if poolInfo, exists := fp.staticPoolData[address]; exists {
fp.logger.Debug("Fallback pool info from static data",
"address", address.Hex(),
"protocol", poolInfo.Protocol,
"token0", poolInfo.Token0.Hex(),
"token1", poolInfo.Token1.Hex())
return poolInfo, nil
}
// Second, try contract registry if available
if fp.contractRegistry != nil {
if poolInfo := fp.contractRegistry.GetPoolInfo(address); poolInfo != nil {
fallbackInfo := &FallbackPoolInfo{
Address: address,
Token0: poolInfo.Token0,
Token1: poolInfo.Token1,
Protocol: poolInfo.Protocol,
Fee: poolInfo.Fee,
IsVerified: poolInfo.IsVerified,
Source: "contract_registry",
Confidence: poolInfo.Confidence,
}
fp.logger.Debug("Fallback pool info from registry",
"address", address.Hex(),
"protocol", fallbackInfo.Protocol,
"confidence", fallbackInfo.Confidence)
return fallbackInfo, nil
}
}
// No fallback available for unknown pools - return error
return nil, fmt.Errorf("no fallback data available for pool %s", address.Hex())
}
// GetFallbackContractType provides fallback contract type information
func (fp *DefaultFallbackProvider) GetFallbackContractType(ctx context.Context, address common.Address) (string, error) {
if !fp.enabled {
return "", fmt.Errorf("fallback provider disabled")
}
fp.mu.RLock()
defer fp.mu.RUnlock()
// Check if it's a known token
if _, exists := fp.staticTokenData[address]; exists {
return "ERC20", nil
}
// Check if it's a known pool
if _, exists := fp.staticPoolData[address]; exists {
return "Pool", nil
}
// Try contract registry
if fp.contractRegistry != nil {
if contractInfo, err := fp.contractRegistry.GetContractInfo(ctx, address); err == nil && contractInfo != nil {
return contractInfo.Type.String(), nil
}
}
// Default to unknown
return "Unknown", nil
}
// AddStaticTokenData adds static token data for fallback use
func (fp *DefaultFallbackProvider) AddStaticTokenData(address common.Address, info *FallbackTokenInfo) {
fp.mu.Lock()
defer fp.mu.Unlock()
fp.staticTokenData[address] = info
fp.logger.Debug("Added static token data",
"address", address.Hex(),
"symbol", info.Symbol)
}
// AddStaticPoolData adds static pool data for fallback use
func (fp *DefaultFallbackProvider) AddStaticPoolData(address common.Address, info *FallbackPoolInfo) {
fp.mu.Lock()
defer fp.mu.Unlock()
fp.staticPoolData[address] = info
fp.logger.Debug("Added static pool data",
"address", address.Hex(),
"protocol", info.Protocol)
}
// IsAddressKnown checks if an address is in the static fallback data
func (fp *DefaultFallbackProvider) IsAddressKnown(address common.Address) bool {
fp.mu.RLock()
defer fp.mu.RUnlock()
_, isToken := fp.staticTokenData[address]
_, isPool := fp.staticPoolData[address]
return isToken || isPool
}
// GetKnownAddresses returns all known addresses in the fallback provider
func (fp *DefaultFallbackProvider) GetKnownAddresses() (tokens []common.Address, pools []common.Address) {
fp.mu.RLock()
defer fp.mu.RUnlock()
for addr := range fp.staticTokenData {
tokens = append(tokens, addr)
}
for addr := range fp.staticPoolData {
pools = append(pools, addr)
}
return tokens, pools
}
// ValidateAddressWithFallback performs validation using fallback data
func (fp *DefaultFallbackProvider) ValidateAddressWithFallback(ctx context.Context, address common.Address, expectedType string) (bool, float64, error) {
if !fp.enabled {
return false, 0.0, fmt.Errorf("fallback provider disabled")
}
// Check if address is known in our static data
if fp.IsAddressKnown(address) {
actualType, err := fp.GetFallbackContractType(ctx, address)
if err != nil {
return false, 0.0, err
}
if actualType == expectedType {
return true, 1.0, nil // High confidence for known addresses
}
return false, 0.0, fmt.Errorf("type mismatch: expected %s, got %s", expectedType, actualType)
}
// For unknown addresses, provide low confidence validation
return true, 0.3, nil // Allow with low confidence
}
// GetStats returns statistics about the fallback provider
func (fp *DefaultFallbackProvider) GetStats() map[string]interface{} {
fp.mu.RLock()
defer fp.mu.RUnlock()
return map[string]interface{}{
"enabled": fp.enabled,
"static_tokens_count": len(fp.staticTokenData),
"static_pools_count": len(fp.staticPoolData),
"cache_timeout": fp.cacheTimeout.String(),
"has_registry": fp.contractRegistry != nil,
}
}
// Enable enables the fallback provider
func (fp *DefaultFallbackProvider) Enable() {
fp.mu.Lock()
defer fp.mu.Unlock()
fp.enabled = true
fp.logger.Info("Fallback provider enabled")
}
// Disable disables the fallback provider
func (fp *DefaultFallbackProvider) Disable() {
fp.mu.Lock()
defer fp.mu.Unlock()
fp.enabled = false
fp.logger.Info("Fallback provider disabled")
}