319 lines
9.8 KiB
Go
319 lines
9.8 KiB
Go
package integration_test
|
|
|
|
import (
|
|
"context"
|
|
"math/big"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/core/types"
|
|
"github.com/ethereum/go-ethereum/crypto"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"mev-bot/bindings/arbitrage"
|
|
"mev-bot/pkg/arbitrage"
|
|
"mev-bot/pkg/security"
|
|
)
|
|
|
|
func TestContractDeploymentOnForkedArbitrum(t *testing.T) {
|
|
// Setup forked Arbitrum environment
|
|
client, cleanup := setupForkedArbitrum(t)
|
|
defer cleanup()
|
|
|
|
// Create a test private key for deployment
|
|
privateKey, err := crypto.GenerateKey()
|
|
require.NoError(t, err)
|
|
|
|
auth, err := bind.NewKeyedTransactorWithChainID(privateKey, big.NewInt(42161))
|
|
require.NoError(t, err)
|
|
|
|
// Set gas price for Arbitrum
|
|
gasPrice, err := client.SuggestGasPrice(context.Background())
|
|
require.NoError(t, err)
|
|
auth.GasPrice = gasPrice
|
|
auth.GasLimit = uint64(5000000)
|
|
|
|
t.Run("Deploy ArbitrageExecutor Contract", func(t *testing.T) {
|
|
// Deploy the ArbitrageExecutor contract
|
|
address, tx, contract, err := arbitrage.DeployArbitrageExecutor(
|
|
auth,
|
|
client,
|
|
common.HexToAddress("0x1F98431c8aD98523631AE4a59f267346ea31F984"), // Uniswap V3 Factory
|
|
common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"), // WETH
|
|
)
|
|
require.NoError(t, err)
|
|
require.NotEqual(t, common.Address{}, address)
|
|
|
|
// Wait for deployment confirmation
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
defer cancel()
|
|
|
|
receipt, err := bind.WaitMined(ctx, client, tx)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, types.ReceiptStatusSuccessful, receipt.Status)
|
|
|
|
// Verify contract is deployed correctly
|
|
code, err := client.CodeAt(context.Background(), address, nil)
|
|
require.NoError(t, err)
|
|
assert.Greater(t, len(code), 0, "Contract should have bytecode")
|
|
|
|
// Test contract initialization
|
|
owner, err := contract.Owner(nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, auth.From, owner)
|
|
|
|
// Test setting minimum profit threshold
|
|
newThreshold := big.NewInt(1000000000000000000) // 1 ETH
|
|
tx, err = contract.SetMinProfitThreshold(auth, newThreshold)
|
|
require.NoError(t, err)
|
|
|
|
receipt, err = bind.WaitMined(ctx, client, tx)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, types.ReceiptStatusSuccessful, receipt.Status)
|
|
|
|
threshold, err := contract.MinProfitThreshold(nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, newThreshold, threshold)
|
|
})
|
|
|
|
t.Run("Test Contract Security Features", func(t *testing.T) {
|
|
// Deploy with security features enabled
|
|
address, tx, contract, err := arbitrage.DeployArbitrageExecutor(
|
|
auth,
|
|
client,
|
|
common.HexToAddress("0x1F98431c8aD98523631AE4a59f267346ea31F984"),
|
|
common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"),
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
defer cancel()
|
|
|
|
receipt, err := bind.WaitMined(ctx, client, tx)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, types.ReceiptStatusSuccessful, receipt.Status)
|
|
|
|
// Test emergency pause functionality
|
|
tx, err = contract.Pause(auth)
|
|
require.NoError(t, err)
|
|
|
|
receipt, err = bind.WaitMined(ctx, client, tx)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, types.ReceiptStatusSuccessful, receipt.Status)
|
|
|
|
paused, err := contract.Paused(nil)
|
|
require.NoError(t, err)
|
|
assert.True(t, paused)
|
|
|
|
// Test unpause
|
|
tx, err = contract.Unpause(auth)
|
|
require.NoError(t, err)
|
|
|
|
receipt, err = bind.WaitMined(ctx, client, tx)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, types.ReceiptStatusSuccessful, receipt.Status)
|
|
})
|
|
|
|
t.Run("Test Gas Limit Validation", func(t *testing.T) {
|
|
// Test deployment with insufficient gas
|
|
lowGasAuth := *auth
|
|
lowGasAuth.GasLimit = uint64(100000) // Too low for contract deployment
|
|
|
|
_, _, _, err := arbitrage.DeployArbitrageExecutor(
|
|
&lowGasAuth,
|
|
client,
|
|
common.HexToAddress("0x1F98431c8aD98523631AE4a59f267346ea31F984"),
|
|
common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"),
|
|
)
|
|
assert.Error(t, err, "Should fail with insufficient gas")
|
|
})
|
|
}
|
|
|
|
func TestContractInteractionWithRealPools(t *testing.T) {
|
|
client, cleanup := setupForkedArbitrum(t)
|
|
defer cleanup()
|
|
|
|
// Use real Arbitrum pool addresses for testing
|
|
wethUsdcPool := common.HexToAddress("0xC31E54c7a869B9FcBEcc14363CF510d1c41fa443") // WETH/USDC 0.05%
|
|
|
|
privateKey, err := crypto.GenerateKey()
|
|
require.NoError(t, err)
|
|
|
|
auth, err := bind.NewKeyedTransactorWithChainID(privateKey, big.NewInt(42161))
|
|
require.NoError(t, err)
|
|
|
|
// Deploy contract
|
|
_, tx, contract, err := arbitrage.DeployArbitrageExecutor(
|
|
auth,
|
|
client,
|
|
common.HexToAddress("0x1F98431c8aD98523631AE4a59f267346ea31F984"),
|
|
common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"),
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
defer cancel()
|
|
|
|
receipt, err := bind.WaitMined(ctx, client, tx)
|
|
require.NoError(t, err)
|
|
require.Equal(t, types.ReceiptStatusSuccessful, receipt.Status)
|
|
|
|
t.Run("Test Pool State Reading", func(t *testing.T) {
|
|
// Test reading pool state through contract
|
|
poolState, err := contract.GetPoolState(nil, wethUsdcPool)
|
|
require.NoError(t, err)
|
|
|
|
assert.Greater(t, poolState.SqrtPriceX96.Uint64(), uint64(0))
|
|
assert.Greater(t, poolState.Liquidity.Uint64(), uint64(0))
|
|
assert.NotEqual(t, int32(0), poolState.Tick)
|
|
})
|
|
|
|
t.Run("Test Price Impact Calculation", func(t *testing.T) {
|
|
swapAmount := big.NewInt(1000000) // 1 USDC
|
|
|
|
priceImpact, err := contract.CalculatePriceImpact(nil, wethUsdcPool, swapAmount, true)
|
|
require.NoError(t, err)
|
|
|
|
// Price impact should be reasonable for small swaps
|
|
assert.LessOrEqual(t, priceImpact.Uint64(), uint64(10000)) // Less than 1% (10000 basis points)
|
|
})
|
|
|
|
t.Run("Test Arbitrage Opportunity Detection", func(t *testing.T) {
|
|
// Simulate a price difference scenario
|
|
pool1 := wethUsdcPool
|
|
pool2 := common.HexToAddress("0x17c14D2c404D167802b16C450d3c99F88F2c4F4d") // Alternative WETH/USDC pool
|
|
|
|
opportunity, err := contract.DetectArbitrageOpportunity(nil, pool1, pool2, big.NewInt(1000000))
|
|
require.NoError(t, err)
|
|
|
|
// Log the detected opportunity for analysis
|
|
t.Logf("Detected opportunity: profitable=%v, estimated_profit=%v",
|
|
opportunity.Profitable, opportunity.EstimatedProfit)
|
|
})
|
|
}
|
|
|
|
func TestContractUpgradeability(t *testing.T) {
|
|
client, cleanup := setupForkedArbitrum(t)
|
|
defer cleanup()
|
|
|
|
privateKey, err := crypto.GenerateKey()
|
|
require.NoError(t, err)
|
|
|
|
auth, err := bind.NewKeyedTransactorWithChainID(privateKey, big.NewInt(42161))
|
|
require.NoError(t, err)
|
|
|
|
t.Run("Test Contract Version Management", func(t *testing.T) {
|
|
// Deploy initial version
|
|
address, tx, contract, err := arbitrage.DeployArbitrageExecutor(
|
|
auth,
|
|
client,
|
|
common.HexToAddress("0x1F98431c8aD98523631AE4a59f267346ea31F984"),
|
|
common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"),
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
defer cancel()
|
|
|
|
receipt, err := bind.WaitMined(ctx, client, tx)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, types.ReceiptStatusSuccessful, receipt.Status)
|
|
|
|
// Check initial version
|
|
version, err := contract.Version(nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "1.0.0", version)
|
|
|
|
// Test configuration updates
|
|
newMaxGasPrice := big.NewInt(50000000000) // 50 gwei
|
|
tx, err = contract.SetMaxGasPrice(auth, newMaxGasPrice)
|
|
require.NoError(t, err)
|
|
|
|
receipt, err = bind.WaitMined(ctx, client, tx)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, types.ReceiptStatusSuccessful, receipt.Status)
|
|
|
|
maxGasPrice, err := contract.MaxGasPrice(nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, newMaxGasPrice, maxGasPrice)
|
|
})
|
|
}
|
|
|
|
func TestContractWithSecurityManager(t *testing.T) {
|
|
client, cleanup := setupForkedArbitrum(t)
|
|
defer cleanup()
|
|
|
|
// Initialize security manager
|
|
keyManager := security.NewKeyManager()
|
|
err := keyManager.Initialize([]byte("test-encryption-key-32-bytes-long"))
|
|
require.NoError(t, err)
|
|
|
|
// Generate and store a test key
|
|
privateKey, err := crypto.GenerateKey()
|
|
require.NoError(t, err)
|
|
|
|
err = keyManager.StoreKey("test-key", privateKey)
|
|
require.NoError(t, err)
|
|
|
|
err = keyManager.SetActiveKey("test-key")
|
|
require.NoError(t, err)
|
|
|
|
// Get the active key for contract deployment
|
|
activeKey, err := keyManager.GetActivePrivateKey()
|
|
require.NoError(t, err)
|
|
|
|
auth, err := bind.NewKeyedTransactorWithChainID(activeKey, big.NewInt(42161))
|
|
require.NoError(t, err)
|
|
|
|
t.Run("Deploy With Secure Key Management", func(t *testing.T) {
|
|
address, tx, contract, err := arbitrage.DeployArbitrageExecutor(
|
|
auth,
|
|
client,
|
|
common.HexToAddress("0x1F98431c8aD98523631AE4a59f267346ea31F984"),
|
|
common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"),
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
defer cancel()
|
|
|
|
receipt, err := bind.WaitMined(ctx, client, tx)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, types.ReceiptStatusSuccessful, receipt.Status)
|
|
|
|
// Verify the contract owner matches our secure key
|
|
owner, err := contract.Owner(nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, auth.From, owner)
|
|
|
|
// Test secure transaction signing
|
|
tx, err = contract.SetMinProfitThreshold(auth, big.NewInt(500000000000000000))
|
|
require.NoError(t, err)
|
|
|
|
receipt, err = bind.WaitMined(ctx, client, tx)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, types.ReceiptStatusSuccessful, receipt.Status)
|
|
})
|
|
|
|
t.Run("Test Key Rotation", func(t *testing.T) {
|
|
// Generate a new key
|
|
newPrivateKey, err := crypto.GenerateKey()
|
|
require.NoError(t, err)
|
|
|
|
err = keyManager.StoreKey("new-key", newPrivateKey)
|
|
require.NoError(t, err)
|
|
|
|
// Rotate to the new key
|
|
err = keyManager.SetActiveKey("new-key")
|
|
require.NoError(t, err)
|
|
|
|
// Verify the new key is active
|
|
currentKey, err := keyManager.GetActivePrivateKey()
|
|
require.NoError(t, err)
|
|
assert.Equal(t, newPrivateKey, currentKey)
|
|
})
|
|
}
|