- 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>
494 lines
16 KiB
Go
494 lines
16 KiB
Go
package registry
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
|
|
"github.com/fraktal/mev-beta/internal/contracts"
|
|
"github.com/fraktal/mev-beta/internal/logger"
|
|
)
|
|
|
|
// ContractInfo contains comprehensive information about a contract
|
|
type ContractInfo struct {
|
|
Address common.Address
|
|
Type contracts.ContractType
|
|
Name string
|
|
Symbol string
|
|
Decimals uint8
|
|
Token0 common.Address // For pools
|
|
Token1 common.Address // For pools
|
|
Fee uint32 // For V3 pools (in basis points)
|
|
Factory common.Address
|
|
Protocol string
|
|
IsVerified bool
|
|
LastUpdated time.Time
|
|
Confidence float64
|
|
}
|
|
|
|
// ContractRegistry provides authoritative mapping of contract addresses to their types and metadata
|
|
type ContractRegistry struct {
|
|
mu sync.RWMutex
|
|
contracts map[common.Address]*ContractInfo
|
|
tokensBySymbol map[string]common.Address
|
|
poolsByTokenPair map[string][]common.Address // "token0:token1" -> pool addresses
|
|
detector *contracts.ContractDetector
|
|
logger *logger.Logger
|
|
updateInterval time.Duration
|
|
lastFullUpdate time.Time
|
|
}
|
|
|
|
// NewContractRegistry creates a new contract registry
|
|
func NewContractRegistry(detector *contracts.ContractDetector, logger *logger.Logger) *ContractRegistry {
|
|
registry := &ContractRegistry{
|
|
contracts: make(map[common.Address]*ContractInfo),
|
|
tokensBySymbol: make(map[string]common.Address),
|
|
poolsByTokenPair: make(map[string][]common.Address),
|
|
detector: detector,
|
|
logger: logger,
|
|
updateInterval: 24 * time.Hour, // Update every 24 hours
|
|
lastFullUpdate: time.Time{},
|
|
}
|
|
|
|
// Initialize with known Arbitrum contracts
|
|
registry.initializeKnownContracts()
|
|
return registry
|
|
}
|
|
|
|
// initializeKnownContracts populates the registry with well-known Arbitrum contracts
|
|
func (cr *ContractRegistry) initializeKnownContracts() {
|
|
cr.mu.Lock()
|
|
defer cr.mu.Unlock()
|
|
|
|
now := time.Now()
|
|
|
|
// Major ERC-20 tokens on Arbitrum
|
|
knownTokens := map[common.Address]*ContractInfo{
|
|
common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"): {
|
|
Address: common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"),
|
|
Type: contracts.ContractTypeERC20Token,
|
|
Name: "Wrapped Ether",
|
|
Symbol: "WETH",
|
|
Decimals: 18,
|
|
Protocol: "Arbitrum",
|
|
IsVerified: true,
|
|
LastUpdated: now,
|
|
Confidence: 1.0,
|
|
},
|
|
common.HexToAddress("0xA0b86a33E6D8E4BBa6Fd6bD5BA0e2FF8A1e8B8D4"): {
|
|
Address: common.HexToAddress("0xA0b86a33E6D8E4BBa6Fd6bD5BA0e2FF8A1e8B8D4"),
|
|
Type: contracts.ContractTypeERC20Token,
|
|
Name: "USD Coin",
|
|
Symbol: "USDC",
|
|
Decimals: 6,
|
|
Protocol: "Arbitrum",
|
|
IsVerified: true,
|
|
LastUpdated: now,
|
|
Confidence: 1.0,
|
|
},
|
|
common.HexToAddress("0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9"): {
|
|
Address: common.HexToAddress("0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9"),
|
|
Type: contracts.ContractTypeERC20Token,
|
|
Name: "Tether USD",
|
|
Symbol: "USDT",
|
|
Decimals: 6,
|
|
Protocol: "Arbitrum",
|
|
IsVerified: true,
|
|
LastUpdated: now,
|
|
Confidence: 1.0,
|
|
},
|
|
common.HexToAddress("0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f"): {
|
|
Address: common.HexToAddress("0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f"),
|
|
Type: contracts.ContractTypeERC20Token,
|
|
Name: "Wrapped BTC",
|
|
Symbol: "WBTC",
|
|
Decimals: 8,
|
|
Protocol: "Arbitrum",
|
|
IsVerified: true,
|
|
LastUpdated: now,
|
|
Confidence: 1.0,
|
|
},
|
|
common.HexToAddress("0x912CE59144191C1204E64559FE8253a0e49E6548"): {
|
|
Address: common.HexToAddress("0x912CE59144191C1204E64559FE8253a0e49E6548"),
|
|
Type: contracts.ContractTypeERC20Token,
|
|
Name: "Arbitrum",
|
|
Symbol: "ARB",
|
|
Decimals: 18,
|
|
Protocol: "Arbitrum",
|
|
IsVerified: true,
|
|
LastUpdated: now,
|
|
Confidence: 1.0,
|
|
},
|
|
}
|
|
|
|
// Major Uniswap V3 pools on Arbitrum
|
|
knownPools := map[common.Address]*ContractInfo{
|
|
common.HexToAddress("0xC6962004f452bE9203591991D15f6b388e09E8D0"): {
|
|
Address: common.HexToAddress("0xC6962004f452bE9203591991D15f6b388e09E8D0"),
|
|
Type: contracts.ContractTypeUniswapV3Pool,
|
|
Name: "USDC/WETH 0.05%",
|
|
Token0: common.HexToAddress("0xA0b86a33E6D8E4BBa6Fd6bD5BA0e2FF8A1e8B8D4"), // USDC
|
|
Token1: common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"), // WETH
|
|
Fee: 500, // 0.05%
|
|
Factory: common.HexToAddress("0x1F98431c8aD98523631AE4a59f267346ea31F984"),
|
|
Protocol: "UniswapV3",
|
|
IsVerified: true,
|
|
LastUpdated: now,
|
|
Confidence: 1.0,
|
|
},
|
|
common.HexToAddress("0x17c14D2c404D167802b16C450d3c99F88F2c4F4d"): {
|
|
Address: common.HexToAddress("0x17c14D2c404D167802b16C450d3c99F88F2c4F4d"),
|
|
Type: contracts.ContractTypeUniswapV3Pool,
|
|
Name: "USDC/WETH 0.3%",
|
|
Token0: common.HexToAddress("0xA0b86a33E6D8E4BBa6Fd6bD5BA0e2FF8A1e8B8D4"), // USDC
|
|
Token1: common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"), // WETH
|
|
Fee: 3000, // 0.3%
|
|
Factory: common.HexToAddress("0x1F98431c8aD98523631AE4a59f267346ea31F984"),
|
|
Protocol: "UniswapV3",
|
|
IsVerified: true,
|
|
LastUpdated: now,
|
|
Confidence: 1.0,
|
|
},
|
|
common.HexToAddress("0x2f5e87C9312fa29aed5c179E456625D79015299c"): {
|
|
Address: common.HexToAddress("0x2f5e87C9312fa29aed5c179E456625D79015299c"),
|
|
Type: contracts.ContractTypeUniswapV3Pool,
|
|
Name: "WBTC/WETH 0.05%",
|
|
Token0: common.HexToAddress("0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f"), // WBTC
|
|
Token1: common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"), // WETH
|
|
Fee: 500, // 0.05%
|
|
Factory: common.HexToAddress("0x1F98431c8aD98523631AE4a59f267346ea31F984"),
|
|
Protocol: "UniswapV3",
|
|
IsVerified: true,
|
|
LastUpdated: now,
|
|
Confidence: 1.0,
|
|
},
|
|
common.HexToAddress("0x641C00A822e8b671738d32a431a4Fb6074E5c79d"): {
|
|
Address: common.HexToAddress("0x641C00A822e8b671738d32a431a4Fb6074E5c79d"),
|
|
Type: contracts.ContractTypeUniswapV3Pool,
|
|
Name: "USDT/WETH 0.05%",
|
|
Token0: common.HexToAddress("0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9"), // USDT
|
|
Token1: common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"), // WETH
|
|
Fee: 500, // 0.05%
|
|
Factory: common.HexToAddress("0x1F98431c8aD98523631AE4a59f267346ea31F984"),
|
|
Protocol: "UniswapV3",
|
|
IsVerified: true,
|
|
LastUpdated: now,
|
|
Confidence: 1.0,
|
|
},
|
|
common.HexToAddress("0xFe7D6a84287235C7b4b57C4fEb9a44d4C6Ed3BB8"): {
|
|
Address: common.HexToAddress("0xFe7D6a84287235C7b4b57C4fEb9a44d4C6Ed3BB8"),
|
|
Type: contracts.ContractTypeUniswapV3Pool,
|
|
Name: "ARB/WETH 0.05%",
|
|
Token0: common.HexToAddress("0x912CE59144191C1204E64559FE8253a0e49E6548"), // ARB
|
|
Token1: common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"), // WETH
|
|
Fee: 500, // 0.05%
|
|
Factory: common.HexToAddress("0x1F98431c8aD98523631AE4a59f267346ea31F984"),
|
|
Protocol: "UniswapV3",
|
|
IsVerified: true,
|
|
LastUpdated: now,
|
|
Confidence: 1.0,
|
|
},
|
|
}
|
|
|
|
// Major routers on Arbitrum
|
|
knownRouters := map[common.Address]*ContractInfo{
|
|
common.HexToAddress("0xE592427A0AEce92De3Edee1F18E0157C05861564"): {
|
|
Address: common.HexToAddress("0xE592427A0AEce92De3Edee1F18E0157C05861564"),
|
|
Type: contracts.ContractTypeUniswapV3Router,
|
|
Name: "Uniswap V3 Router",
|
|
Protocol: "UniswapV3",
|
|
IsVerified: true,
|
|
LastUpdated: now,
|
|
Confidence: 1.0,
|
|
},
|
|
common.HexToAddress("0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45"): {
|
|
Address: common.HexToAddress("0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45"),
|
|
Type: contracts.ContractTypeUniswapV3Router,
|
|
Name: "Uniswap V3 Router 2",
|
|
Protocol: "UniswapV3",
|
|
IsVerified: true,
|
|
LastUpdated: now,
|
|
Confidence: 1.0,
|
|
},
|
|
common.HexToAddress("0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506"): {
|
|
Address: common.HexToAddress("0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506"),
|
|
Type: contracts.ContractTypeUniswapV2Router,
|
|
Name: "SushiSwap Router",
|
|
Protocol: "SushiSwap",
|
|
IsVerified: true,
|
|
LastUpdated: now,
|
|
Confidence: 1.0,
|
|
},
|
|
}
|
|
|
|
// Add all known contracts
|
|
for addr, info := range knownTokens {
|
|
cr.contracts[addr] = info
|
|
cr.tokensBySymbol[info.Symbol] = addr
|
|
}
|
|
|
|
for addr, info := range knownPools {
|
|
cr.contracts[addr] = info
|
|
// Index pools by token pair
|
|
if info.Token0 != (common.Address{}) && info.Token1 != (common.Address{}) {
|
|
pairKey := cr.makeTokenPairKey(info.Token0, info.Token1)
|
|
cr.poolsByTokenPair[pairKey] = append(cr.poolsByTokenPair[pairKey], addr)
|
|
}
|
|
}
|
|
|
|
for addr, info := range knownRouters {
|
|
cr.contracts[addr] = info
|
|
}
|
|
|
|
cr.logger.Info("Contract registry initialized",
|
|
"tokens", len(knownTokens),
|
|
"pools", len(knownPools),
|
|
"routers", len(knownRouters))
|
|
}
|
|
|
|
// GetContractInfo retrieves contract information, using cache first then detection
|
|
func (cr *ContractRegistry) GetContractInfo(ctx context.Context, address common.Address) (*ContractInfo, error) {
|
|
// Try cache first
|
|
if info := cr.getCachedInfo(address); info != nil {
|
|
return info, nil
|
|
}
|
|
|
|
// Not in cache, use detector
|
|
detection := cr.detector.DetectContractType(ctx, address)
|
|
if detection.Error != nil {
|
|
return nil, fmt.Errorf("contract detection failed: %w", detection.Error)
|
|
}
|
|
|
|
// Create contract info from detection
|
|
info := &ContractInfo{
|
|
Address: address,
|
|
Type: detection.ContractType,
|
|
Name: fmt.Sprintf("Unknown %s", detection.ContractType.String()),
|
|
Protocol: "Unknown",
|
|
IsVerified: false,
|
|
LastUpdated: time.Now(),
|
|
Confidence: detection.Confidence,
|
|
}
|
|
|
|
// Cache the result
|
|
cr.cacheContractInfo(info)
|
|
|
|
return info, nil
|
|
}
|
|
|
|
// getCachedInfo safely retrieves cached contract info
|
|
func (cr *ContractRegistry) getCachedInfo(address common.Address) *ContractInfo {
|
|
cr.mu.RLock()
|
|
defer cr.mu.RUnlock()
|
|
return cr.contracts[address]
|
|
}
|
|
|
|
// cacheContractInfo safely caches contract info
|
|
func (cr *ContractRegistry) cacheContractInfo(info *ContractInfo) {
|
|
cr.mu.Lock()
|
|
defer cr.mu.Unlock()
|
|
cr.contracts[info.Address] = info
|
|
}
|
|
|
|
// IsKnownToken checks if an address is a known ERC-20 token
|
|
func (cr *ContractRegistry) IsKnownToken(address common.Address) bool {
|
|
cr.mu.RLock()
|
|
defer cr.mu.RUnlock()
|
|
|
|
if info, exists := cr.contracts[address]; exists {
|
|
return info.Type == contracts.ContractTypeERC20Token
|
|
}
|
|
return false
|
|
}
|
|
|
|
// IsKnownPool checks if an address is a known pool
|
|
func (cr *ContractRegistry) IsKnownPool(address common.Address) bool {
|
|
cr.mu.RLock()
|
|
defer cr.mu.RUnlock()
|
|
|
|
if info, exists := cr.contracts[address]; exists {
|
|
return info.Type == contracts.ContractTypeUniswapV2Pool ||
|
|
info.Type == contracts.ContractTypeUniswapV3Pool
|
|
}
|
|
return false
|
|
}
|
|
|
|
// IsKnownRouter checks if an address is a known router
|
|
func (cr *ContractRegistry) IsKnownRouter(address common.Address) bool {
|
|
cr.mu.RLock()
|
|
defer cr.mu.RUnlock()
|
|
|
|
if info, exists := cr.contracts[address]; exists {
|
|
return info.Type == contracts.ContractTypeUniswapV2Router ||
|
|
info.Type == contracts.ContractTypeUniswapV3Router ||
|
|
info.Type == contracts.ContractTypeUniversalRouter
|
|
}
|
|
return false
|
|
}
|
|
|
|
// GetTokenBySymbol retrieves a token address by symbol
|
|
func (cr *ContractRegistry) GetTokenBySymbol(symbol string) (common.Address, bool) {
|
|
cr.mu.RLock()
|
|
defer cr.mu.RUnlock()
|
|
|
|
addr, exists := cr.tokensBySymbol[symbol]
|
|
return addr, exists
|
|
}
|
|
|
|
// GetPoolsForTokenPair retrieves pools for a given token pair
|
|
func (cr *ContractRegistry) GetPoolsForTokenPair(token0, token1 common.Address) []common.Address {
|
|
cr.mu.RLock()
|
|
defer cr.mu.RUnlock()
|
|
|
|
pairKey := cr.makeTokenPairKey(token0, token1)
|
|
return cr.poolsByTokenPair[pairKey]
|
|
}
|
|
|
|
// makeTokenPairKey creates a consistent key for token pairs
|
|
func (cr *ContractRegistry) makeTokenPairKey(token0, token1 common.Address) string {
|
|
// Ensure consistent ordering
|
|
if token0.Big().Cmp(token1.Big()) > 0 {
|
|
token0, token1 = token1, token0
|
|
}
|
|
return fmt.Sprintf("%s:%s", token0.Hex(), token1.Hex())
|
|
}
|
|
|
|
// GetKnownContracts returns all cached contracts
|
|
func (cr *ContractRegistry) GetKnownContracts() map[common.Address]*ContractInfo {
|
|
cr.mu.RLock()
|
|
defer cr.mu.RUnlock()
|
|
|
|
// Return a copy to prevent external modification
|
|
result := make(map[common.Address]*ContractInfo)
|
|
for addr, info := range cr.contracts {
|
|
result[addr] = info
|
|
}
|
|
return result
|
|
}
|
|
|
|
// UpdateContractInfo updates contract information (for dynamic discovery)
|
|
func (cr *ContractRegistry) UpdateContractInfo(info *ContractInfo) {
|
|
cr.mu.Lock()
|
|
defer cr.mu.Unlock()
|
|
|
|
info.LastUpdated = time.Now()
|
|
cr.contracts[info.Address] = info
|
|
|
|
// Update indexes
|
|
if info.Type == contracts.ContractTypeERC20Token && info.Symbol != "" {
|
|
cr.tokensBySymbol[info.Symbol] = info.Address
|
|
}
|
|
|
|
if (info.Type == contracts.ContractTypeUniswapV2Pool || info.Type == contracts.ContractTypeUniswapV3Pool) &&
|
|
info.Token0 != (common.Address{}) && info.Token1 != (common.Address{}) {
|
|
pairKey := cr.makeTokenPairKey(info.Token0, info.Token1)
|
|
// Check if already exists in slice
|
|
pools := cr.poolsByTokenPair[pairKey]
|
|
exists := false
|
|
for _, pool := range pools {
|
|
if pool == info.Address {
|
|
exists = true
|
|
break
|
|
}
|
|
}
|
|
if !exists {
|
|
cr.poolsByTokenPair[pairKey] = append(pools, info.Address)
|
|
}
|
|
}
|
|
}
|
|
|
|
// GetCacheStats returns statistics about the cached contracts
|
|
func (cr *ContractRegistry) GetCacheStats() map[string]int {
|
|
cr.mu.RLock()
|
|
defer cr.mu.RUnlock()
|
|
|
|
stats := map[string]int{
|
|
"total": len(cr.contracts),
|
|
"tokens": 0,
|
|
"v2_pools": 0,
|
|
"v3_pools": 0,
|
|
"routers": 0,
|
|
"verified": 0,
|
|
}
|
|
|
|
for _, info := range cr.contracts {
|
|
switch info.Type {
|
|
case contracts.ContractTypeERC20Token:
|
|
stats["tokens"]++
|
|
case contracts.ContractTypeUniswapV2Pool:
|
|
stats["v2_pools"]++
|
|
case contracts.ContractTypeUniswapV3Pool:
|
|
stats["v3_pools"]++
|
|
case contracts.ContractTypeUniswapV2Router, contracts.ContractTypeUniswapV3Router:
|
|
stats["routers"]++
|
|
}
|
|
|
|
if info.IsVerified {
|
|
stats["verified"]++
|
|
}
|
|
}
|
|
|
|
return stats
|
|
}
|
|
|
|
// GetPoolInfo returns pool information for a given address
|
|
func (cr *ContractRegistry) GetPoolInfo(address common.Address) *ContractInfo {
|
|
cr.mu.RLock()
|
|
defer cr.mu.RUnlock()
|
|
|
|
if info, exists := cr.contracts[address]; exists {
|
|
if info.Type == contracts.ContractTypeUniswapV2Pool || info.Type == contracts.ContractTypeUniswapV3Pool {
|
|
return info
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// AddPool adds a pool to the registry
|
|
func (cr *ContractRegistry) AddPool(poolAddress, token0, token1 common.Address, protocol string) {
|
|
cr.mu.Lock()
|
|
defer cr.mu.Unlock()
|
|
|
|
// Determine pool type based on protocol
|
|
var poolType contracts.ContractType
|
|
switch protocol {
|
|
case "UniswapV2":
|
|
poolType = contracts.ContractTypeUniswapV2Pool
|
|
case "UniswapV3":
|
|
poolType = contracts.ContractTypeUniswapV3Pool
|
|
default:
|
|
poolType = contracts.ContractTypeUniswapV2Pool // Default to V2
|
|
}
|
|
|
|
// Add or update pool info
|
|
info := &ContractInfo{
|
|
Address: poolAddress,
|
|
Type: poolType,
|
|
Token0: token0,
|
|
Token1: token1,
|
|
Protocol: protocol,
|
|
IsVerified: false, // Runtime discovered pools are not pre-verified
|
|
LastUpdated: time.Now(),
|
|
Confidence: 0.8, // High confidence for runtime discovered pools
|
|
}
|
|
|
|
cr.contracts[poolAddress] = info
|
|
|
|
// Update token pair mapping
|
|
pairKey := cr.makeTokenPairKey(token0, token1)
|
|
if pools, exists := cr.poolsByTokenPair[pairKey]; exists {
|
|
// Check if pool already exists in the list
|
|
for _, existingPool := range pools {
|
|
if existingPool == poolAddress {
|
|
return // Already exists
|
|
}
|
|
}
|
|
cr.poolsByTokenPair[pairKey] = append(pools, poolAddress)
|
|
} else {
|
|
cr.poolsByTokenPair[pairKey] = []common.Address{poolAddress}
|
|
}
|
|
}
|