Some checks failed
V2 CI/CD Pipeline / Pre-Flight Checks (push) Has been cancelled
V2 CI/CD Pipeline / Build & Dependencies (push) Has been cancelled
V2 CI/CD Pipeline / Code Quality & Linting (push) Has been cancelled
V2 CI/CD Pipeline / Unit Tests (100% Coverage Required) (push) Has been cancelled
V2 CI/CD Pipeline / Integration Tests (push) Has been cancelled
V2 CI/CD Pipeline / Performance Benchmarks (push) Has been cancelled
V2 CI/CD Pipeline / Decimal Precision Validation (push) Has been cancelled
V2 CI/CD Pipeline / Modularity Validation (push) Has been cancelled
V2 CI/CD Pipeline / Final Validation Summary (push) Has been cancelled
Add comprehensive unit tests for all execution engine components: Component Test Coverage: - UniswapV2 encoder: 15 test cases + benchmarks - UniswapV3 encoder: 20 test cases + benchmarks - Curve encoder: 16 test cases + benchmarks - Flashloan manager: 18 test cases + benchmarks - Transaction builder: 15 test cases + benchmarks - Risk manager: 25 test cases + benchmarks - Executor: 20 test cases + benchmarks Test Categories: - Happy path scenarios - Error handling and edge cases - Zero/invalid inputs - Boundary conditions (max amounts, limits) - Concurrent operations (nonce management) - Configuration validation - State management Key Test Features: - Protocol-specific encoding validation - ABI encoding correctness - Gas calculation accuracy - Slippage calculation - Nonce management thread safety - Circuit breaker behavior - Risk assessment rules - Transaction lifecycle Total: 129 test cases + performance benchmarks Target: 100% test coverage for execution engine Related to Phase 4 (Execution Engine) implementation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
306 lines
7.9 KiB
Go
306 lines
7.9 KiB
Go
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)
|
|
})
|
|
}
|
|
}
|