package execution import ( "math/big" "testing" "time" "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestNewUniswapV2Encoder(t *testing.T) { encoder := NewUniswapV2Encoder() assert.NotNil(t, encoder) assert.Equal(t, UniswapV2RouterAddress, encoder.routerAddress) } func TestUniswapV2Encoder_EncodeSwap(t *testing.T) { encoder := NewUniswapV2Encoder() tokenIn := common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1") // WETH tokenOut := common.HexToAddress("0xFF970a61A04b1cA14834A43f5dE4533eBDDB5CC8") // USDC amountIn := big.NewInt(1e18) minAmountOut := big.NewInt(1500e6) poolAddress := common.HexToAddress("0x0000000000000000000000000000000000000001") recipient := common.HexToAddress("0x0000000000000000000000000000000000000002") deadline := time.Now().Add(5 * time.Minute) to, data, err := encoder.EncodeSwap( tokenIn, tokenOut, amountIn, minAmountOut, poolAddress, recipient, deadline, ) require.NoError(t, err) assert.Equal(t, encoder.routerAddress, to) assert.NotEmpty(t, data) // Check method ID (first 4 bytes) // swapExactTokensForTokens(uint256,uint256,address[],address,uint256) assert.Len(t, data, 4+5*32+32+2*32) // methodID + 5 params + array length + 2 addresses // Verify method signature expectedMethodID := []byte{0x38, 0xed, 0x17, 0x39} // swapExactTokensForTokens signature assert.Equal(t, expectedMethodID, data[:4]) } func TestUniswapV2Encoder_EncodeMultiHopSwap(t *testing.T) { encoder := NewUniswapV2Encoder() path := []common.Address{ common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"), // WETH common.HexToAddress("0xFF970a61A04b1cA14834A43f5dE4533eBDDB5CC8"), // USDC common.HexToAddress("0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f"), // WBTC } amountIn := big.NewInt(1e18) minAmountOut := big.NewInt(1e7) recipient := common.HexToAddress("0x0000000000000000000000000000000000000002") deadline := time.Now().Add(5 * time.Minute) to, data, err := encoder.EncodeMultiHopSwap( path, amountIn, minAmountOut, recipient, deadline, ) require.NoError(t, err) assert.Equal(t, encoder.routerAddress, to) assert.NotEmpty(t, data) // Verify method ID expectedMethodID := []byte{0x38, 0xed, 0x17, 0x39} assert.Equal(t, expectedMethodID, data[:4]) } func TestUniswapV2Encoder_EncodeMultiHopSwap_EmptyPath(t *testing.T) { encoder := NewUniswapV2Encoder() path := []common.Address{} amountIn := big.NewInt(1e18) minAmountOut := big.NewInt(1e7) recipient := common.HexToAddress("0x0000000000000000000000000000000000000002") deadline := time.Now().Add(5 * time.Minute) _, _, err := encoder.EncodeMultiHopSwap( path, amountIn, minAmountOut, recipient, deadline, ) assert.Error(t, err) assert.Contains(t, err.Error(), "path must contain at least 2 tokens") } func TestUniswapV2Encoder_EncodeMultiHopSwap_SingleToken(t *testing.T) { encoder := NewUniswapV2Encoder() path := []common.Address{ common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"), } amountIn := big.NewInt(1e18) minAmountOut := big.NewInt(1e7) recipient := common.HexToAddress("0x0000000000000000000000000000000000000002") deadline := time.Now().Add(5 * time.Minute) _, _, err := encoder.EncodeMultiHopSwap( path, amountIn, minAmountOut, recipient, deadline, ) assert.Error(t, err) assert.Contains(t, err.Error(), "path must contain at least 2 tokens") } func TestUniswapV2Encoder_EncodeExactOutput(t *testing.T) { encoder := NewUniswapV2Encoder() tokenIn := common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1") tokenOut := common.HexToAddress("0xFF970a61A04b1cA14834A43f5dE4533eBDDB5CC8") amountOut := big.NewInt(1500e6) maxAmountIn := big.NewInt(2e18) recipient := common.HexToAddress("0x0000000000000000000000000000000000000002") deadline := time.Now().Add(5 * time.Minute) to, data, err := encoder.EncodeExactOutput( tokenIn, tokenOut, amountOut, maxAmountIn, recipient, deadline, ) require.NoError(t, err) assert.Equal(t, encoder.routerAddress, to) assert.NotEmpty(t, data) // Verify method ID for swapTokensForExactTokens assert.Len(t, data, 4+5*32+32+2*32) } func TestUniswapV2Encoder_ZeroAddresses(t *testing.T) { encoder := NewUniswapV2Encoder() tokenIn := common.Address{} tokenOut := common.Address{} amountIn := big.NewInt(1e18) minAmountOut := big.NewInt(1500e6) poolAddress := common.Address{} recipient := common.Address{} deadline := time.Now().Add(5 * time.Minute) // Should not error with zero addresses (validation done elsewhere) to, data, err := encoder.EncodeSwap( tokenIn, tokenOut, amountIn, minAmountOut, poolAddress, recipient, deadline, ) require.NoError(t, err) assert.NotEmpty(t, to) assert.NotEmpty(t, data) } func TestUniswapV2Encoder_ZeroAmounts(t *testing.T) { encoder := NewUniswapV2Encoder() tokenIn := common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1") tokenOut := common.HexToAddress("0xFF970a61A04b1cA14834A43f5dE4533eBDDB5CC8") amountIn := big.NewInt(0) minAmountOut := big.NewInt(0) poolAddress := common.HexToAddress("0x0000000000000000000000000000000000000001") recipient := common.HexToAddress("0x0000000000000000000000000000000000000002") deadline := time.Now().Add(5 * time.Minute) // Should not error with zero amounts (validation done elsewhere) to, data, err := encoder.EncodeSwap( tokenIn, tokenOut, amountIn, minAmountOut, poolAddress, recipient, deadline, ) require.NoError(t, err) assert.NotEmpty(t, to) assert.NotEmpty(t, data) } func TestUniswapV2Encoder_LargeAmounts(t *testing.T) { encoder := NewUniswapV2Encoder() tokenIn := common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1") tokenOut := common.HexToAddress("0xFF970a61A04b1cA14834A43f5dE4533eBDDB5CC8") // Max uint256 amountIn := new(big.Int) amountIn.SetString("115792089237316195423570985008687907853269984665640564039457584007913129639935", 10) minAmountOut := new(big.Int) minAmountOut.SetString("115792089237316195423570985008687907853269984665640564039457584007913129639935", 10) poolAddress := common.HexToAddress("0x0000000000000000000000000000000000000001") recipient := common.HexToAddress("0x0000000000000000000000000000000000000002") deadline := time.Now().Add(5 * time.Minute) to, data, err := encoder.EncodeSwap( tokenIn, tokenOut, amountIn, minAmountOut, poolAddress, recipient, deadline, ) require.NoError(t, err) assert.NotEmpty(t, to) assert.NotEmpty(t, data) } func TestUniswapV2Encoder_PastDeadline(t *testing.T) { encoder := NewUniswapV2Encoder() tokenIn := common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1") tokenOut := common.HexToAddress("0xFF970a61A04b1cA14834A43f5dE4533eBDDB5CC8") amountIn := big.NewInt(1e18) minAmountOut := big.NewInt(1500e6) poolAddress := common.HexToAddress("0x0000000000000000000000000000000000000001") recipient := common.HexToAddress("0x0000000000000000000000000000000000000002") deadline := time.Now().Add(-5 * time.Minute) // Past deadline // Should not error (validation done on-chain) to, data, err := encoder.EncodeSwap( tokenIn, tokenOut, amountIn, minAmountOut, poolAddress, recipient, deadline, ) require.NoError(t, err) assert.NotEmpty(t, to) assert.NotEmpty(t, data) } func TestPadLeft(t *testing.T) { tests := []struct { name string input []byte length int expected int }{ { name: "Empty input", input: []byte{}, length: 32, expected: 32, }, { name: "Small number", input: []byte{0x01}, length: 32, expected: 32, }, { name: "Full size", input: make([]byte, 32), length: 32, expected: 32, }, { name: "Address", input: make([]byte, 20), length: 32, expected: 32, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := padLeft(tt.input, tt.length) assert.Len(t, result, tt.expected) }) } }