//go:build legacy_pools // +build legacy_pools package pools import ( "math/big" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/fraktal/mev-beta/internal/logger" ) // TestNewPoolDiscovery tests the creation of a new PoolDiscovery func TestNewPoolDiscovery(t *testing.T) { logger := logger.New("info", "text", "") // Test with nil client (for testing purposes) pd := NewPoolDiscovery(nil, logger) require.NotNil(t, pd) assert.NotNil(t, pd.pools) assert.NotNil(t, pd.exchanges) assert.NotNil(t, pd.eventSignatures) assert.NotNil(t, pd.knownFactories) assert.NotNil(t, pd.minLiquidityThreshold) assert.Equal(t, 0.01, pd.priceImpactThreshold) } // TestInitializeEventSignatures tests the initialization of event signatures func TestInitializeEventSignatures(t *testing.T) { logger := logger.New("info", "text", "") pd := NewPoolDiscovery(nil, logger) // Check that key event signatures are present assert.Contains(t, pd.eventSignatures, "0x0d3648bd0f6ba80134a33ba9275ac585d9d315f0ad8355cddefde31afa28d0e9") // PairCreated assert.Contains(t, pd.eventSignatures, "0x783cca1c0412dd0d695e784568c96da2e9c22ff989357a2e8b1d9b2b4e6b7118") // PoolCreated assert.Contains(t, pd.eventSignatures, "0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822") // Swap } // TestInitializeKnownFactories tests the initialization of known factories func TestInitializeKnownFactories(t *testing.T) { logger := logger.New("info", "text", "") pd := NewPoolDiscovery(nil, logger) // Check that key factories are present assert.Contains(t, pd.knownFactories, "0xf1d7cc64fb4452f05c498126312ebe29f30fbcf9") // Uniswap V2 assert.Contains(t, pd.knownFactories, "0x1f98431c8ad98523631ae4a59f267346ea31f984") // Uniswap V3 assert.Contains(t, pd.knownFactories, "0xc35dadb65012ec5796536bd9864ed8773abc74c4") // SushiSwap } // TestPoolDiscovery_GetPoolCount tests getting pool count func TestPoolDiscovery_GetPoolCount(t *testing.T) { logger := logger.New("info", "text", "") pd := NewPoolDiscovery(nil, logger) // Initially should be zero count := pd.GetPoolCount() assert.Equal(t, 0, count) // Add a test pool pool := &Pool{ Address: "0x1234567890123456789012345678901234567890", Token0: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", Token1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", Fee: 3000, Protocol: "UniswapV3", Factory: "0x1f98431c8ad98523631ae4a59f267346ea31f984", LastUpdated: time.Now(), TotalVolume: big.NewInt(0), SwapCount: 0, } pd.pools["0x1234567890123456789012345678901234567890"] = pool // Should now be one count = pd.GetPoolCount() assert.Equal(t, 1, count) } // TestPoolDiscovery_GetExchangeCount tests getting exchange count func TestPoolDiscovery_GetExchangeCount(t *testing.T) { logger := logger.New("info", "text", "") pd := NewPoolDiscovery(nil, logger) // Initially should be zero count := pd.GetExchangeCount() assert.Equal(t, 0, count) // Add a test exchange exchange := &Exchange{ Name: "TestExchange", Router: "0x1234567890123456789012345678901234567890", Factory: "0x1f98431c8ad98523631ae4a59f267346ea31f984", Protocol: "UniswapV3", Version: "1.0", Discovered: time.Now(), PoolCount: 0, TotalVolume: big.NewInt(0), } pd.exchanges["0x1234567890123456789012345678901234567890"] = exchange // Should now be one count = pd.GetExchangeCount() assert.Equal(t, 1, count) } // TestPoolDiscovery_GetPool tests getting a pool by address func TestPoolDiscovery_GetPool(t *testing.T) { logger := logger.New("info", "text", "") pd := NewPoolDiscovery(nil, logger) // Test getting non-existent pool pool, exists := pd.GetPool("0x1234567890123456789012345678901234567890") assert.False(t, exists) assert.Nil(t, pool) // Add a test pool testPool := &Pool{ Address: "0x1234567890123456789012345678901234567890", Token0: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", Token1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", Fee: 3000, Protocol: "UniswapV3", Factory: "0x1f98431c8ad98523631ae4a59f267346ea31f984", LastUpdated: time.Now(), TotalVolume: big.NewInt(1000000000000000000), SwapCount: 5, } pd.pools["0x1234567890123456789012345678901234567890"] = testPool // Test getting existing pool pool, exists = pd.GetPool("0x1234567890123456789012345678901234567890") assert.True(t, exists) assert.NotNil(t, pool) assert.Equal(t, "0x1234567890123456789012345678901234567890", pool.Address) assert.Equal(t, "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", pool.Token0) assert.Equal(t, "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", pool.Token1) assert.Equal(t, uint32(3000), pool.Fee) assert.Equal(t, "UniswapV3", pool.Protocol) assert.Equal(t, "0x1f98431c8ad98523631ae4a59f267346ea31f984", pool.Factory) assert.Equal(t, int64(1000000000000000000), pool.TotalVolume.Int64()) assert.Equal(t, uint64(5), pool.SwapCount) } // TestPoolDiscovery_GetAllPools tests getting all pools func TestPoolDiscovery_GetAllPools(t *testing.T) { logger := logger.New("info", "text", "") pd := NewPoolDiscovery(nil, logger) // Test getting all pools when empty pools := pd.GetAllPools() assert.Empty(t, pools) // Add test pools pool1 := &Pool{ Address: "0x1234567890123456789012345678901234567890", Token0: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", Token1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", Fee: 3000, Protocol: "UniswapV3", Factory: "0x1f98431c8ad98523631ae4a59f267346ea31f984", LastUpdated: time.Now(), TotalVolume: big.NewInt(1000000000000000000), SwapCount: 5, } pool2 := &Pool{ Address: "0x0987654321098765432109876543210987654321", Token0: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", Token1: "0x1f98431c8ad98523631ae4a59f267346ea31f984", Fee: 500, Protocol: "UniswapV2", Factory: "0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f", LastUpdated: time.Now(), TotalVolume: big.NewInt(2000000000000000000), SwapCount: 10, } pd.pools["0x1234567890123456789012345678901234567890"] = pool1 pd.pools["0x0987654321098765432109876543210987654321"] = pool2 // Test getting all pools pools = pd.GetAllPools() assert.Len(t, pools, 2) assert.Contains(t, pools, "0x1234567890123456789012345678901234567890") assert.Contains(t, pools, "0x0987654321098765432109876543210987654321") // Verify pool data retrievedPool1 := pools["0x1234567890123456789012345678901234567890"] assert.Equal(t, pool1.Address, retrievedPool1.Address) assert.Equal(t, pool1.Token0, retrievedPool1.Token0) assert.Equal(t, pool1.Token1, retrievedPool1.Token1) assert.Equal(t, pool1.Fee, retrievedPool1.Fee) assert.Equal(t, pool1.Protocol, retrievedPool1.Protocol) assert.Equal(t, pool1.Factory, retrievedPool1.Factory) retrievedPool2 := pools["0x0987654321098765432109876543210987654321"] assert.Equal(t, pool2.Address, retrievedPool2.Address) assert.Equal(t, pool2.Token0, retrievedPool2.Token0) assert.Equal(t, pool2.Token1, retrievedPool2.Token1) assert.Equal(t, pool2.Fee, retrievedPool2.Fee) assert.Equal(t, pool2.Protocol, retrievedPool2.Protocol) assert.Equal(t, pool2.Factory, retrievedPool2.Factory) } // TestPoolDiscovery_PersistData tests data persistence functionality func TestPoolDiscovery_PersistData(t *testing.T) { logger := logger.New("info", "text", "") pd := NewPoolDiscovery(nil, logger) // Add test data pool := &Pool{ Address: "0x1234567890123456789012345678901234567890", Token0: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", Token1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", Fee: 3000, Protocol: "UniswapV3", Factory: "0x1f98431c8ad98523631ae4a59f267346ea31f984", LastUpdated: time.Now(), TotalVolume: big.NewInt(1000000000000000000), SwapCount: 5, } pd.pools["0x1234567890123456789012345678901234567890"] = pool // Test persistence (this will create files in the data directory) // Note: This test doesn't verify file contents, but ensures the method doesn't panic pd.persistData() // Test loading persisted data pd.loadPersistedData() // Verify data is still there assert.Len(t, pd.pools, 1) _, exists := pd.pools["0x1234567890123456789012345678901234567890"] assert.True(t, exists) } // TestCalculatePriceImpact tests price impact calculation func TestCalculatePriceImpact(t *testing.T) { logger := logger.New("info", "text", "") pd := NewPoolDiscovery(nil, logger) // Create a test pool with reserves pool := &Pool{ Reserves0: func() *big.Int { val, _ := big.NewInt(0).SetString("10000000000000000000", 10); return val }(), // 10 tokens Reserves1: func() *big.Int { val, _ := big.NewInt(0).SetString("20000000000000000000", 10); return val }(), // 20 tokens } // Test with zero reserves (should return 0) emptyPool := &Pool{} priceImpact := pd.calculatePriceImpact(emptyPool, big.NewInt(1000000000000000000), big.NewInt(1000000000000000000)) assert.Equal(t, 0.0, priceImpact) // Test with valid reserves amountIn := big.NewInt(1000000000000000000) // 1 token amountOut := big.NewInt(1990000000000000000) // ~1.99 tokens (due to 0.5% fee) priceImpact = pd.calculatePriceImpact(pool, amountIn, amountOut) // Price impact should be positive (small value due to small trade size) assert.True(t, priceImpact >= 0.0) // Test with larger trade largeAmountIn := func() *big.Int { val, _ := big.NewInt(0).SetString("1000000000000000000", 10); return val }() // 1 token largeAmountOut := func() *big.Int { val, _ := big.NewInt(0).SetString("1800000000000000000", 10); return val }() // ~1.8 tokens (larger price impact) priceImpact = pd.calculatePriceImpact(pool, largeAmountIn, largeAmountOut) // Larger trade should have larger price impact assert.True(t, priceImpact >= 0.0) } // TestParseSwapData tests parsing of swap data func TestParseSwapData(t *testing.T) { logger := logger.New("info", "text", "") pd := NewPoolDiscovery(nil, logger) // Test with empty data result := pd.parseSwapData("", "UniswapV2") assert.Nil(t, result) // Test with short data result = pd.parseSwapData("0x", "UniswapV2") assert.Nil(t, result) // Test parseLiquidityData data := "0x0000000000000000000000000000000000000000000000000000000000000001" + "0000000000000000000000000000000000000000000000000000000000000002" result2 := pd.parseLiquidityData(data, "Mint") assert.NotNil(t, result2) assert.Equal(t, int64(1), result2.AmountIn.Int64()) assert.Equal(t, int64(2), result2.AmountOut.Int64()) // Test parseSyncData syncData := "0000000000000000000000000000000000000000000000000000000000000001" + "0000000000000000000000000000000000000000000000000000000000000002" result3 := pd.parseSyncData(syncData) assert.NotNil(t, result3) assert.Equal(t, int64(1), result3.Reserve0.Int64()) assert.Equal(t, int64(2), result3.Reserve1.Int64()) } // TestPoolDiscovery_AddressFromTopic tests address extraction from topics func TestPoolDiscovery_AddressFromTopic(t *testing.T) { logger := logger.New("info", "text", "") pd := NewPoolDiscovery(nil, logger) // Test with invalid topic result := pd.addressFromTopic(nil) assert.Equal(t, "", result) // Test with short topic result = pd.addressFromTopic("0x1234") assert.Equal(t, "", result) // Test with valid topic (this function expects the full 32-byte topic) result = pd.addressFromTopic("0x0000000000000000000000001234567890123456789012345678901234567890") assert.Equal(t, "0x1234567890123456789012345678901234567890", result) }