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") }