Files
mev-beta/pkg/arbitrum/new_parsers_test.go
Krypto Kajun 3f69aeafcf fix: resolve all compilation issues across transport and lifecycle packages
- Fixed duplicate type declarations in transport package
- Removed unused variables in lifecycle and dependency injection
- Fixed big.Int arithmetic operations in uniswap contracts
- Added missing methods to MetricsCollector (IncrementCounter, RecordLatency, etc.)
- Fixed jitter calculation in TCP transport retry logic
- Updated ComponentHealth field access to use transport type
- Ensured all core packages build successfully

All major compilation errors resolved:
 Transport package builds clean
 Lifecycle package builds clean
 Main MEV bot application builds clean
 Fixed method signature mismatches
 Resolved type conflicts and duplications

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-19 17:23:14 -05:00

472 lines
16 KiB
Go

package arbitrum
import (
"context"
"math/big"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rpc"
"github.com/fraktal/mev-beta/internal/logger"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// Mock RPC client for testing
type mockRPCClient struct {
responses map[string]interface{}
}
func (m *mockRPCClient) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error {
// Mock successful responses based on method
switch method {
case "eth_call":
// Mock token0/token1/fee responses
call := args[0].(map[string]interface{})
data := call["data"].(string)
// Mock responses for different function calls
switch {
case len(data) >= 10 && data[2:10] == "0d0e30db": // token0()
*result.(*string) = "0x000000000000000000000000A0b86a33E6441f43E2e4A96439abFA2A69067ACD" // Mock token address
case len(data) >= 10 && data[2:10] == "d21220a7": // token1()
*result.(*string) = "0x000000000000000000000000af88d065e77c8cC2239327C5EDb3A432268e5831" // Mock token address
case len(data) >= 10 && data[2:10] == "ddca3f43": // fee()
*result.(*string) = "0x0000000000000000000000000000000000000000000000000000000000000bb8" // 3000 (0.3%)
case len(data) >= 10 && data[2:10] == "fc0e74d1": // getTokenX()
*result.(*string) = "0x000000000000000000000000A0b86a33E6441f43E2e4A96439abFA2A69067ACD" // Mock token address
case len(data) >= 10 && data[2:10] == "8cc8b9a9": // getTokenY()
*result.(*string) = "0x000000000000000000000000af88d065e77c8cC2239327C5EDb3A432268e5831" // Mock token address
case len(data) >= 10 && data[2:10] == "69fe0e2d": // getBinStep()
*result.(*string) = "0x0000000000000000000000000000000000000000000000000000000000000019" // 25 (bin step)
default:
*result.(*string) = "0x0000000000000000000000000000000000000000000000000000000000000000"
}
case "eth_getLogs":
// Mock empty logs for pool discovery
*result.(*[]interface{}) = []interface{}{}
}
return nil
}
func createMockLogger() *logger.Logger {
return logger.New("debug", "text", "")
}
func createMockRPCClient() *rpc.Client {
// Create a mock that satisfies the interface
return &rpc.Client{}
}
// Test CamelotV3Parser
func TestCamelotV3Parser_New(t *testing.T) {
client := createMockRPCClient()
logger := createMockLogger()
parser := NewCamelotV3Parser(client, logger)
require.NotNil(t, parser)
camelotParser, ok := parser.(*CamelotV3Parser)
require.True(t, ok, "Parser should be CamelotV3Parser type")
assert.Equal(t, ProtocolCamelotV3, camelotParser.protocol)
}
func TestCamelotV3Parser_GetSupportedContractTypes(t *testing.T) {
client := createMockRPCClient()
logger := createMockLogger()
parser := NewCamelotV3Parser(client, logger).(*CamelotV3Parser)
types := parser.GetSupportedContractTypes()
assert.Contains(t, types, ContractTypeFactory)
assert.Contains(t, types, ContractTypeRouter)
assert.Contains(t, types, ContractTypePool)
}
func TestCamelotV3Parser_GetSupportedEventTypes(t *testing.T) {
client := createMockRPCClient()
logger := createMockLogger()
parser := NewCamelotV3Parser(client, logger).(*CamelotV3Parser)
events := parser.GetSupportedEventTypes()
assert.Contains(t, events, EventTypeSwap)
assert.Contains(t, events, EventTypeLiquidityAdd)
assert.Contains(t, events, EventTypeLiquidityRemove)
assert.Contains(t, events, EventTypePoolCreated)
assert.Contains(t, events, EventTypePositionUpdate)
}
func TestCamelotV3Parser_IsKnownContract(t *testing.T) {
client := createMockRPCClient()
logger := createMockLogger()
parser := NewCamelotV3Parser(client, logger).(*CamelotV3Parser)
// Test known contract (factory)
factoryAddr := common.HexToAddress("0x1a3c9B1d2F0529D97f2afC5136Cc23e58f1FD35B")
assert.True(t, parser.IsKnownContract(factoryAddr))
// Test unknown contract
unknownAddr := common.HexToAddress("0x1234567890123456789012345678901234567890")
assert.False(t, parser.IsKnownContract(unknownAddr))
}
func TestCamelotV3Parser_ParseLog(t *testing.T) {
client := createMockRPCClient()
logger := createMockLogger()
parser := NewCamelotV3Parser(client, logger).(*CamelotV3Parser)
// Create mock swap log
factoryAddr := common.HexToAddress("0x1a3c9B1d2F0529D97f2afC5136Cc23e58f1FD35B")
_ = parser.eventSigs // Reference to avoid unused variable warning
log := &types.Log{
Address: factoryAddr,
Topics: []common.Hash{
common.HexToHash("0xe14ced199d67634c498b12b8ffc4244e2be5b5f2b3b7b0db5c35b2c73b89b3b8"), // Swap event topic
common.HexToHash("0x000000000000000000000000742d35Cc6AaB8f5d6649c8C4F7C6b2d1234567890"), // sender
common.HexToHash("0x000000000000000000000000742d35Cc6AaB8f5d6649c8C4F7C6b2d0987654321"), // recipient
},
Data: make([]byte, 160), // 5 * 32 bytes for non-indexed params
}
event, err := parser.ParseLog(log)
if err == nil && event != nil {
assert.Equal(t, ProtocolCamelotV3, event.Protocol)
assert.NotNil(t, event.DecodedParams)
}
}
// Test TraderJoeV2Parser
func TestTraderJoeV2Parser_New(t *testing.T) {
client := createMockRPCClient()
logger := createMockLogger()
parser := NewTraderJoeV2Parser(client, logger)
require.NotNil(t, parser)
tjParser, ok := parser.(*TraderJoeV2Parser)
require.True(t, ok, "Parser should be TraderJoeV2Parser type")
assert.Equal(t, ProtocolTraderJoeV2, tjParser.protocol)
}
func TestTraderJoeV2Parser_GetSupportedContractTypes(t *testing.T) {
client := createMockRPCClient()
logger := createMockLogger()
parser := NewTraderJoeV2Parser(client, logger).(*TraderJoeV2Parser)
types := parser.GetSupportedContractTypes()
assert.Contains(t, types, ContractTypeFactory)
assert.Contains(t, types, ContractTypeRouter)
assert.Contains(t, types, ContractTypePool)
}
func TestTraderJoeV2Parser_GetSupportedEventTypes(t *testing.T) {
client := createMockRPCClient()
logger := createMockLogger()
parser := NewTraderJoeV2Parser(client, logger).(*TraderJoeV2Parser)
events := parser.GetSupportedEventTypes()
assert.Contains(t, events, EventTypeSwap)
assert.Contains(t, events, EventTypeLiquidityAdd)
assert.Contains(t, events, EventTypeLiquidityRemove)
assert.Contains(t, events, EventTypePoolCreated)
}
func TestTraderJoeV2Parser_IsKnownContract(t *testing.T) {
client := createMockRPCClient()
logger := createMockLogger()
parser := NewTraderJoeV2Parser(client, logger).(*TraderJoeV2Parser)
// Test known contract (factory)
factoryAddr := common.HexToAddress("0x8e42f2F4101563bF679975178e880FD87d3eFd4e")
assert.True(t, parser.IsKnownContract(factoryAddr))
// Test unknown contract
unknownAddr := common.HexToAddress("0x1234567890123456789012345678901234567890")
assert.False(t, parser.IsKnownContract(unknownAddr))
}
func TestTraderJoeV2Parser_ParseTransactionData(t *testing.T) {
client := createMockRPCClient()
logger := createMockLogger()
parser := NewTraderJoeV2Parser(client, logger).(*TraderJoeV2Parser)
// Create mock transaction data for swapExactTokensForTokens
data := make([]byte, 324)
copy(data[0:4], []byte{0x38, 0xed, 0x17, 0x39}) // Function selector
// Add mock token addresses and amounts
tokenX := common.HexToAddress("0xA0b86a33E6441f43E2e4A96439abFA2A69067ACD")
tokenY := common.HexToAddress("0xaf88d065e77c8cC2239327C5EDb3A432268e5831")
copy(data[16:32], tokenX.Bytes())
copy(data[48:64], tokenY.Bytes())
tx := types.NewTransaction(0, common.Address{}, big.NewInt(0), 0, big.NewInt(0), data)
event, err := parser.ParseTransactionData(tx)
if err == nil && event != nil {
assert.Equal(t, ProtocolTraderJoeV2, event.Protocol)
assert.Equal(t, EventTypeSwap, event.EventType)
assert.NotNil(t, event.DecodedParams)
}
}
// Test KyberElasticParser
func TestKyberElasticParser_New(t *testing.T) {
client := createMockRPCClient()
logger := createMockLogger()
parser := NewKyberElasticParser(client, logger)
require.NotNil(t, parser)
kyberParser, ok := parser.(*KyberElasticParser)
require.True(t, ok, "Parser should be KyberElasticParser type")
assert.Equal(t, ProtocolKyberElastic, kyberParser.protocol)
}
func TestKyberElasticParser_GetSupportedContractTypes(t *testing.T) {
client := createMockRPCClient()
logger := createMockLogger()
parser := NewKyberElasticParser(client, logger).(*KyberElasticParser)
types := parser.GetSupportedContractTypes()
assert.Contains(t, types, ContractTypeFactory)
assert.Contains(t, types, ContractTypeRouter)
assert.Contains(t, types, ContractTypePool)
}
func TestKyberElasticParser_GetSupportedEventTypes(t *testing.T) {
client := createMockRPCClient()
logger := createMockLogger()
parser := NewKyberElasticParser(client, logger).(*KyberElasticParser)
events := parser.GetSupportedEventTypes()
assert.Contains(t, events, EventTypeSwap)
assert.Contains(t, events, EventTypeLiquidityAdd)
assert.Contains(t, events, EventTypeLiquidityRemove)
assert.Contains(t, events, EventTypePoolCreated)
assert.Contains(t, events, EventTypePositionUpdate)
}
func TestKyberElasticParser_IsKnownContract(t *testing.T) {
client := createMockRPCClient()
logger := createMockLogger()
parser := NewKyberElasticParser(client, logger).(*KyberElasticParser)
// Test known contract (factory)
factoryAddr := common.HexToAddress("0x5F1dddbf348aC2fbe22a163e30F99F9ECE3DD50a")
assert.True(t, parser.IsKnownContract(factoryAddr))
// Test unknown contract
unknownAddr := common.HexToAddress("0x1234567890123456789012345678901234567890")
assert.False(t, parser.IsKnownContract(unknownAddr))
}
func TestKyberElasticParser_DecodeFunctionCall(t *testing.T) {
client := createMockRPCClient()
logger := createMockLogger()
parser := NewKyberElasticParser(client, logger).(*KyberElasticParser)
// Create mock function call data for exactInputSingle
data := make([]byte, 228)
copy(data[0:4], []byte{0x04, 0xe4, 0x5a, 0xaf}) // Function selector
// Add mock token addresses
tokenA := common.HexToAddress("0xA0b86a33E6441f43E2e4A96439abFA2A69067ACD")
tokenB := common.HexToAddress("0xaf88d065e77c8cC2239327C5EDb3A432268e5831")
copy(data[16:32], tokenA.Bytes())
copy(data[48:64], tokenB.Bytes())
event, err := parser.DecodeFunctionCall(data)
if err == nil && event != nil {
assert.Equal(t, ProtocolKyberElastic, event.Protocol)
assert.Equal(t, EventTypeSwap, event.EventType)
assert.NotNil(t, event.DecodedParams)
}
}
// Test GetPoolInfo with mock RPC responses
func TestCamelotV3Parser_GetPoolInfo_WithMockRPC(t *testing.T) {
// Create a more sophisticated mock
_ = &mockRPCClient{
responses: make(map[string]interface{}),
}
logger := createMockLogger()
parser := NewCamelotV3Parser(nil, logger).(*CamelotV3Parser)
poolAddr := common.HexToAddress("0x1234567890123456789012345678901234567890")
// This would normally call the RPC, but we'll test the structure
// In a real implementation, we'd use dependency injection or interfaces
// for proper mocking of the RPC client
// Test that the method exists and has correct signature
assert.NotNil(t, parser.GetPoolInfo)
// Test with nil client should return error
_, err := parser.GetPoolInfo(poolAddr)
assert.Error(t, err) // Should fail due to nil client
}
// Integration test for DiscoverPools
func TestTraderJoeV2Parser_DiscoverPools(t *testing.T) {
client := createMockRPCClient()
logger := createMockLogger()
parser := NewTraderJoeV2Parser(client, logger).(*TraderJoeV2Parser)
// Test pool discovery
pools, err := parser.DiscoverPools(1000000, 1000010)
// Should return empty pools due to mock, but no error
assert.NoError(t, err)
assert.NotNil(t, pools)
assert.Equal(t, 0, len(pools)) // Mock returns empty
}
// Test ParseTransactionLogs
func TestKyberElasticParser_ParseTransactionLogs(t *testing.T) {
client := createMockRPCClient()
logger := createMockLogger()
parser := NewKyberElasticParser(client, logger).(*KyberElasticParser)
// Create mock transaction and receipt
tx := types.NewTransaction(0, common.Address{}, big.NewInt(0), 0, big.NewInt(0), []byte{})
receipt := &types.Receipt{
BlockNumber: big.NewInt(1000000),
Logs: []*types.Log{
{
Address: common.HexToAddress("0x5F1dddbf348aC2fbe22a163e30F99F9ECE3DD50a"),
Topics: []common.Hash{
common.HexToHash("0x1234567890123456789012345678901234567890123456789012345678901234"),
},
Data: make([]byte, 32),
},
},
}
events, err := parser.ParseTransactionLogs(tx, receipt)
assert.NoError(t, err)
assert.NotNil(t, events)
// Events might be empty due to unknown topic, but should not error
}
// Benchmark tests
func BenchmarkCamelotV3Parser_ParseLog(b *testing.B) {
client := createMockRPCClient()
logger := createMockLogger()
parser := NewCamelotV3Parser(client, logger).(*CamelotV3Parser)
log := &types.Log{
Address: common.HexToAddress("0x1a3c9B1d2F0529D97f2afC5136Cc23e58f1FD35B"),
Topics: []common.Hash{
common.HexToHash("0x1234567890123456789012345678901234567890123456789012345678901234"),
},
Data: make([]byte, 160),
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
parser.ParseLog(log)
}
}
func BenchmarkTraderJoeV2Parser_DecodeFunctionCall(b *testing.B) {
client := createMockRPCClient()
logger := createMockLogger()
parser := NewTraderJoeV2Parser(client, logger).(*TraderJoeV2Parser)
data := make([]byte, 324)
copy(data[0:4], []byte{0x38, 0xed, 0x17, 0x39}) // Function selector
b.ResetTimer()
for i := 0; i < b.N; i++ {
parser.DecodeFunctionCall(data)
}
}
// Test error handling
func TestParsers_ErrorHandling(t *testing.T) {
client := createMockRPCClient()
logger := createMockLogger()
parsers := []DEXParserInterface{
NewCamelotV3Parser(client, logger),
NewTraderJoeV2Parser(client, logger),
NewKyberElasticParser(client, logger),
}
for _, parser := range parsers {
// Test with invalid data
_, err := parser.DecodeFunctionCall([]byte{0x01, 0x02}) // Too short
assert.Error(t, err, "Should error on too short data")
// Test with unknown function selector
_, err = parser.DecodeFunctionCall([]byte{0xFF, 0xFF, 0xFF, 0xFF, 0x00})
assert.Error(t, err, "Should error on unknown selector")
// Test empty transaction data
tx := types.NewTransaction(0, common.Address{}, big.NewInt(0), 0, big.NewInt(0), []byte{})
_, err = parser.ParseTransactionData(tx)
assert.Error(t, err, "Should error on empty transaction data")
}
}
// Test protocol-specific features
func TestTraderJoeV2Parser_LiquidityBookFeatures(t *testing.T) {
client := createMockRPCClient()
logger := createMockLogger()
parser := NewTraderJoeV2Parser(client, logger).(*TraderJoeV2Parser)
// Test that liquidity book specific events are supported
events := parser.GetSupportedEventTypes()
assert.Contains(t, events, EventTypeSwap)
assert.Contains(t, events, EventTypeLiquidityAdd)
assert.Contains(t, events, EventTypeLiquidityRemove)
// Test that event signatures are properly initialized
assert.NotEmpty(t, parser.eventSigs)
// Verify specific LB events exist
hasSwapEvent := false
hasDepositEvent := false
hasWithdrawEvent := false
for _, sig := range parser.eventSigs {
switch sig.Name {
case "Swap":
hasSwapEvent = true
case "DepositedToBins":
hasDepositEvent = true
case "WithdrawnFromBins":
hasWithdrawEvent = true
}
}
assert.True(t, hasSwapEvent, "Should have Swap event")
assert.True(t, hasDepositEvent, "Should have DepositedToBins event")
assert.True(t, hasWithdrawEvent, "Should have WithdrawnFromBins event")
}
func TestKyberElasticParser_ReinvestmentFeatures(t *testing.T) {
client := createMockRPCClient()
logger := createMockLogger()
parser := NewKyberElasticParser(client, logger).(*KyberElasticParser)
// Test that Kyber-specific events are supported
events := parser.GetSupportedEventTypes()
assert.Contains(t, events, EventTypeSwap)
assert.Contains(t, events, EventTypePositionUpdate)
// Test multiple router addresses (including meta router)
routers := parser.contracts[ContractTypeRouter]
assert.True(t, len(routers) >= 2, "Should have multiple router addresses")
// Test that factory address is set correctly
factories := parser.contracts[ContractTypeFactory]
assert.Equal(t, 1, len(factories), "Should have one factory address")
expectedFactory := common.HexToAddress("0x5F1dddbf348aC2fbe22a163e30F99F9ECE3DD50a")
assert.Equal(t, expectedFactory, factories[0])
}