package sequencer import ( "encoding/hex" "testing" "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // TestIsSwapTransaction_UniswapV2 tests detection of UniswapV2 swap transactions func TestIsSwapTransaction_UniswapV2(t *testing.T) { tests := []struct { name string selector string wantSwap bool }{ { name: "swapExactTokensForTokens", selector: "38ed1739", wantSwap: true, }, { name: "swapTokensForExactTokens", selector: "8803dbee", wantSwap: true, }, { name: "swapExactETHForTokens", selector: "7ff36ab5", wantSwap: true, }, { name: "swapETHForExactTokens", selector: "fb3bdb41", wantSwap: true, }, { name: "swapExactTokensForETH", selector: "18cbafe5", wantSwap: true, }, { name: "swapTokensForExactETH", selector: "4a25d94a", wantSwap: true, }, { name: "direct pool swap", selector: "022c0d9f", wantSwap: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Create transaction data with selector + dummy parameters data, err := hex.DecodeString(tt.selector + "000000000000000000000000000000000000000000000000") require.NoError(t, err) got := IsSwapTransaction(data) assert.Equal(t, tt.wantSwap, got, "IsSwapTransaction() for %s", tt.name) }) } } // TestIsSwapTransaction_UniswapV3 tests detection of UniswapV3 swap transactions func TestIsSwapTransaction_UniswapV3(t *testing.T) { tests := []struct { name string selector string wantSwap bool }{ { name: "exactInputSingle", selector: "414bf389", wantSwap: true, }, { name: "exactInput", selector: "c04b8d59", wantSwap: true, }, { name: "exactOutputSingle", selector: "db3e2198", wantSwap: true, }, { name: "exactOutput", selector: "f28c0498", wantSwap: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { data, err := hex.DecodeString(tt.selector + "000000000000000000000000000000000000000000000000") require.NoError(t, err) got := IsSwapTransaction(data) assert.Equal(t, tt.wantSwap, got, "IsSwapTransaction() for %s", tt.name) }) } } // TestIsSwapTransaction_Curve tests detection of Curve swap transactions func TestIsSwapTransaction_Curve(t *testing.T) { tests := []struct { name string selector string wantSwap bool }{ { name: "exchange", selector: "3df02124", wantSwap: true, }, { name: "exchange_underlying", selector: "a6417ed6", wantSwap: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { data, err := hex.DecodeString(tt.selector + "000000000000000000000000000000000000000000000000") require.NoError(t, err) got := IsSwapTransaction(data) assert.Equal(t, tt.wantSwap, got, "IsSwapTransaction() for %s", tt.name) }) } } // TestIsSwapTransaction_1inch tests detection of 1inch swap transactions func TestIsSwapTransaction_1inch(t *testing.T) { tests := []struct { name string selector string wantSwap bool }{ { name: "1inch swap", selector: "7c025200", wantSwap: true, }, { name: "1inch uniswapV3Swap", selector: "e449022e", wantSwap: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { data, err := hex.DecodeString(tt.selector + "000000000000000000000000000000000000000000000000") require.NoError(t, err) got := IsSwapTransaction(data) assert.Equal(t, tt.wantSwap, got, "IsSwapTransaction() for %s", tt.name) }) } } // TestIsSwapTransaction_0xProtocol tests detection of 0x Protocol swap transactions func TestIsSwapTransaction_0xProtocol(t *testing.T) { tests := []struct { name string selector string wantSwap bool }{ { name: "sellToUniswap", selector: "d9627aa4", wantSwap: true, }, { name: "fillRfqOrder", selector: "415565b0", wantSwap: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { data, err := hex.DecodeString(tt.selector + "000000000000000000000000000000000000000000000000") require.NoError(t, err) got := IsSwapTransaction(data) assert.Equal(t, tt.wantSwap, got, "IsSwapTransaction() for %s", tt.name) }) } } // TestIsSwapTransaction_NonSwap tests that non-swap transactions are not detected func TestIsSwapTransaction_NonSwap(t *testing.T) { tests := []struct { name string selector string wantSwap bool }{ { name: "transfer", selector: "a9059cbb", wantSwap: false, }, { name: "approve", selector: "095ea7b3", wantSwap: false, }, { name: "transferFrom", selector: "23b872dd", wantSwap: false, }, { name: "mint", selector: "40c10f19", wantSwap: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { data, err := hex.DecodeString(tt.selector + "000000000000000000000000000000000000000000000000") require.NoError(t, err) got := IsSwapTransaction(data) assert.Equal(t, tt.wantSwap, got, "IsSwapTransaction() for %s", tt.name) }) } } // TestIsSwapTransaction_EdgeCases tests edge cases func TestIsSwapTransaction_EdgeCases(t *testing.T) { tests := []struct { name string data []byte wantSwap bool }{ { name: "empty data", data: []byte{}, wantSwap: false, }, { name: "data too short (3 bytes)", data: []byte{0x01, 0x02, 0x03}, wantSwap: false, }, { name: "exactly 4 bytes - valid swap selector", data: []byte{0x02, 0x2c, 0x0d, 0x9f}, // 022c0d9f = swap wantSwap: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := IsSwapTransaction(tt.data) assert.Equal(t, tt.wantSwap, got, "IsSwapTransaction() for %s", tt.name) }) } } // TestGetSwapProtocol_BySelector tests protocol detection by function selector func TestGetSwapProtocol_BySelector(t *testing.T) { tests := []struct { name string selector string wantName string wantType string shouldDetect bool }{ { name: "UniswapV2 direct pool swap", selector: "022c0d9f", wantName: "UniswapV2", wantType: "pool", shouldDetect: true, }, { name: "UniswapV2 router swapExactTokensForTokens", selector: "38ed1739", wantName: "UniswapV2", wantType: "router", shouldDetect: true, }, { name: "UniswapV3 exactInputSingle", selector: "414bf389", wantName: "UniswapV3", wantType: "router", shouldDetect: true, }, { name: "Curve exchange", selector: "3df02124", wantName: "Curve", wantType: "pool", shouldDetect: true, }, { name: "Balancer swap", selector: "52bbbe29", wantName: "Balancer", wantType: "vault", shouldDetect: true, }, { name: "Camelot V3 exactInputSingle", selector: "128acb08", wantName: "Camelot", wantType: "router", shouldDetect: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Create dummy address (not zero) addr := common.HexToAddress("0x1234567890123456789012345678901234567890") // Create transaction data with selector data, err := hex.DecodeString(tt.selector + "000000000000000000000000000000000000000000000000") require.NoError(t, err) protocol := GetSwapProtocol(&addr, data) require.NotNil(t, protocol) if tt.shouldDetect { assert.Equal(t, tt.wantName, protocol.Name, "Protocol name for %s", tt.name) assert.Equal(t, tt.wantType, protocol.Type, "Protocol type for %s", tt.name) } }) } } // TestGetSwapProtocol_EdgeCases tests edge cases for protocol detection func TestGetSwapProtocol_EdgeCases(t *testing.T) { tests := []struct { name string to *common.Address data []byte wantName string }{ { name: "nil address", to: nil, data: []byte{0x01, 0x02, 0x03, 0x04, 0x05}, wantName: "unknown", }, { name: "zero address", to: func() *common.Address { addr := common.HexToAddress("0x0000000000000000000000000000000000000000") return &addr }(), data: []byte{0x01, 0x02, 0x03, 0x04, 0x05}, wantName: "unknown", }, { name: "data too short", to: func() *common.Address { addr := common.HexToAddress("0x1234567890123456789012345678901234567890") return &addr }(), data: []byte{0x01, 0x02, 0x03}, wantName: "unknown", }, { name: "unknown selector", to: func() *common.Address { addr := common.HexToAddress("0x1234567890123456789012345678901234567890") return &addr }(), data: []byte{0xff, 0xff, 0xff, 0xff, 0x00}, wantName: "unknown", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { protocol := GetSwapProtocol(tt.to, tt.data) require.NotNil(t, protocol) assert.Equal(t, tt.wantName, protocol.Name, "Protocol name for %s", tt.name) }) } } // TestIsSupportedDEX tests supported DEX detection func TestIsSupportedDEX(t *testing.T) { tests := []struct { name string protocol *DEXProtocol wantSupport bool }{ { name: "UniswapV2 supported", protocol: &DEXProtocol{Name: "UniswapV2", Version: "", Type: "router"}, wantSupport: true, }, { name: "UniswapV3 supported", protocol: &DEXProtocol{Name: "UniswapV3", Version: "", Type: "router"}, wantSupport: true, }, { name: "SushiSwap supported", protocol: &DEXProtocol{Name: "SushiSwap", Version: "", Type: "router"}, wantSupport: true, }, { name: "Camelot supported", protocol: &DEXProtocol{Name: "Camelot", Version: "V3", Type: "router"}, wantSupport: true, }, { name: "Curve supported", protocol: &DEXProtocol{Name: "Curve", Version: "", Type: "pool"}, wantSupport: true, }, { name: "Balancer supported", protocol: &DEXProtocol{Name: "Balancer", Version: "V2", Type: "vault"}, wantSupport: true, }, { name: "unknown DEX not supported", protocol: &DEXProtocol{Name: "unknown", Version: "", Type: ""}, wantSupport: false, }, { name: "nil protocol not supported", protocol: nil, wantSupport: false, }, { name: "PancakeSwap not supported (not in list)", protocol: &DEXProtocol{Name: "PancakeSwap", Version: "V2", Type: "router"}, wantSupport: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := IsSupportedDEX(tt.protocol) assert.Equal(t, tt.wantSupport, got, "IsSupportedDEX() for %s", tt.name) }) } } // TestDecodeArbitrumMessage tests decoding of Arbitrum sequencer messages func TestDecodeArbitrumMessage(t *testing.T) { tests := []struct { name string msgMap map[string]interface{} wantErr bool checks func(t *testing.T, msg *ArbitrumMessage) }{ { name: "valid message with L2 transaction", msgMap: map[string]interface{}{ "sequenceNumber": float64(12345), "message": map[string]interface{}{ "message": map[string]interface{}{ "header": map[string]interface{}{ "kind": float64(3), "blockNumber": float64(100000), "timestamp": float64(1234567890), }, "l2Msg": "BAQ=", // Base64 for [4, 4] - invalid transaction but valid base64 }, }, }, wantErr: false, checks: func(t *testing.T, msg *ArbitrumMessage) { assert.Equal(t, uint64(12345), msg.SequenceNumber) assert.Equal(t, uint8(3), msg.Kind) assert.Equal(t, uint64(100000), msg.BlockNumber) assert.Equal(t, uint64(1234567890), msg.Timestamp) assert.Equal(t, "BAQ=", msg.L2MsgRaw) }, }, { name: "missing message wrapper", msgMap: map[string]interface{}{ "sequenceNumber": float64(12345), }, wantErr: true, }, { name: "missing inner message", msgMap: map[string]interface{}{ "sequenceNumber": float64(12345), "message": map[string]interface{}{}, }, wantErr: true, }, { name: "missing l2Msg", msgMap: map[string]interface{}{ "sequenceNumber": float64(12345), "message": map[string]interface{}{ "message": map[string]interface{}{ "header": map[string]interface{}{ "kind": float64(3), }, }, }, }, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { msg, err := DecodeArbitrumMessage(tt.msgMap) if tt.wantErr { assert.Error(t, err) return } require.NoError(t, err) require.NotNil(t, msg) if tt.checks != nil { tt.checks(t, msg) } }) } } // TestDecodeL2Transaction tests Base64 decoding and RLP parsing func TestDecodeL2Transaction(t *testing.T) { tests := []struct { name string l2Msg string wantErr bool errMsg string }{ { name: "empty base64", l2Msg: "", wantErr: true, errMsg: "illegal base64 data", }, { name: "invalid base64", l2Msg: "not valid base64!!!", wantErr: true, errMsg: "illegal base64 data", }, { name: "valid base64 but not signed transaction (kind 0)", l2Msg: "AAQ=", // Base64 for [0, 4] wantErr: true, errMsg: "not a signed transaction", }, { name: "valid base64, kind 4 (signed tx) but invalid RLP", l2Msg: "BAQ=", // Base64 for [4, 4] - kind 4 (signed tx) but invalid RLP wantErr: true, errMsg: "RLP decode failed", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { tx, err := DecodeL2Transaction(tt.l2Msg) if tt.wantErr { assert.Error(t, err) if tt.errMsg != "" { assert.Contains(t, err.Error(), tt.errMsg) } return } require.NoError(t, err) require.NotNil(t, tx) }) } } // TestAllSelectorsCovered ensures all selectors in IsSwapTransaction are tested func TestAllSelectorsCovered(t *testing.T) { // All selectors that should be recognized as swaps allSelectors := []string{ // UniswapV2 Router "38ed1739", "8803dbee", "7ff36ab5", "fb3bdb41", "18cbafe5", "4a25d94a", // UniswapV3 Router "414bf389", "c04b8d59", "db3e2198", "f28c0498", // UniswapV2 Pair direct "022c0d9f", // Curve "3df02124", "a6417ed6", // 1inch "7c025200", "e449022e", // 0x Protocol "d9627aa4", "415565b0", } for _, selector := range allSelectors { t.Run(selector, func(t *testing.T) { data, err := hex.DecodeString(selector + "0000000000000000000000000000000000000000") require.NoError(t, err, "Failed to decode selector %s", selector) isSwap := IsSwapTransaction(data) assert.True(t, isSwap, "Selector %s should be recognized as swap", selector) }) } }