refactor: move all remaining files to orig/ directory

Completed clean root directory structure:
- Root now contains only: .git, .env, docs/, orig/
- Moved all remaining files and directories to orig/:
  - Config files (.claude, .dockerignore, .drone.yml, etc.)
  - All .env variants (except active .env)
  - Git config (.gitconfig, .github, .gitignore, etc.)
  - Tool configs (.golangci.yml, .revive.toml, etc.)
  - Documentation (*.md files, @prompts)
  - Build files (Dockerfiles, Makefile, go.mod, go.sum)
  - Docker compose files
  - All source directories (scripts, tests, tools, etc.)
  - Runtime directories (logs, monitoring, reports)
  - Dependency files (node_modules, lib, cache)
  - Special files (--delete)

- Removed empty runtime directories (bin/, data/)

V2 structure is now clean:
- docs/planning/ - V2 planning documents
- orig/ - Complete V1 codebase preserved
- .env - Active environment config (not in git)

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Administrator
2025-11-10 10:53:05 +01:00
parent 803de231ba
commit c54c569f30
718 changed files with 8304 additions and 8281 deletions

View File

@@ -0,0 +1,199 @@
//go:build integration && legacy && forked
// +build integration,legacy,forked
package integration_test
import (
"math/big"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/fraktal/mev-beta/internal/logger"
"github.com/fraktal/mev-beta/pkg/arbitrum"
"github.com/fraktal/mev-beta/test/mocks"
)
// TestL2MessageParsingAccuracy tests the accuracy of L2 message parsing
func TestL2MessageParsingAccuracy(t *testing.T) {
log := logger.New("info", "text", "")
parser := arbitrum.NewL2MessageParser(log)
testCases := []struct {
name string
protocol string
expectedTokens []common.Address
expectedFee uint32
}{
{
name: "UniswapV3_USDC_WETH",
protocol: "UniswapV3",
expectedTokens: []common.Address{
common.HexToAddress("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"), // USDC
common.HexToAddress("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"), // WETH
},
expectedFee: 3000,
},
{
name: "SushiSwap_USDC_WETH",
protocol: "SushiSwap",
expectedTokens: []common.Address{
common.HexToAddress("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"), // USDC
common.HexToAddress("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"), // WETH
},
expectedFee: 3000,
},
{
name: "Camelot_ARB_WETH",
protocol: "Camelot",
expectedTokens: []common.Address{
common.HexToAddress("0x912CE59144191C1204E64559FE8253a0e49E6548"), // ARB
common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"), // WETH on Arbitrum
},
expectedFee: 3000,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
// Create mock transaction with DEX interaction
poolAddress := common.HexToAddress("0xE592427A0AEce92De3Edee1F18E0157C05861564") // Uniswap V3 Router
// Create mock transaction data for swap
swapData := createMockSwapData(tc.expectedTokens[0], tc.expectedTokens[1], tc.expectedFee)
tx := mocks.CreateMockTransaction(poolAddress, swapData)
// Parse DEX interaction
interaction, err := parser.ParseDEXInteraction(tx)
if tc.protocol == "UniswapV3" {
// UniswapV3 should be successfully parsed
require.NoError(t, err)
require.NotNil(t, interaction)
assert.Equal(t, tc.protocol, interaction.Protocol)
// Note: Fee field not available in current DEXInteraction struct
assert.Equal(t, tc.protocol, interaction.Protocol)
} else {
// Other protocols might not be implemented yet, so we allow nil results
if interaction != nil {
assert.Equal(t, tc.protocol, interaction.Protocol)
}
}
})
}
}
// TestL2MessageLatency tests the latency of L2 message processing
func TestL2MessageLatency(t *testing.T) {
log := logger.New("info", "text", "")
parser := arbitrum.NewL2MessageParser(log)
const numMessages = 100
const maxLatencyMs = 10 // Maximum acceptable latency in milliseconds
for i := 0; i < numMessages; i++ {
// Create L2 message
l2Message := mocks.CreateMockL2Message()
// Measure parsing time
startTime := time.Now()
if l2Message.ParsedTx != nil {
_, err := parser.ParseDEXInteraction(l2Message.ParsedTx)
// Error is expected for mock data, just measure timing
_ = err
}
latency := time.Since(startTime)
latencyMs := latency.Nanoseconds() / 1000000
// Verify latency is acceptable
assert.LessOrEqual(t, latencyMs, int64(maxLatencyMs),
"L2 message processing latency too high: %dms", latencyMs)
}
}
// TestMultiProtocolDetection tests detection of multiple DEX protocols
func TestMultiProtocolDetection(t *testing.T) {
log := logger.New("info", "text", "")
parser := arbitrum.NewL2MessageParser(log)
protocols := []string{"UniswapV3", "SushiSwap", "Camelot", "Balancer", "Curve"}
for _, protocol := range protocols {
t.Run(protocol, func(t *testing.T) {
// Create mock transaction for each protocol
poolAddress := getProtocolPoolAddress(protocol)
swapData := createMockSwapDataForProtocol(protocol)
tx := mocks.CreateMockTransaction(poolAddress, swapData)
// Parse DEX interaction
interaction, err := parser.ParseDEXInteraction(tx)
// For UniswapV3, we expect successful parsing
// For others, we may not have full implementation yet
if protocol == "UniswapV3" {
require.NoError(t, err)
require.NotNil(t, interaction)
assert.Equal(t, protocol, interaction.Protocol)
} else {
// Log the results for other protocols
if err != nil {
t.Logf("Protocol %s not fully implemented yet: %v", protocol, err)
} else if interaction != nil {
t.Logf("Protocol %s detected: %+v", protocol, interaction)
} else {
t.Logf("Protocol %s: no interaction detected (expected for mock data)", protocol)
}
}
})
}
}
// Helper functions for test data creation
func createMockSwapData(token0, token1 common.Address, fee uint32) []byte {
// exactInputSingle selector: 0x414bf389
selector := []byte{0x41, 0x4b, 0xf3, 0x89}
// Create a mock payload for exactInputSingle
payload := make([]byte, 256)
// tokenIn (address)
copy(payload[12:32], token0.Bytes())
// tokenOut (address)
copy(payload[44:64], token1.Bytes())
// amountIn (uint256)
amountIn := new(big.Int).SetInt64(1000000000000000000) // 1 ETH
amountInBytes := amountIn.Bytes()
copy(payload[192-len(amountInBytes):192], amountInBytes)
return append(selector, payload...)
}
func createMockSwapDataForProtocol(protocol string) []byte {
// For testing, we'll just use the same mock data for all protocols.
// In a real scenario, this would generate protocol-specific data.
token0 := common.HexToAddress("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48") // USDC
token1 := common.HexToAddress("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2") // WETH
return createMockSwapData(token0, token1, 3000)
}
func getProtocolPoolAddress(protocol string) common.Address {
// Return known pool addresses for different protocols on Arbitrum
protocolPools := map[string]string{
"UniswapV3": "0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640",
"SushiSwap": "0x905dfCD5649217c42684f23958568e533C711Aa3",
"Camelot": "0x84652bb2539513BAf36e225c930Fdd8eaa63CE27",
"Balancer": "0x32dF62dc3aEd2cD6224193052Ce665DC18165841",
"Curve": "0x7f90122BF0700F9E7e1F688fe926940E8839F353",
}
if addr, exists := protocolPools[protocol]; exists {
return common.HexToAddress(addr)
}
return common.HexToAddress("0x0000000000000000000000000000000000000000")
}

View File

@@ -0,0 +1,321 @@
//go:build integration && legacy && forked
// +build integration,legacy,forked
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"
"github.com/fraktal/mev-beta/bindings/arbitrage"
"github.com/fraktal/mev-beta/pkg/arbitrage"
"github.com/fraktal/mev-beta/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)
})
}

View File

@@ -0,0 +1,513 @@
package integration
import (
"context"
"encoding/hex"
"fmt"
"sync"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/assert"
"github.com/fraktal/mev-beta/internal/logger"
"github.com/fraktal/mev-beta/internal/monitoring"
"github.com/fraktal/mev-beta/internal/recovery"
"github.com/fraktal/mev-beta/internal/utils"
"github.com/fraktal/mev-beta/internal/validation"
)
// CorruptionSimulator simulates various corruption scenarios for testing
type CorruptionSimulator struct {
validator *validation.AddressValidator
converter *utils.SafeAddressConverter
integrityMonitor *monitoring.IntegrityMonitor
errorHandler *recovery.ErrorHandler
retryHandler *recovery.RetryHandler
logger *logger.Logger
}
// NewCorruptionSimulator creates a new corruption simulation test environment
func NewCorruptionSimulator(t *testing.T) *CorruptionSimulator {
log := logger.New("debug", "text", "")
validator := validation.NewAddressValidator()
converter := utils.NewSafeAddressConverter()
integrityMonitor := monitoring.NewIntegrityMonitor(log)
errorHandler := recovery.NewErrorHandler(log)
retryHandler := recovery.NewRetryHandler(log)
return &CorruptionSimulator{
validator: validator,
converter: converter,
integrityMonitor: integrityMonitor,
errorHandler: errorHandler,
retryHandler: retryHandler,
logger: log,
}
}
// CorruptionScenario represents a specific corruption test case
type CorruptionScenario struct {
Name string
CorruptedAddresses []string
ValidAddresses []string
ExpectedDetections int
ExpectedRecoveries int
Severity recovery.ErrorSeverity
}
func TestCorruption_TOKEN_0x000000_Scenarios(t *testing.T) {
simulator := NewCorruptionSimulator(t)
scenarios := []CorruptionScenario{
{
Name: "Critical TOKEN_0x000000 Pattern",
CorruptedAddresses: []string{
"0x0000000300000000000000000000000000000000", // Exact pattern
"0x0000000100000000000000000000000000000000", // Similar pattern
"0x0000000500000000000000000000000000000000", // Variant
},
ValidAddresses: []string{
"0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", // WETH
"0xaf88d065e77c8cC2239327C5EDb3A432268e5831", // USDC
},
ExpectedDetections: 3,
Severity: recovery.SeverityCritical,
},
{
Name: "Mixed Corruption Patterns",
CorruptedAddresses: []string{
"0x1234000000000000000000000000000000000000", // Partial corruption
"0x0000000000000000000000000000000000000001", // Almost zero
"0xabcd567800000000000000000000000000000000", // Trailing zeros
},
ValidAddresses: []string{
"0x912CE59144191C1204E64559FE8253a0e49E6548", // ARB
"0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f", // WBTC
},
ExpectedDetections: 3,
Severity: recovery.SeverityHigh,
},
{
Name: "Subtle Corruption",
CorruptedAddresses: []string{
"0x82aF49447D8a07e3bd95BD0d56f35241523fBaZ1", // Invalid hex char
"0x82af49447d8a07e3bd95bd0d56f35241523fbab", // Truncated address
},
ValidAddresses: []string{
"0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9", // USDT
},
ExpectedDetections: 2,
Severity: recovery.SeverityMedium,
},
}
for _, scenario := range scenarios {
t.Run(scenario.Name, func(t *testing.T) {
simulator.runCorruptionScenario(t, scenario)
})
}
}
func (cs *CorruptionSimulator) runCorruptionScenario(t *testing.T, scenario CorruptionScenario) {
// Reset monitoring stats
cs.integrityMonitor = monitoring.NewIntegrityMonitor(cs.logger)
detectedCorruptions := 0
successfulRecoveries := 0
// Process corrupted addresses
for _, corruptedAddr := range scenario.CorruptedAddresses {
cs.logger.Debug("Processing corrupted address", "address", corruptedAddr)
// Validate address
result := cs.validator.ValidateAddress(corruptedAddr)
cs.integrityMonitor.RecordAddressProcessed()
if !result.IsValid {
detectedCorruptions++
addr := common.HexToAddress(corruptedAddr)
cs.integrityMonitor.RecordCorruptionDetected(addr, result.CorruptionScore, "test_scenario")
cs.integrityMonitor.RecordValidationResult(false)
// Simulate recovery attempt
recoveryAction := cs.errorHandler.HandleError(
context.Background(),
recovery.ErrorTypeAddressCorruption,
scenario.Severity,
"test_component",
addr,
"Simulated corruption",
map[string]interface{}{"corruption_score": result.CorruptionScore},
)
cs.integrityMonitor.RecordRecoveryAction(recoveryAction)
// Count successful recoveries
if recoveryAction == recovery.ActionUseFallbackData ||
recoveryAction == recovery.ActionRetryWithBackoff {
successfulRecoveries++
}
} else {
cs.integrityMonitor.RecordValidationResult(true)
}
}
// Process valid addresses
for _, validAddr := range scenario.ValidAddresses {
cs.logger.Debug("Processing valid address", "address", validAddr)
result := cs.validator.ValidateAddress(validAddr)
cs.integrityMonitor.RecordAddressProcessed()
if result.IsValid {
cs.integrityMonitor.RecordValidationResult(true)
} else {
t.Errorf("Valid address %s was rejected: %v", validAddr, result.ErrorMessages)
}
}
// Verify results
assert.Equal(t, scenario.ExpectedDetections, detectedCorruptions,
"Should detect expected number of corruptions")
metrics := cs.integrityMonitor.GetMetrics()
assert.Equal(t, int64(detectedCorruptions), metrics.CorruptAddressesDetected,
"Monitoring should track detected corruptions")
totalAddresses := len(scenario.CorruptedAddresses) + len(scenario.ValidAddresses)
assert.Equal(t, int64(totalAddresses), metrics.TotalAddressesProcessed,
"Should track all processed addresses")
// Health score should reflect corruption level
expectedHealthScore := 1.0 - (float64(detectedCorruptions) / float64(totalAddresses))
assert.InDelta(t, expectedHealthScore, metrics.HealthScore, 0.2,
"Health score should reflect corruption rate")
t.Logf("Scenario %s: Detected %d/%d corruptions, Health=%.3f",
scenario.Name, detectedCorruptions, scenario.ExpectedDetections, metrics.HighScore)
}
func TestCorruption_HighVolumeStressTest(t *testing.T) {
if testing.Short() {
t.Skip("Skipping stress test in short mode")
}
simulator := NewCorruptionSimulator(t)
const (
numWorkers = 20
addressesPerWorker = 1000
corruptionRate = 0.1 // 10% corruption rate
)
var wg sync.WaitGroup
startTime := time.Now()
// Launch worker goroutines
for workerID := 0; workerID < numWorkers; workerID++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
for i := 0; i < addressesPerWorker; i++ {
var address string
// Generate corrupted or valid address based on corruption rate
if float64(i%100) < corruptionRate*100 {
// Generate corrupted address
address = generateCorruptedAddress(id, i)
} else {
// Generate valid-looking address
address = generateValidAddress(id, i)
}
// Process address through validation pipeline
result := simulator.validator.ValidateAddress(address)
simulator.integrityMonitor.RecordAddressProcessed()
if result.IsValid {
simulator.integrityMonitor.RecordValidationResult(true)
} else {
addr := common.HexToAddress(address)
simulator.integrityMonitor.RecordCorruptionDetected(addr, result.CorruptionScore, "stress_test")
simulator.integrityMonitor.RecordValidationResult(false)
// Simulate recovery
recoveryAction := simulator.errorHandler.HandleError(
context.Background(),
recovery.ErrorTypeAddressCorruption,
recovery.SeverityMedium,
"stress_test",
addr,
"Stress test corruption",
nil,
)
simulator.integrityMonitor.RecordRecoveryAction(recoveryAction)
}
}
}(workerID)
}
// Wait for completion
wg.Wait()
duration := time.Since(startTime)
// Analyze results
metrics := simulator.integrityMonitor.GetMetrics()
totalAddresses := int64(numWorkers * addressesPerWorker)
actualCorruptionRate := float64(metrics.CorruptAddressesDetected) / float64(totalAddresses)
t.Logf("Stress Test Results:")
t.Logf(" Duration: %v", duration)
t.Logf(" Total Addresses: %d", totalAddresses)
t.Logf(" Corruptions Detected: %d", metrics.CorruptAddressesDetected)
t.Logf(" Actual Corruption Rate: %.2f%%", actualCorruptionRate*100)
t.Logf(" Health Score: %.3f", metrics.HealthScore)
t.Logf(" Throughput: %.0f addresses/sec", float64(totalAddresses)/duration.Seconds())
// Verify performance and accuracy
assert.Equal(t, totalAddresses, metrics.TotalAddressesProcessed)
assert.InDelta(t, corruptionRate, actualCorruptionRate, 0.05) // Within 5% of expected
assert.Greater(t, metrics.RetryOperationsTriggered, int64(0)) // Should have triggered recoveries
// Should process at least 1000 addresses per second
throughput := float64(totalAddresses) / duration.Seconds()
assert.Greater(t, throughput, 1000.0, "Should maintain high throughput under stress")
}
func TestCorruption_RecoveryMechanisms(t *testing.T) {
simulator := NewCorruptionSimulator(t)
testCases := []struct {
name string
corruptedAddr string
errorType recovery.ErrorType
severity recovery.ErrorSeverity
expectedAction recovery.RecoveryAction
}{
{
name: "Critical corruption requires fallback",
corruptedAddr: "0x0000000300000000000000000000000000000000",
errorType: recovery.ErrorTypeAddressCorruption,
severity: recovery.SeverityCritical,
expectedAction: recovery.ActionUseFallbackData,
},
{
name: "Medium corruption allows retry",
corruptedAddr: "0x123400000000000000000000000000000000000",
errorType: recovery.ErrorTypeValidationFailed,
severity: recovery.SeverityMedium,
expectedAction: recovery.ActionRetryWithBackoff,
},
{
name: "Low corruption can be skipped",
corruptedAddr: "0x82af49447d8a07e3bd95bd0d56f35241523fbab1", // Wrong checksum
errorType: recovery.ErrorTypeValidationFailed,
severity: recovery.SeverityLow,
expectedAction: recovery.ActionSkipAndContinue,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
addr := common.HexToAddress(tc.corruptedAddr)
action := simulator.errorHandler.HandleError(
context.Background(),
tc.errorType,
tc.severity,
"test_recovery",
addr,
"Recovery test",
nil,
)
assert.Equal(t, tc.expectedAction, action,
"Should select appropriate recovery action for %s", tc.name)
// Record action and verify it's tracked
simulator.integrityMonitor.RecordRecoveryAction(action)
metrics := simulator.integrityMonitor.GetMetrics()
assert.Greater(t, metrics.RecoveryActions[action], int64(0),
"Should track recovery action usage")
})
}
}
func TestCorruption_RetryMechanisms(t *testing.T) {
simulator := NewCorruptionSimulator(t)
// Configure retry for testing
retryConfig := recovery.RetryConfig{
MaxAttempts: 3,
InitialDelay: 1 * time.Millisecond,
MaxDelay: 10 * time.Millisecond,
BackoffFactor: 2.0,
JitterEnabled: false,
TimeoutPerAttempt: 100 * time.Millisecond,
}
simulator.retryHandler.SetConfig("test_retry", retryConfig)
t.Run("Successful retry after corruption", func(t *testing.T) {
attempts := 0
operation := func(ctx context.Context, attempt int) error {
attempts++
if attempt == 1 {
// Simulate corruption on first attempt
return fmt.Errorf("address corruption detected")
}
// Success on retry
return nil
}
result := simulator.retryHandler.ExecuteWithRetry(context.Background(), "test_retry", operation)
assert.True(t, result.Success)
assert.Equal(t, 2, result.Attempts)
assert.Equal(t, 2, attempts)
})
t.Run("Persistent corruption fails after max attempts", func(t *testing.T) {
attempts := 0
operation := func(ctx context.Context, attempt int) error {
attempts++
return fmt.Errorf("persistent corruption")
}
result := simulator.retryHandler.ExecuteWithRetry(context.Background(), "test_retry", operation)
assert.False(t, result.Success)
assert.Equal(t, 3, result.Attempts) // Max attempts
assert.Equal(t, 3, attempts)
})
}
func TestCorruption_EndToEndPipeline(t *testing.T) {
simulator := NewCorruptionSimulator(t)
// Simulate complete transaction processing pipeline
testTransactions := []struct {
txHash string
inputData string
addresses []string
shouldFail bool
}{
{
txHash: "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
inputData: "0x022c0d9f000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000",
addresses: []string{
"0x0000000300000000000000000000000000000000", // Corrupted
"0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", // Valid WETH
},
shouldFail: true, // Should fail due to corruption
},
{
txHash: "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
inputData: "0x128acb08000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e583100000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1",
addresses: []string{
"0xaf88d065e77c8cC2239327C5EDb3A432268e5831", // Valid USDC
"0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", // Valid WETH
},
shouldFail: false, // Should succeed
},
}
for _, tx := range testTransactions {
t.Run(fmt.Sprintf("Transaction_%s", tx.txHash[:10]), func(t *testing.T) {
success := simulator.processTransaction(tx.txHash, tx.inputData, tx.addresses)
if tx.shouldFail {
assert.False(t, success, "Transaction with corruption should fail")
} else {
assert.True(t, success, "Transaction with valid addresses should succeed")
}
})
}
// Verify overall system health
metrics := simulator.integrityMonitor.GetMetrics()
assert.Greater(t, metrics.TotalAddressesProcessed, int64(0))
if metrics.CorruptAddressesDetected > 0 {
assert.Greater(t, metrics.RetryOperationsTriggered+metrics.FallbackOperationsUsed, int64(0),
"Should have triggered recovery mechanisms for corruption")
}
}
// processTransaction simulates processing a transaction through the full pipeline
func (cs *CorruptionSimulator) processTransaction(txHash, inputData string, addresses []string) bool {
allValid := true
for _, addr := range addresses {
cs.integrityMonitor.RecordAddressProcessed()
// Validate address
result := cs.validator.ValidateAddress(addr)
if !result.IsValid {
allValid = false
// Record corruption
address := common.HexToAddress(addr)
cs.integrityMonitor.RecordCorruptionDetected(address, result.CorruptionScore, "pipeline_test")
cs.integrityMonitor.RecordValidationResult(false)
// Attempt recovery
recoveryAction := cs.errorHandler.HandleError(
context.Background(),
recovery.ErrorTypeAddressCorruption,
recovery.SeverityCritical,
"transaction_processor",
address,
"Address corruption in transaction",
map[string]interface{}{
"tx_hash": txHash,
"input_data": inputData,
},
)
cs.integrityMonitor.RecordRecoveryAction(recoveryAction)
// Only continue if recovery suggests it's safe
if recoveryAction == recovery.ActionEmergencyStop || recoveryAction == recovery.ActionCircuitBreaker {
return false
}
} else {
cs.integrityMonitor.RecordValidationResult(true)
}
// Simulate contract call
if result.IsValid || result.CorruptionScore < 50 { // Allow low corruption with retry
cs.integrityMonitor.RecordContractCallResult(true)
} else {
cs.integrityMonitor.RecordContractCallResult(false)
allValid = false
}
}
return allValid
}
// Helper functions for generating test addresses
func generateCorruptedAddress(workerID, index int) string {
patterns := []string{
"0x000000%02d00000000000000000000000000000000", // TOKEN_0x000000 style
"0x%04x000000000000000000000000000000000000", // Partial corruption
"0x000000000000000000000000000000000000%04x", // Trailing corruption
}
pattern := patterns[index%len(patterns)]
return fmt.Sprintf(pattern, workerID*1000+index)
}
func generateValidAddress(workerID, index int) string {
bytes := make([]byte, common.AddressLength)
seed := workerID*97 + index*31
for i := 0; i < len(bytes); i++ {
seed = (seed*131 + i*17) & 0xff
bytes[i] = byte(seed)
}
return "0x" + hex.EncodeToString(bytes)
}

View File

@@ -0,0 +1,513 @@
//go:build integration && legacy && forked
// +build integration,legacy,forked
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"
"github.com/fraktal/mev-beta/bindings/arbitrage"
arbService "github.com/fraktal/mev-beta/pkg/arbitrage"
"github.com/fraktal/mev-beta/pkg/mev"
"github.com/fraktal/mev-beta/pkg/oracle"
"github.com/fraktal/mev-beta/pkg/uniswap"
)
func TestEndToEndProfitValidation(t *testing.T) {
client, cleanup := setupForkedArbitrum(t)
defer cleanup()
// Deploy arbitrage contract
privateKey, err := crypto.GenerateKey()
require.NoError(t, err)
auth, err := bind.NewKeyedTransactorWithChainID(privateKey, big.NewInt(42161))
require.NoError(t, err)
contractAddr, tx, contract, err := arbitrage.DeployArbitrageExecutor(
auth,
client,
common.HexToAddress("0x1F98431c8aD98523631AE4a59f267346ea31F984"), // Uniswap V3 Factory
common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"), // WETH
)
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("Real Market Arbitrage Opportunity", func(t *testing.T) {
// Real Arbitrum pool addresses with different fee tiers
wethUsdcPool05 := common.HexToAddress("0xC31E54c7a869B9FcBEcc14363CF510d1c41fa443") // 0.05%
wethUsdcPool30 := common.HexToAddress("0x17c14D2c404D167802b16C450d3c99F88F2c4F4d") // 0.3%
// Get current prices from both pools
price1, err := uniswap.GetPoolPrice(client, wethUsdcPool05)
require.NoError(t, err)
price2, err := uniswap.GetPoolPrice(client, wethUsdcPool30)
require.NoError(t, err)
t.Logf("Pool 1 (0.05%%) price: %s", price1.String())
t.Logf("Pool 2 (0.30%%) price: %s", price2.String())
// Calculate price difference
priceDiff := new(big.Int).Sub(price1, price2)
if priceDiff.Sign() < 0 {
priceDiff.Neg(priceDiff)
}
// Calculate percentage difference
priceDiffPercent := new(big.Int).Div(
new(big.Int).Mul(priceDiff, big.NewInt(10000)),
price1,
)
t.Logf("Price difference: %s (%s basis points)", priceDiff.String(), priceDiffPercent.String())
// Test arbitrage opportunity detection
swapAmount := big.NewInt(1000000000000000000) // 1 ETH
opportunity, err := contract.DetectArbitrageOpportunity(nil, wethUsdcPool05, wethUsdcPool30, swapAmount)
require.NoError(t, err)
if opportunity.Profitable {
t.Logf("Arbitrage opportunity detected!")
t.Logf("Estimated profit: %s ETH", new(big.Float).Quo(
new(big.Float).SetInt(opportunity.EstimatedProfit),
new(big.Float).SetInt(big.NewInt(1000000000000000000)),
).String())
// Validate minimum profit threshold
minProfit := big.NewInt(10000000000000000) // 0.01 ETH minimum
assert.GreaterOrEqual(t, opportunity.EstimatedProfit.Cmp(minProfit), 0,
"Profit should meet minimum threshold")
// Test gas cost calculation
gasPrice, err := client.SuggestGasPrice(context.Background())
require.NoError(t, err)
estimatedGas := big.NewInt(300000) // Estimated gas for arbitrage
gasCost := new(big.Int).Mul(gasPrice, estimatedGas)
netProfit := new(big.Int).Sub(opportunity.EstimatedProfit, gasCost)
t.Logf("Gas cost: %s ETH", new(big.Float).Quo(
new(big.Float).SetInt(gasCost),
new(big.Float).SetInt(big.NewInt(1000000000000000000)),
).String())
t.Logf("Net profit: %s ETH", new(big.Float).Quo(
new(big.Float).SetInt(netProfit),
new(big.Float).SetInt(big.NewInt(1000000000000000000)),
).String())
assert.Greater(t, netProfit.Sign(), 0, "Net profit should be positive after gas costs")
} else {
t.Log("No profitable arbitrage opportunity detected in current market conditions")
}
})
t.Run("Simulate Large Trade Impact", func(t *testing.T) {
// Simulate a large trade that creates arbitrage opportunity
wethUsdcPool := common.HexToAddress("0xC31E54c7a869B9FcBEcc14363CF510d1c41fa443")
// Large swap amount that should create price impact
largeSwapAmount := new(big.Int)
largeSwapAmount.SetString("100000000000000000000", 10) // 100 ETH
// Calculate price impact
priceImpact, err := contract.CalculatePriceImpact(nil, wethUsdcPool, largeSwapAmount, true)
require.NoError(t, err)
t.Logf("Price impact for 100 ETH swap: %s basis points", priceImpact.String())
// Price impact should be significant for large trades
assert.Greater(t, priceImpact.Uint64(), uint64(100), "Large trades should have measurable price impact")
// Test if this creates arbitrage opportunities
if priceImpact.Uint64() > 500 { // More than 5% price impact
// This should create profitable arbitrage opportunities
t.Log("Large trade creates significant arbitrage opportunity")
}
})
t.Run("Multi-Pool Arbitrage Chain", func(t *testing.T) {
// Test arbitrage opportunities across multiple pools
pools := []common.Address{
common.HexToAddress("0xC31E54c7a869B9FcBEcc14363CF510d1c41fa443"), // WETH/USDC 0.05%
common.HexToAddress("0x17c14D2c404D167802b16C450d3c99F88F2c4F4d"), // WETH/USDC 0.3%
common.HexToAddress("0x641C00A822e8b671738d32a431a4Fb6074E5c79d"), // WETH/USDT 0.05%
}
swapAmount := big.NewInt(5000000000000000000) // 5 ETH
totalOpportunities := 0
totalPotentialProfit := big.NewInt(0)
for i := 0; i < len(pools); i++ {
for j := i + 1; j < len(pools); j++ {
opportunity, err := contract.DetectArbitrageOpportunity(nil, pools[i], pools[j], swapAmount)
require.NoError(t, err)
if opportunity.Profitable {
totalOpportunities++
totalPotentialProfit.Add(totalPotentialProfit, opportunity.EstimatedProfit)
t.Logf("Opportunity between pool %d and %d: %s ETH profit",
i, j, new(big.Float).Quo(
new(big.Float).SetInt(opportunity.EstimatedProfit),
new(big.Float).SetInt(big.NewInt(1000000000000000000)),
).String())
}
}
}
t.Logf("Total opportunities found: %d", totalOpportunities)
t.Logf("Total potential profit: %s ETH", new(big.Float).Quo(
new(big.Float).SetInt(totalPotentialProfit),
new(big.Float).SetInt(big.NewInt(1000000000000000000)),
).String())
})
}
func TestRealWorldGasOptimization(t *testing.T) {
client, cleanup := setupForkedArbitrum(t)
defer cleanup()
t.Run("Gas Price Strategy Optimization", func(t *testing.T) {
// Get current network conditions
gasPrice, err := client.SuggestGasPrice(context.Background())
require.NoError(t, err)
// Get latest block for base fee (EIP-1559)
header, err := client.HeaderByNumber(context.Background(), nil)
require.NoError(t, err)
baseFee := header.BaseFee
t.Logf("Current gas price: %s gwei", new(big.Int).Div(gasPrice, big.NewInt(1000000000)))
t.Logf("Current base fee: %s gwei", new(big.Int).Div(baseFee, big.NewInt(1000000000)))
// Test MEV competition analysis
analyzer := mev.NewCompetitionAnalyzer(client)
opportunity := &mev.MEVOpportunity{
Type: mev.TypeArbitrage,
EstimatedProfit: big.NewInt(50000000000000000), // 0.05 ETH
RequiredGasLimit: big.NewInt(300000),
PoolAddress: common.HexToAddress("0xC31E54c7a869B9FcBEcc14363CF510d1c41fa443"),
Timestamp: time.Now(),
}
ctx := context.Background()
competition, err := analyzer.AnalyzeCompetition(ctx, opportunity)
require.NoError(t, err)
strategy, err := analyzer.CalculateOptimalBid(ctx, opportunity, competition)
require.NoError(t, err)
t.Logf("Recommended priority fee: %s gwei",
new(big.Int).Div(strategy.PriorityFeePerGas, big.NewInt(1000000000)))
t.Logf("Max fee per gas: %s gwei",
new(big.Int).Div(strategy.MaxFeePerGas, big.NewInt(1000000000)))
t.Logf("Expected profit after gas: %s ETH",
new(big.Float).Quo(
new(big.Float).SetInt(strategy.ExpectedProfit),
new(big.Float).SetInt(big.NewInt(1000000000000000000)),
).String())
// Validate strategy is profitable
assert.Greater(t, strategy.ExpectedProfit.Sign(), 0, "Strategy should be profitable after gas costs")
assert.LessOrEqual(t, strategy.MaxFeePerGas.Cmp(new(big.Int).Mul(baseFee, big.NewInt(3))), 0,
"Max fee should not exceed 3x base fee for reasonable execution")
})
t.Run("Gas Limit Optimization", func(t *testing.T) {
// Test different gas limits for arbitrage execution
gasLimits := []*big.Int{
big.NewInt(250000),
big.NewInt(300000),
big.NewInt(400000),
big.NewInt(500000),
}
profit := big.NewInt(80000000000000000) // 0.08 ETH base profit
gasPrice := big.NewInt(10000000000) // 10 gwei
bestGasLimit := big.NewInt(0)
bestNetProfit := big.NewInt(0)
for _, gasLimit := range gasLimits {
gasCost := new(big.Int).Mul(gasPrice, gasLimit)
netProfit := new(big.Int).Sub(profit, gasCost)
t.Logf("Gas limit %s: Net profit %s ETH",
gasLimit.String(),
new(big.Float).Quo(
new(big.Float).SetInt(netProfit),
new(big.Float).SetInt(big.NewInt(1000000000000000000)),
).String())
if netProfit.Cmp(bestNetProfit) > 0 {
bestNetProfit.Set(netProfit)
bestGasLimit.Set(gasLimit)
}
}
t.Logf("Optimal gas limit: %s", bestGasLimit.String())
assert.Greater(t, bestGasLimit.Uint64(), uint64(0), "Should find optimal gas limit")
})
}
func TestRealMarketConditions(t *testing.T) {
client, cleanup := setupForkedArbitrum(t)
defer cleanup()
t.Run("Market Volatility Impact", func(t *testing.T) {
// Test arbitrage detection under different market conditions
service, err := arbService.NewArbitrageService(client)
require.NoError(t, err)
// Create events representing different market conditions
volatileEvents := []*arbService.SimpleSwapEvent{
// Small trade - normal market
{
TxHash: common.HexToHash("0x1"),
Pool: common.HexToAddress("0xC31E54c7a869B9FcBEcc14363CF510d1c41fa443"),
Token0: common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"),
Token1: common.HexToAddress("0xaf88d065e77c8cC2239327C5EDb3A432268e5831"),
Amount0: big.NewInt(1000000000000000000), // 1 ETH
Amount1: big.NewInt(-2000000000), // -2000 USDC
SqrtPriceX96: func() *big.Int { x, _ := new(big.Int).SetString("79228162514264337593543950336", 10); return x }(),
},
// Large trade - volatile market
{
TxHash: common.HexToHash("0x2"),
Pool: common.HexToAddress("0xC31E54c7a869B9FcBEcc14363CF510d1c41fa443"),
Token0: common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"),
Token1: common.HexToAddress("0xaf88d065e77c8cC2239327C5EDb3A432268e5831"),
Amount0: func() *big.Int { x, _ := new(big.Int).SetString("50000000000000000000", 10); return x }(), // 50 ETH
Amount1: big.NewInt(-100000000000), // -100,000 USDC
SqrtPriceX96: func() *big.Int { x, _ := new(big.Int).SetString("80000000000000000000000000000", 10); return x }(),
},
}
detectedOpportunities := 0
for i, event := range volatileEvents {
err := service.ProcessSwapEvent(event)
require.NoError(t, err)
// Check if this event would trigger arbitrage detection
if service.IsSignificantSwap(event) {
detectedOpportunities++
t.Logf("Event %d triggered arbitrage detection (amount: %s ETH)",
i+1, new(big.Float).Quo(
new(big.Float).SetInt(event.Amount0),
new(big.Float).SetInt(big.NewInt(1000000000000000000)),
).String())
}
}
assert.Greater(t, detectedOpportunities, 0, "Should detect opportunities in volatile market")
})
t.Run("Oracle Price Validation", func(t *testing.T) {
// Test oracle-based price validation for arbitrage
priceOracle := oracle.NewPriceOracle(client)
// WETH/USDC price from different sources
wethAddress := common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1")
usdcAddress := common.HexToAddress("0xaf88d065e77c8cC2239327C5EDb3A432268e5831")
ctx := context.Background()
// Get price from Uniswap V3
uniPrice, err := priceOracle.GetUniswapV3Price(ctx, wethAddress, usdcAddress, 500)
require.NoError(t, err)
// Get price from alternative DEX (SushiSwap)
sushiPrice, err := priceOracle.GetSushiSwapPrice(ctx, wethAddress, usdcAddress)
require.NoError(t, err)
t.Logf("Uniswap V3 WETH/USDC price: %s", uniPrice.String())
t.Logf("SushiSwap WETH/USDC price: %s", sushiPrice.String())
// Calculate price deviation
priceDiff := new(big.Int).Sub(uniPrice, sushiPrice)
if priceDiff.Sign() < 0 {
priceDiff.Neg(priceDiff)
}
deviationPercent := new(big.Int).Div(
new(big.Int).Mul(priceDiff, big.NewInt(10000)),
uniPrice,
)
t.Logf("Price deviation: %s basis points", deviationPercent.String())
// Significant price deviation indicates arbitrage opportunity
if deviationPercent.Uint64() > 50 { // More than 0.5%
t.Log("Significant price deviation detected - potential arbitrage opportunity")
assert.Greater(t, deviationPercent.Uint64(), uint64(50), "Price deviation indicates opportunity")
} else {
t.Log("Prices are aligned - no immediate arbitrage opportunity")
}
})
t.Run("Liquidity Depth Analysis", func(t *testing.T) {
// Test liquidity depth for arbitrage execution
pools := []common.Address{
common.HexToAddress("0xC31E54c7a869B9FcBEcc14363CF510d1c41fa443"), // WETH/USDC 0.05%
common.HexToAddress("0x17c14D2c404D167802b16C450d3c99F88F2c4F4d"), // WETH/USDC 0.3%
}
for i, pool := range pools {
liquidity, err := uniswap.GetPoolLiquidity(client, pool)
require.NoError(t, err)
t.Logf("Pool %d liquidity: %s", i+1, liquidity.String())
// Minimum liquidity threshold for profitable arbitrage
minLiquidity := new(big.Int)
minLiquidity.SetString("1000000000000000000000", 10) // 1000 ETH equivalent
if liquidity.Cmp(minLiquidity) >= 0 {
t.Logf("Pool %d has sufficient liquidity for large arbitrage", i+1)
} else {
t.Logf("Pool %d has limited liquidity - small arbitrage only", i+1)
}
assert.Greater(t, liquidity.Uint64(), uint64(0), "Pool should have measurable liquidity")
}
})
}
func TestProfitabilityUnderStress(t *testing.T) {
client, cleanup := setupForkedArbitrum(t)
defer cleanup()
t.Run("High Gas Price Environment", func(t *testing.T) {
// Simulate high gas price conditions (network congestion)
highGasPrice := big.NewInt(50000000000) // 50 gwei
opportunity := &mev.MEVOpportunity{
Type: mev.TypeArbitrage,
EstimatedProfit: big.NewInt(30000000000000000), // 0.03 ETH
RequiredGasLimit: big.NewInt(300000),
PoolAddress: common.HexToAddress("0xC31E54c7a869B9FcBEcc14363CF510d1c41fa443"),
Timestamp: time.Now(),
}
gasCost := new(big.Int).Mul(highGasPrice, opportunity.RequiredGasLimit)
netProfit := new(big.Int).Sub(opportunity.EstimatedProfit, gasCost)
t.Logf("High gas environment - Gas cost: %s ETH, Net profit: %s ETH",
new(big.Float).Quo(new(big.Float).SetInt(gasCost), new(big.Float).SetInt(big.NewInt(1e18))),
new(big.Float).Quo(new(big.Float).SetInt(netProfit), new(big.Float).SetInt(big.NewInt(1e18))))
if netProfit.Sign() > 0 {
t.Log("Arbitrage remains profitable even with high gas prices")
} else {
t.Log("High gas prices make arbitrage unprofitable")
}
})
t.Run("MEV Competition Pressure", func(t *testing.T) {
// Simulate competitive MEV environment
analyzer := mev.NewCompetitionAnalyzer(client)
opportunity := &mev.MEVOpportunity{
Type: mev.TypeArbitrage,
EstimatedProfit: big.NewInt(100000000000000000), // 0.1 ETH
RequiredGasLimit: big.NewInt(300000),
PoolAddress: common.HexToAddress("0xC31E54c7a869B9FcBEcc14363CF510d1c41fa443"),
Timestamp: time.Now(),
}
ctx := context.Background()
// Simulate different competition levels
competitionLevels := []string{"low", "medium", "high", "extreme"}
for _, level := range competitionLevels {
// Mock competition metrics based on level
competition := &mev.CompetitionMetrics{
CompetitorCount: getCompetitorCount(level),
AveragePriorityFee: getAveragePriorityFee(level),
SuccessRate: getSuccessRate(level),
RecentOpportunities: 10,
}
strategy, err := analyzer.CalculateOptimalBid(ctx, opportunity, competition)
require.NoError(t, err)
t.Logf("Competition level %s: Priority fee %s gwei, Expected profit %s ETH",
level,
new(big.Int).Div(strategy.PriorityFeePerGas, big.NewInt(1e9)),
new(big.Float).Quo(new(big.Float).SetInt(strategy.ExpectedProfit), new(big.Float).SetInt(big.NewInt(1e18))))
// Even under extreme competition, some profit should remain
if level != "extreme" {
assert.Greater(t, strategy.ExpectedProfit.Sign(), 0,
"Should maintain profitability under %s competition", level)
}
}
})
}
// Helper functions for stress testing
func getCompetitorCount(level string) int {
switch level {
case "low":
return 2
case "medium":
return 5
case "high":
return 10
case "extreme":
return 20
default:
return 3
}
}
func getAveragePriorityFee(level string) *big.Int {
switch level {
case "low":
return big.NewInt(2000000000) // 2 gwei
case "medium":
return big.NewInt(5000000000) // 5 gwei
case "high":
return big.NewInt(10000000000) // 10 gwei
case "extreme":
return big.NewInt(25000000000) // 25 gwei
default:
return big.NewInt(3000000000) // 3 gwei
}
}
func getSuccessRate(level string) float64 {
switch level {
case "low":
return 0.9
case "medium":
return 0.7
case "high":
return 0.4
case "extreme":
return 0.1
default:
return 0.8
}
}

View File

@@ -0,0 +1,190 @@
//go:build integration && legacy
// +build integration,legacy
package integration_test
import (
"context"
"crypto/tls"
"math/big"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/require"
"github.com/fraktal/mev-beta/internal/logger"
"github.com/fraktal/mev-beta/pkg/security"
)
const (
testEncryptionKey = "integrationlegacyencryptionkey0123456789"
)
func newSecurityManagerForTest(t *testing.T) (*security.SecurityManager, func()) {
t.Helper()
// Create local RPC stub that always succeeds
rpcServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"jsonrpc":"2.0","id":"sm-1","result":"0x1"}`))
}))
// Use repo-local temp directory to satisfy production validation rules
keyDir, err := os.MkdirTemp(".", "sec-harness-")
require.NoError(t, err)
t.Cleanup(func() { _ = os.RemoveAll(keyDir) })
// Ensure logs directory exists to avoid cluttering stdout in parallel runs
require.NoError(t, os.MkdirAll("logs", 0o755))
t.Setenv("MEV_BOT_ENCRYPTION_KEY", testEncryptionKey)
cfg := &security.SecurityConfig{
KeyStoreDir: keyDir,
EncryptionEnabled: true,
TransactionRPS: 25,
RPCRPS: 25,
MaxBurstSize: 5,
FailureThreshold: 3,
RecoveryTimeout: 2 * time.Second,
TLSMinVersion: tls.VersionTLS12,
TLSCipherSuites: []uint16{
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
},
EmergencyStopFile: filepath.Join(keyDir, "emergency.stop"),
MaxGasPrice: "50000000000", // 50 gwei
LogLevel: "error",
RPCURL: rpcServer.URL,
}
manager, err := security.NewSecurityManager(cfg)
require.NoError(t, err)
cleanup := func() {
rpcServer.Close()
// Trigger emergency stop to halt background activity gracefully
_ = manager.TriggerEmergencyStop("test cleanup")
}
return manager, cleanup
}
func TestLegacySecurityManagerEndToEnd(t *testing.T) {
manager, cleanup := newSecurityManagerForTest(t)
defer cleanup()
recipient := common.HexToAddress("0x8a753747A1Fa494EC906cE90E9f37563A8AF630e")
params := &security.TransactionParams{
To: &recipient,
Value: big.NewInt(1_000_000_000_000_000), // 0.001 ETH
Gas: 21000,
GasPrice: big.NewInt(1_000_000_000), // 1 gwei
Nonce: 0,
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// Validate transaction under normal operation
require.NoError(t, manager.ValidateTransaction(ctx, params))
// Perform secure RPC call against stub server
result, err := manager.SecureRPCCall(ctx, "eth_chainId", []interface{}{})
require.NoError(t, err)
require.Equal(t, "0x1", result)
// Trigger emergency stop and confirm transactions are blocked
require.NoError(t, manager.TriggerEmergencyStop("integration harness assertion"))
err = manager.ValidateTransaction(ctx, params)
require.Error(t, err)
require.Contains(t, err.Error(), "emergency mode")
}
func TestLegacyChainIDValidatorIntegration(t *testing.T) {
t.Setenv("MEV_BOT_ENCRYPTION_KEY", testEncryptionKey)
keystoreDir, err := os.MkdirTemp(".", "km-harness-")
require.NoError(t, err)
defer os.RemoveAll(keystoreDir)
logger := logger.New("error", "text", "")
cfg := &security.KeyManagerConfig{
KeyDir: keystoreDir,
KeystorePath: keystoreDir,
EncryptionKey: testEncryptionKey,
BackupEnabled: false,
MaxFailedAttempts: 5,
LockoutDuration: time.Minute,
MaxSigningRate: 20,
SessionTimeout: time.Minute,
EnableRateLimiting: false,
}
chainID := big.NewInt(42161)
keyManager, err := security.NewKeyManagerWithChainID(cfg, logger, chainID)
require.NoError(t, err)
defer keyManager.Shutdown()
privateKey, err := keyManager.GetActivePrivateKey()
require.NoError(t, err)
fromAddr := crypto.PubkeyToAddress(privateKey.PublicKey)
toAddr := common.HexToAddress("0xC36442b4a4522E871399CD717aBDD847Ab11FE88")
tx := types.NewTransaction(
0,
toAddr,
big.NewInt(0),
21000,
big.NewInt(1_500_000_000), // 1.5 gwei
nil,
)
request := &security.SigningRequest{
Transaction: tx,
ChainID: chainID,
From: fromAddr,
Purpose: "integration test",
UrgencyLevel: 1,
}
result, err := keyManager.SignTransaction(request)
require.NoError(t, err)
require.NotNil(t, result.SignedTx)
validator := security.NewChainIDValidator(logger, chainID)
validation := validator.ValidateChainID(result.SignedTx, fromAddr, nil)
require.True(t, validation.Valid)
require.Equal(t, "NONE", validation.ReplayRisk)
// Sign a transaction with an incorrect chain ID manually and ensure validator catches it
privateKeyMismatch, err := keyManager.GetActivePrivateKey()
require.NoError(t, err)
mismatchTx := types.NewTransaction(
1,
toAddr,
big.NewInt(0),
21000,
big.NewInt(1_500_000_000),
nil,
)
wrongSigner := types.NewEIP155Signer(big.NewInt(1))
mismatchedSignedTx, err := types.SignTx(mismatchTx, wrongSigner, privateKeyMismatch)
require.NoError(t, err)
mismatchResult := validator.ValidateChainID(mismatchedSignedTx, fromAddr, nil)
require.False(t, mismatchResult.Valid)
require.Greater(t, len(mismatchResult.Errors), 0)
}

View File

@@ -0,0 +1,181 @@
//go:build integration && legacy && forked
// +build integration,legacy,forked
package integration_test
import (
"context"
"fmt"
"math/big"
"os"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/fraktal/mev-beta/internal/config"
"github.com/fraktal/mev-beta/internal/logger"
"github.com/fraktal/mev-beta/pkg/arbitrage"
"github.com/fraktal/mev-beta/pkg/marketmanager"
"github.com/fraktal/mev-beta/pkg/security"
)
func main() {
// Create a simple test to verify integration
fmt.Println("Testing Market Manager Integration...")
// Create logger
log := logger.New("debug", "text", "")
// Create a mock config
cfg := &config.ArbitrageConfig{
Enabled: true,
ArbitrageContractAddress: "0x1234567890123456789012345678901234567890",
FlashSwapContractAddress: "0x0987654321098765432109876543210987654321",
MinProfitThreshold: 10000000000000000, // 0.01 ETH
MinROIPercent: 0.1, // 0.1%
MaxConcurrentExecutions: 5,
OpportunityTTL: time.Minute,
MinSignificantSwapSize: 1000000000000000000, // 1 ETH
GasPriceMultiplier: 1.2,
SlippageTolerance: 0.005, // 0.5%
}
// Create mock database (in real implementation this would be a real DB)
mockDB := &MockDatabase{}
// Create key manager config
keyManagerConfig := &security.KeyManagerConfig{
KeystorePath: "./test-keys",
EncryptionKey: "test-key-1234567890",
KeyRotationDays: 30,
MaxSigningRate: 100,
SessionTimeout: time.Hour,
AuditLogPath: "./test-audit.log",
BackupPath: "./test-backups",
}
// Create key manager
keyManager, err := security.NewKeyManager(keyManagerConfig, log)
if err != nil {
fmt.Printf("Failed to create key manager: %v\n", err)
os.Exit(1)
}
// Create a mock Ethereum client (in real implementation this would be a real client)
// For this test, we'll pass nil and handle it in the service
fmt.Println("Creating arbitrage service with market manager integration...")
// Create arbitrage service - this will now include the market manager integration
// Note: In a real implementation, you would pass a real Ethereum client
arbitrageService, err := arbitrage.NewArbitrageService(
nil, // Mock client - in real implementation this would be a real client
log,
cfg,
keyManager,
mockDB,
)
if err != nil {
fmt.Printf("Failed to create arbitrage service: %v\n", err)
os.Exit(1)
}
fmt.Println("✅ Arbitrage service created successfully with market manager integration")
// Test the market manager functionality
testMarketManagerIntegration(arbitrageService)
fmt.Println("✅ Integration test completed successfully!")
}
func testMarketManagerIntegration(service *arbitrage.ArbitrageService) {
fmt.Println("Testing market manager integration...")
// Create a sample market using the new market manager
factory := common.HexToAddress("0x1F98431c8aD98523631AE4a59f267346ea31F984") // Uniswap V3 Factory
poolAddress := common.HexToAddress("0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640") // Sample pool
token0 := common.HexToAddress("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48") // USDC
token1 := common.HexToAddress("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2") // WETH
// Create market using marketmanager
market := marketmanager.NewMarket(
factory,
poolAddress,
token0,
token1,
3000, // 0.3% fee
"USDC_WETH",
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48_0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"UniswapV3",
)
// Set market data
market.UpdatePriceData(
big.NewFloat(2000.0), // Price: 2000 USDC per WETH
big.NewInt(1000000000000000000), // Liquidity: 1 ETH
big.NewInt(2505414483750470000), // sqrtPriceX96
200000, // Tick
)
market.UpdateMetadata(
time.Now().Unix(),
12345678,
common.HexToHash("0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"),
marketmanager.StatusConfirmed,
)
fmt.Printf("✅ Created sample market: %s\n", market.Ticker)
// Test conversion functions
convertedMarket := service.ConvertPoolDataToMarket(&MockPoolData{
Address: poolAddress,
Token0: token0,
Token1: token1,
Fee: 500, // 0.05% fee
Liquidity: big.NewInt(500000000000000000), // 0.5 ETH
SqrtPriceX96: big.NewInt(2505414483750470000), // Same sqrtPriceX96
Tick: 200000,
}, "UniswapV3")
fmt.Printf("✅ Converted market from PoolData: %s\n", convertedMarket.Ticker)
// Test reverse conversion
convertedPoolData := service.ConvertMarketToPoolData(market)
fmt.Printf("✅ Converted PoolData from market: Fee=%d, Tick=%d\n", convertedPoolData.Fee, convertedPoolData.Tick)
fmt.Println("✅ Market manager integration test completed!")
}
// MockDatabase implements the ArbitrageDatabase interface for testing
type MockDatabase struct{}
func (m *MockDatabase) SaveOpportunity(ctx context.Context, opportunity *arbitrage.ArbitrageOpportunity) error {
return nil
}
func (m *MockDatabase) SaveExecution(ctx context.Context, result *arbitrage.ExecutionResult) error {
return nil
}
func (m *MockDatabase) GetExecutionHistory(ctx context.Context, limit int) ([]*arbitrage.ExecutionResult, error) {
return []*arbitrage.ExecutionResult{}, nil
}
func (m *MockDatabase) SavePoolData(ctx context.Context, poolData *arbitrage.SimplePoolData) error {
return nil
}
func (m *MockDatabase) GetPoolData(ctx context.Context, poolAddress common.Address) (*arbitrage.SimplePoolData, error) {
return nil, nil
}
// MockPoolData simulates the existing PoolData structure
type MockPoolData struct {
Address common.Address
Token0 common.Address
Token1 common.Address
Fee int64
Liquidity *big.Int
SqrtPriceX96 *big.Int
Tick int
}

View File

@@ -0,0 +1,413 @@
//go:build integration && legacy && forked
// +build integration,legacy,forked
package integration_test
import (
"context"
"fmt"
"math/big"
"runtime"
"sync"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/assert"
)
func BenchmarkArbitrageDetection(b *testing.B) {
client, cleanup := setupForkedArbitrum(b)
defer cleanup()
b.ResetTimer()
b.ReportAllocs()
// Benchmark basic arbitrage detection logic
for i := 0; i < b.N; i++ {
// Simulate arbitrage detection calculations
pool1Price := big.NewInt(2000000000) // 2000 USDC
pool2Price := big.NewInt(2010000000) // 2010 USDC
swapAmount := big.NewInt(1000000000000000000) // 1 ETH
// Calculate price difference
priceDiff := new(big.Int).Sub(pool2Price, pool1Price)
if priceDiff.Sign() > 0 {
// Calculate potential profit
profit := new(big.Int).Mul(priceDiff, swapAmount)
profit.Div(profit, pool1Price)
_ = profit // Use result to prevent optimization
}
}
}
func BenchmarkPoolDiscovery(b *testing.B) {
client, cleanup := setupForkedArbitrum(b)
defer cleanup()
// Benchmark pool discovery logic
factories := []common.Address{
common.HexToAddress("0x1F98431c8aD98523631AE4a59f267346ea31F984"), // Uniswap V3
common.HexToAddress("0xc35DADB65012eC5796536bD9864eD8773aBc74C4"), // SushiSwap V2
common.HexToAddress("0x6EcCab422D763aC031210895C81787E87B6EAeaa"), // Camelot V2
}
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
// Simulate pool discovery operations
for j, factory := range factories {
// Mock pool discovery timing
poolCount := 10 + j*5
pools := make([]common.Address, poolCount)
for k := 0; k < poolCount; k++ {
// Generate mock pool addresses
pools[k] = common.BigToAddress(big.NewInt(int64(k) + factory.Big().Int64()))
}
_ = pools // Use result
}
}
}
func BenchmarkConcurrentOpportunityScanning(b *testing.B) {
client, cleanup := setupForkedArbitrum(b)
defer cleanup()
// Real pool addresses for testing
pools := []common.Address{
common.HexToAddress("0xC31E54c7a869B9FcBEcc14363CF510d1c41fa443"), // WETH/USDC 0.05%
common.HexToAddress("0x17c14D2c404D167802b16C450d3c99F88F2c4F4d"), // WETH/USDC 0.3%
common.HexToAddress("0x641C00A822e8b671738d32a431a4Fb6074E5c79d"), // WETH/USDT 0.05%
common.HexToAddress("0xB1026b8e7276e7AC75410F1fcbbe21796e8f7526"), // WBTC/USDC 0.05%
}
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
// Simulate concurrent opportunity scanning
for _, pool := range pools {
// Mock price comparison between pools
price1 := big.NewInt(2000000000)
price2 := big.NewInt(2005000000)
swapAmount := big.NewInt(1000000000000000000)
// Calculate opportunity profitability
priceDiff := new(big.Int).Sub(price2, price1)
profit := new(big.Int).Mul(priceDiff, swapAmount)
profit.Div(profit, price1)
_ = profit // Use result
_ = pool // Use pool
}
}
}
func BenchmarkMEVCompetitionAnalysis(b *testing.B) {
client, cleanup := setupForkedArbitrum(b)
defer cleanup()
b.ResetTimer()
b.ReportAllocs()
// Benchmark MEV competition analysis
for i := 0; i < b.N; i++ {
// Simulate competition analysis calculations
estimatedProfit := big.NewInt(100000000000000000) // 0.1 ETH
gasLimit := big.NewInt(300000)
gasPrice := big.NewInt(20000000000) // 20 gwei
competitorCount := 5
// Calculate gas cost
gasCost := new(big.Int).Mul(gasPrice, gasLimit)
// Calculate competition factor
competitionFactor := big.NewInt(int64(competitorCount * 2))
adjustedGasPrice := new(big.Int).Add(gasPrice, competitionFactor)
// Calculate net profit
netProfit := new(big.Int).Sub(estimatedProfit, new(big.Int).Mul(adjustedGasPrice, gasLimit))
_ = netProfit // Use result
}
}
func TestConcurrentArbitrageDetection(t *testing.T) {
client, cleanup := setupForkedArbitrum(t)
defer cleanup()
t.Run("High Load Concurrent Processing", func(t *testing.T) {
numWorkers := 20
eventsPerWorker := 100
totalEvents := numWorkers * eventsPerWorker
var wg sync.WaitGroup
errors := make(chan error, totalEvents)
processed := make(chan int, totalEvents)
startTime := time.Now()
// Launch concurrent workers
for w := 0; w < numWorkers; w++ {
wg.Add(1)
go func(workerID int) {
defer wg.Done()
// Simulate processing events
for i := 0; i < eventsPerWorker; i++ {
// Mock event processing
price1 := big.NewInt(2000000000)
price2 := big.NewInt(2005000000)
swapAmount := big.NewInt(1000000000000000000)
// Calculate arbitrage opportunity
priceDiff := new(big.Int).Sub(price2, price1)
profit := new(big.Int).Mul(priceDiff, swapAmount)
profit.Div(profit, price1)
if profit.Sign() > 0 {
processed <- 1
} else {
processed <- 1
}
}
}(w)
}
// Wait for completion or timeout
done := make(chan bool)
go func() {
wg.Wait()
close(done)
}()
processedCount := 0
timeout := time.After(60 * time.Second)
processing:
for {
select {
case <-processed:
processedCount++
if processedCount == totalEvents {
break processing
}
case err := <-errors:
t.Errorf("Processing error: %v", err)
case <-timeout:
t.Fatalf("Test timed out after 60 seconds. Processed %d/%d events", processedCount, totalEvents)
case <-done:
break processing
}
}
duration := time.Since(startTime)
eventsPerSecond := float64(processedCount) / duration.Seconds()
t.Logf("Processed %d events in %v (%.2f events/sec)", processedCount, duration, eventsPerSecond)
// Performance assertions
assert.Equal(t, totalEvents, processedCount, "Should process all events")
assert.Greater(t, eventsPerSecond, 100.0, "Should process at least 100 events per second")
assert.Less(t, duration, 30*time.Second, "Should complete within 30 seconds")
})
t.Run("Memory Usage Under Load", func(t *testing.T) {
// Test memory efficiency with large number of events
eventCount := 10000
var memBefore, memAfter runtime.MemStats
runtime.GC()
runtime.ReadMemStats(&memBefore)
// Simulate processing large number of events
for i := 0; i < eventCount; i++ {
// Mock event processing that allocates memory
eventData := make([]byte, 256) // Simulate event data
result := make(map[string]*big.Int)
result["profit"] = big.NewInt(int64(i * 1000))
result["gas"] = big.NewInt(300000)
_ = eventData
_ = result
}
runtime.GC()
runtime.ReadMemStats(&memAfter)
memUsed := memAfter.Alloc - memBefore.Alloc
memPerEvent := float64(memUsed) / float64(eventCount)
t.Logf("Memory used: %d bytes for %d events (%.2f bytes/event)",
memUsed, eventCount, memPerEvent)
// Memory efficiency assertion
assert.Less(t, memPerEvent, 2048.0, "Should use less than 2KB per event on average")
})
}
func TestPoolDiscoveryPerformance(t *testing.T) {
client, cleanup := setupForkedArbitrum(t)
defer cleanup()
t.Run("Large Scale Pool Discovery", func(t *testing.T) {
// Test discovery across multiple factories
factories := map[string]common.Address{
"Uniswap V3": common.HexToAddress("0x1F98431c8aD98523631AE4a59f267346ea31F984"),
"SushiSwap V2": common.HexToAddress("0xc35DADB65012eC5796536bD9864eD8773aBc74C4"),
"Camelot V2": common.HexToAddress("0x6EcCab422D763aC031210895C81787E87B6EAeaa"),
}
totalPools := 0
startTime := time.Now()
for name, factory := range factories {
// Mock pool discovery
mockPoolCount := 25 + len(name) // Vary by factory
totalPools += mockPoolCount
t.Logf("%s: Discovered %d pools", name, mockPoolCount)
// Simulate discovery time
time.Sleep(100 * time.Millisecond)
_ = factory // Use factory
}
duration := time.Since(startTime)
poolsPerSecond := float64(totalPools) / duration.Seconds()
t.Logf("Total pools discovered: %d in %v (%.2f pools/sec)",
totalPools, duration, poolsPerSecond)
// Performance assertions
assert.Greater(t, totalPools, 50, "Should discover at least 50 pools across all factories")
assert.Less(t, duration, 30*time.Second, "Discovery should complete within 30 seconds")
})
t.Run("Concurrent Pool Discovery", func(t *testing.T) {
factories := []common.Address{
common.HexToAddress("0x1F98431c8aD98523631AE4a59f267346ea31F984"),
common.HexToAddress("0xc35DADB65012eC5796536bD9864eD8773aBc74C4"),
common.HexToAddress("0x6EcCab422D763aC031210895C81787E87B6EAeaa"),
}
var wg sync.WaitGroup
results := make(chan int, len(factories))
errors := make(chan error, len(factories))
startTime := time.Now()
for _, factory := range factories {
wg.Add(1)
go func(f common.Address) {
defer wg.Done()
// Mock concurrent discovery
mockPoolCount := 15 + int(f.Big().Int64()%20)
time.Sleep(50 * time.Millisecond) // Simulate network delay
results <- mockPoolCount
}(factory)
}
wg.Wait()
close(results)
close(errors)
// Check for errors
for err := range errors {
t.Errorf("Discovery error: %v", err)
}
// Count total pools
totalPools := 0
for count := range results {
totalPools += count
}
duration := time.Since(startTime)
t.Logf("Concurrent discovery: %d pools in %v", totalPools, duration)
assert.Greater(t, totalPools, 30, "Should discover pools concurrently")
assert.Less(t, duration, 20*time.Second, "Concurrent discovery should be faster")
})
}
func TestRealTimeEventProcessing(t *testing.T) {
client, cleanup := setupForkedArbitrum(t)
defer cleanup()
t.Run("Real-time Block Processing", func(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
processed := make(chan *MockSwapEvent, 100)
errors := make(chan error, 10)
// Mock real-time block processing
go func() {
blockNum := uint64(12345)
for {
select {
case <-time.After(1 * time.Second):
// Mock processing a block
blockNum++
// Generate mock swap events
for i := 0; i < 3; i++ {
mockEvent := &MockSwapEvent{
TxHash: common.HexToHash(fmt.Sprintf("0x%d%d", blockNum, i)),
Pool: common.HexToAddress("0xC31E54c7a869B9FcBEcc14363CF510d1c41fa443"),
}
processed <- mockEvent
}
case <-ctx.Done():
return
}
}
}()
// Collect results
eventCount := 0
timeout := time.After(45 * time.Second)
for {
select {
case event := <-processed:
eventCount++
if mockEvent, ok := event.(*MockSwapEvent); ok {
t.Logf("Processed event: %s", mockEvent.TxHash.Hex())
}
case err := <-errors:
t.Errorf("Processing error: %v", err)
case <-timeout:
t.Logf("Processed %d events in real-time", eventCount)
return
case <-ctx.Done():
t.Logf("Processed %d events before context cancellation", eventCount)
return
}
}
})
}
// Helper functions and types for benchmarking
// MockSwapEvent represents a swap event for testing
type MockSwapEvent struct {
TxHash common.Hash
Pool common.Address
}
// MockArbitrageService for testing
type MockArbitrageService struct{}
func (m *MockArbitrageService) ProcessSwapEvent(event *MockSwapEvent) error {
// Mock processing logic
time.Sleep(1 * time.Microsecond)
return nil
}
func (m *MockArbitrageService) IsSignificantSwap(event *MockSwapEvent) bool {
// Mock significance check
return event.TxHash[0]%2 == 0
}

View File

@@ -0,0 +1,145 @@
//go:build integration && legacy && forked
// +build integration,legacy,forked
package integration_test
import (
"context"
"math/big"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/stretchr/testify/assert"
"github.com/fraktal/mev-beta/internal/config"
"github.com/fraktal/mev-beta/internal/logger"
"github.com/fraktal/mev-beta/pkg/events"
"github.com/fraktal/mev-beta/pkg/market"
"github.com/fraktal/mev-beta/pkg/scanner"
)
func TestPipelineIntegration(t *testing.T) {
// Create test config
cfg := &config.BotConfig{
MaxWorkers: 2,
ChannelBufferSize: 5,
MinProfitThreshold: 10.0,
}
// Create test logger
logger := logger.New("info", "text", "")
// Create market manager
marketMgr := market.NewMarketManager(&config.UniswapConfig{
Cache: config.CacheConfig{
Expiration: 300,
MaxSize: 10000,
},
}, logger)
// Create market scanner
scanner := scanner.NewMarketScanner(cfg, logger)
// Create pipeline
pipeline := market.NewPipeline(cfg, logger, marketMgr, scanner, nil)
// Add default stages
pipeline.AddDefaultStages()
// Create test transactions
transactions := make([]*types.Transaction, 0)
// Create a transaction that interacts with a DEX
to := common.HexToAddress("0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640") // Uniswap V3 pool
tx := types.NewTransaction(0, to, big.NewInt(0), 0, big.NewInt(0), nil)
transactions = append(transactions, tx)
// Process transactions through the pipeline
ctx := context.Background()
blockNumber := uint64(12345)
timestamp := uint64(time.Now().Unix())
err := pipeline.ProcessTransactions(ctx, transactions, blockNumber, timestamp)
// Verify no error
assert.NoError(t, err)
}
func TestMarketManagerAndScannerIntegration(t *testing.T) {
// Create test logger
logger := logger.New("info", "text", "")
// Create market manager
marketMgr := market.NewMarketManager(&config.UniswapConfig{
Cache: config.CacheConfig{
Expiration: 300,
MaxSize: 10000,
},
}, logger)
// Get a pool from the market manager
ctx := context.Background()
poolAddress := common.HexToAddress("0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640")
pool, err := marketMgr.GetPool(ctx, poolAddress)
// Verify no error and pool is not nil
assert.NoError(t, err)
assert.NotNil(t, pool)
// Get pools by tokens
token0 := common.HexToAddress("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48") // USDC
token1 := common.HexToAddress("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2") // WETH
pools := marketMgr.GetPoolsByTokens(token0, token1)
// Verify pools are returned
assert.NotNil(t, pools)
}
func TestEventParserAndPipelineIntegration(t *testing.T) {
// Create test config
cfg := &config.BotConfig{
MaxWorkers: 2,
ChannelBufferSize: 5,
}
// Create test logger
logger := logger.New("info", "text", "")
// Create market manager
marketMgr := market.NewMarketManager(&config.UniswapConfig{
Cache: config.CacheConfig{
Expiration: 300,
MaxSize: 10000,
},
}, logger)
// Create market scanner
scnr := scanner.NewMarketScanner(cfg, logger)
// Create pipeline
pipe := market.NewPipeline(cfg, logger, marketMgr, scnr, nil)
pipe.AddDefaultStages()
// Create event parser
parser := events.NewEventParser()
// Create a transaction that interacts with a DEX
to := common.HexToAddress("0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640") // Uniswap V3 pool
tx := types.NewTransaction(0, to, big.NewInt(0), 0, big.NewInt(0), nil)
blockNumber := uint64(12345)
timestamp := uint64(time.Now().Unix())
// Parse the transaction
parsedEvents, err := parser.ParseTransaction(tx, blockNumber, timestamp)
assert.NoError(t, err)
assert.Len(t, parsedEvents, 1)
// Verify the parsed event
event := parsedEvents[0]
assert.Equal(t, events.Swap, event.Type)
assert.Equal(t, "UniswapV3", event.Protocol)
assert.Equal(t, to, event.PoolAddress)
assert.Equal(t, blockNumber, event.BlockNumber)
assert.Equal(t, timestamp, event.Timestamp)
}

View File

@@ -0,0 +1,565 @@
//go:build integration && legacy && forked
// +build integration,legacy,forked
package integration_test
import (
"context"
"fmt"
"math/big"
"net/url"
"os"
"strings"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/fraktal/mev-beta/internal/logger"
"github.com/fraktal/mev-beta/pkg/mev"
"github.com/fraktal/mev-beta/pkg/security"
"github.com/fraktal/mev-beta/pkg/types"
)
// TestRealWorldProfitability tests actual profitability with real market conditions
func TestRealWorldProfitability(t *testing.T) {
if testing.Short() {
t.Skip("Skipping real-world profitability test in short mode")
}
// Set up real environment
setupRealEnvironment(t)
client, err := ethclient.Dial(os.Getenv("ARBITRUM_RPC_ENDPOINT"))
require.NoError(t, err, "Failed to connect to Arbitrum")
defer client.Close()
log := logger.New("debug", "text", "")
t.Run("TestActualArbitrageOpportunityDetection", func(t *testing.T) {
// Test with real WETH/USDC pool on Arbitrum
wethUsdcPool := common.HexToAddress("0xC31E54c7a869B9FcBEcc14363CF510d1c41fa443")
// Query real pool state
opportunities, err := detectRealArbitrageOpportunities(client, wethUsdcPool, log)
require.NoError(t, err)
if len(opportunities) > 0 {
t.Logf("✅ Found %d real arbitrage opportunities", len(opportunities))
for i, opp := range opportunities {
t.Logf("Opportunity %d: Profit=%s ETH, Gas=%s, ROI=%.2f%%",
i+1, formatEther(opp.Profit), opp.GasEstimate.String(), opp.ROI)
// Validate minimum profitability
assert.True(t, opp.Profit.Cmp(big.NewInt(50000000000000000)) >= 0, // 0.05 ETH min
"Opportunity should meet minimum profit threshold")
}
} else {
t.Log("⚠️ No arbitrage opportunities found at this time (normal)")
}
})
t.Run("TestRealGasCostCalculation", func(t *testing.T) {
// Get real gas prices from Arbitrum
gasPrice, err := client.SuggestGasPrice(context.Background())
require.NoError(t, err)
t.Logf("Current Arbitrum gas price: %s gwei", formatGwei(gasPrice))
// Test realistic arbitrage gas costs
baseGas := uint64(800000) // 800k gas for flash swap arbitrage
totalCost := new(big.Int).Mul(gasPrice, big.NewInt(int64(baseGas)))
// Add MEV premium (15x competitive)
mevPremium := big.NewInt(15)
competitiveCost := new(big.Int).Mul(totalCost, mevPremium)
t.Logf("Base gas cost: %s ETH", formatEther(totalCost))
t.Logf("Competitive MEV cost: %s ETH", formatEther(competitiveCost))
// Validate cost is reasonable for arbitrage
maxReasonableCost := big.NewInt(100000000000000000) // 0.1 ETH max
assert.True(t, competitiveCost.Cmp(maxReasonableCost) <= 0,
"MEV gas cost should be reasonable for profitable arbitrage")
})
t.Run("TestMEVCompetitionAnalysis", func(t *testing.T) {
analyzer := mev.NewCompetitionAnalyzer(client, log)
// Create realistic MEV opportunity
opportunity := &mev.MEVOpportunity{
OpportunityType: "arbitrage",
EstimatedProfit: big.NewInt(200000000000000000), // 0.2 ETH
RequiredGas: 800000,
}
// Analyze real competition
competition, err := analyzer.AnalyzeCompetition(context.Background(), opportunity)
require.NoError(t, err)
t.Logf("Competition analysis:")
t.Logf(" Competing bots: %d", competition.CompetingBots)
t.Logf(" Competition intensity: %.2f", competition.CompetitionIntensity)
t.Logf(" Highest priority fee: %s gwei", formatGwei(competition.HighestPriorityFee))
// Calculate optimal bid
bidStrategy, err := analyzer.CalculateOptimalBid(context.Background(), opportunity, competition)
require.NoError(t, err)
t.Logf("Optimal bidding strategy:")
t.Logf(" Priority fee: %s gwei", formatGwei(bidStrategy.PriorityFee))
t.Logf(" Total cost: %s ETH", formatEther(bidStrategy.TotalCost))
t.Logf(" Success probability: %.1f%%", bidStrategy.SuccessProbability*100)
// Validate profitability after competitive bidding
netProfit := new(big.Int).Sub(opportunity.EstimatedProfit, bidStrategy.TotalCost)
assert.True(t, netProfit.Sign() > 0, "Should remain profitable after competitive bidding")
t.Logf("✅ Net profit after competition: %s ETH", formatEther(netProfit))
})
}
// TestRealContractInteraction tests interaction with real Arbitrum contracts
func TestRealContractInteraction(t *testing.T) {
if testing.Short() {
t.Skip("Skipping real contract interaction test in short mode")
}
setupRealEnvironment(t)
client, err := ethclient.Dial(os.Getenv("ARBITRUM_RPC_ENDPOINT"))
require.NoError(t, err)
defer client.Close()
t.Run("TestUniswapV3PoolQuery", func(t *testing.T) {
// Test real Uniswap V3 WETH/USDC pool
poolAddress := common.HexToAddress("0xC31E54c7a869B9FcBEcc14363CF510d1c41fa443")
// Query pool state
poolData, err := queryUniswapV3Pool(client, poolAddress)
require.NoError(t, err)
t.Logf("WETH/USDC Pool State:")
t.Logf(" Token0: %s", poolData.Token0.Hex())
t.Logf(" Token1: %s", poolData.Token1.Hex())
t.Logf(" Fee: %d", poolData.Fee)
t.Logf(" Liquidity: %s", poolData.Liquidity.String())
t.Logf(" Current Price: %s", poolData.Price.String())
// Validate pool data
assert.NotEqual(t, common.Address{}, poolData.Token0, "Token0 should be valid")
assert.NotEqual(t, common.Address{}, poolData.Token1, "Token1 should be valid")
assert.True(t, poolData.Liquidity.Sign() > 0, "Pool should have liquidity")
})
t.Run("TestCamelotRouterQuery", func(t *testing.T) {
// Test real Camelot router
routerAddress := common.HexToAddress("0xc873fEcbd354f5A56E00E710B90EF4201db2448d")
// Query price for WETH -> USDC swap
weth := common.HexToAddress("0x82af49447d8a07e3bd95bd0d56f35241523fbab1")
usdc := common.HexToAddress("0xaf88d065e77c8cc2239327c5edb3a432268e5831")
price, err := queryCamelotPrice(client, routerAddress, weth, usdc, big.NewInt(1000000000000000000)) // 1 WETH
require.NoError(t, err)
t.Logf("Camelot WETH->USDC price: %s USDC for 1 WETH", price.String())
assert.True(t, price.Sign() > 0, "Should get positive USDC amount for WETH")
})
t.Run("TestTokenBalanceQuery", func(t *testing.T) {
// Test querying real token balances
wethAddress := common.HexToAddress("0x82af49447d8a07e3bd95bd0d56f35241523fbab1")
// Query WETH total supply (should be very large)
totalSupply, err := queryTokenSupply(client, wethAddress)
require.NoError(t, err)
t.Logf("WETH total supply: %s", totalSupply.String())
assert.True(t, totalSupply.Cmp(big.NewInt(1000000000000000000)) > 0, // > 1 WETH
"WETH should have significant total supply")
})
}
// TestProfitabilityUnderLoad tests profitability under realistic load
func TestProfitabilityUnderLoad(t *testing.T) {
if testing.Short() {
t.Skip("Skipping load test in short mode")
}
setupRealEnvironment(t)
client, err := ethclient.Dial(os.Getenv("ARBITRUM_RPC_ENDPOINT"))
require.NoError(t, err)
defer client.Close()
log := logger.New("info", "text", "")
t.Run("TestConcurrentOpportunityDetection", func(t *testing.T) {
// Test detecting opportunities concurrently (realistic scenario)
numWorkers := 5
opportunities := make(chan *types.ArbitrageOpportunity, 100)
// Start workers to detect opportunities
for i := 0; i < numWorkers; i++ {
go func(workerID int) {
defer func() {
if r := recover(); r != nil {
t.Errorf("Worker %d panicked: %v", workerID, r)
}
}()
for j := 0; j < 10; j++ { // Each worker checks 10 times
opps, err := detectRealArbitrageOpportunities(client,
common.HexToAddress("0xC31E54c7a869B9FcBEcc14363CF510d1c41fa443"), log)
if err == nil {
for _, opp := range opps {
select {
case opportunities <- opp:
default:
// Channel full, skip
}
}
}
time.Sleep(100 * time.Millisecond)
}
}(i)
}
// Collect results for 5 seconds
timeout := time.After(5 * time.Second)
var totalOpportunities int
var totalPotentialProfit *big.Int = big.NewInt(0)
collectLoop:
for {
select {
case opp := <-opportunities:
totalOpportunities++
totalPotentialProfit.Add(totalPotentialProfit, opp.Profit)
case <-timeout:
break collectLoop
}
}
t.Logf("Load test results:")
t.Logf(" Total opportunities detected: %d", totalOpportunities)
t.Logf(" Total potential profit: %s ETH", formatEther(totalPotentialProfit))
if totalOpportunities > 0 {
avgProfit := new(big.Int).Div(totalPotentialProfit, big.NewInt(int64(totalOpportunities)))
t.Logf(" Average profit per opportunity: %s ETH", formatEther(avgProfit))
}
})
t.Run("TestGasCostVariability", func(t *testing.T) {
// Test gas cost variations over time
var gasPrices []*big.Int
for i := 0; i < 10; i++ {
gasPrice, err := client.SuggestGasPrice(context.Background())
if err == nil {
gasPrices = append(gasPrices, gasPrice)
}
time.Sleep(500 * time.Millisecond)
}
if len(gasPrices) > 0 {
var total *big.Int = big.NewInt(0)
var min, max *big.Int = gasPrices[0], gasPrices[0]
for _, price := range gasPrices {
total.Add(total, price)
if price.Cmp(min) < 0 {
min = price
}
if price.Cmp(max) > 0 {
max = price
}
}
avg := new(big.Int).Div(total, big.NewInt(int64(len(gasPrices))))
t.Logf("Gas price variability:")
t.Logf(" Min: %s gwei", formatGwei(min))
t.Logf(" Max: %s gwei", formatGwei(max))
t.Logf(" Avg: %s gwei", formatGwei(avg))
// Validate gas prices are in reasonable range for Arbitrum
maxReasonable := big.NewInt(10000000000) // 10 gwei
assert.True(t, max.Cmp(maxReasonable) <= 0, "Gas prices should be reasonable for Arbitrum")
}
})
}
// TestSecurityUnderAttack tests security under realistic attack scenarios
func TestSecurityUnderAttack(t *testing.T) {
setupRealEnvironment(t)
t.Run("TestInvalidRPCEndpoints", func(t *testing.T) {
maliciousEndpoints := []string{
"http://malicious-rpc.evil.com",
"https://fake-arbitrum.scam.org",
"ws://localhost:1337", // Without localhost override
"ftp://invalid-scheme.com",
"",
}
for _, endpoint := range maliciousEndpoints {
err := validateRPCEndpoint(endpoint)
assert.Error(t, err, "Should reject malicious endpoint: %s", endpoint)
}
})
t.Run("TestKeyManagerSecurity", func(t *testing.T) {
// Test with various encryption key scenarios
testCases := []struct {
name string
encryptionKey string
shouldFail bool
}{
{"Empty key", "", true},
{"Short key", "short", true},
{"Weak key", "password123", true},
{"Strong key", "very-secure-encryption-key-32-chars", false},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
os.Setenv("MEV_BOT_ENCRYPTION_KEY", tc.encryptionKey)
defer os.Unsetenv("MEV_BOT_ENCRYPTION_KEY")
keyManagerConfig := &security.KeyManagerConfig{
KeystorePath: "test_keystore_security",
EncryptionKey: tc.encryptionKey,
KeyRotationDays: 30,
MaxSigningRate: 100,
SessionTimeout: time.Hour,
AuditLogPath: "test_audit_security.log",
BackupPath: "test_backups_security",
}
log := logger.New("debug", "text", "")
_, err := security.NewKeyManager(keyManagerConfig, log)
if tc.shouldFail {
assert.Error(t, err, "Should fail with %s", tc.name)
} else {
assert.NoError(t, err, "Should succeed with %s", tc.name)
}
// Clean up
os.RemoveAll("test_keystore_security")
os.Remove("test_audit_security.log")
os.RemoveAll("test_backups_security")
})
}
})
t.Run("TestInputValidationAttacks", func(t *testing.T) {
// Test various input attack scenarios
attackAmounts := []*big.Int{
big.NewInt(-1), // Negative
big.NewInt(0), // Zero
new(big.Int).Exp(big.NewInt(10), big.NewInt(50), nil), // Massive overflow
new(big.Int).Exp(big.NewInt(2), big.NewInt(256), nil), // 2^256 overflow
}
for i, amount := range attackAmounts {
err := validateAmount(amount)
assert.Error(t, err, "Should reject attack amount %d: %s", i, amount.String())
}
})
}
// Helper functions for real-world testing
func setupRealEnvironment(t *testing.T) {
// Set required environment variables for testing
if os.Getenv("ARBITRUM_RPC_ENDPOINT") == "" {
os.Setenv("ARBITRUM_RPC_ENDPOINT", "https://arb1.arbitrum.io/rpc")
}
if os.Getenv("MEV_BOT_ENCRYPTION_KEY") == "" {
os.Setenv("MEV_BOT_ENCRYPTION_KEY", "test-encryption-key-for-testing-32")
}
if os.Getenv("MEV_BOT_ALLOW_LOCALHOST") == "" {
os.Setenv("MEV_BOT_ALLOW_LOCALHOST", "false")
}
}
// TestOpportunity represents test-specific arbitrage data (extends canonical ArbitrageOpportunity)
type TestOpportunity struct {
*types.ArbitrageOpportunity
Pool common.Address
}
func detectRealArbitrageOpportunities(client *ethclient.Client, pool common.Address, log *logger.Logger) ([]*types.ArbitrageOpportunity, error) {
// Query real pool state and detect actual arbitrage opportunities
poolData, err := queryUniswapV3Pool(client, pool)
if err != nil {
return nil, err
}
// Compare with Camelot prices
camelotRouter := common.HexToAddress("0xc873fEcbd354f5A56E00E710B90EF4201db2448d")
testAmount := big.NewInt(1000000000000000000) // 1 WETH
camelotPrice, err := queryCamelotPrice(client, camelotRouter, poolData.Token0, poolData.Token1, testAmount)
if err != nil {
return nil, err
}
// Calculate potential arbitrage profit
uniswapPrice := poolData.Price
priceDiff := new(big.Int).Sub(camelotPrice, uniswapPrice)
var opportunities []*types.ArbitrageOpportunity
if priceDiff.Sign() > 0 {
// Potential arbitrage opportunity
minProfitThreshold := big.NewInt(50000000000000000) // 0.05 ETH
if priceDiff.Cmp(minProfitThreshold) >= 0 {
opportunity := &types.ArbitrageOpportunity{
Path: []string{poolData.Token0.Hex(), poolData.Token1.Hex()},
Pools: []string{pool.Hex()},
AmountIn: testAmount,
Profit: priceDiff,
NetProfit: priceDiff,
GasEstimate: big.NewInt(800000),
ROI: calculateROI(priceDiff, testAmount),
Protocol: "test-arbitrage",
ExecutionTime: 10000, // 10 seconds
Confidence: 0.8, // Test confidence
PriceImpact: 0.005, // 0.5% estimated
MaxSlippage: 0.01, // 1% max slippage
TokenIn: poolData.Token0,
TokenOut: poolData.Token1,
Timestamp: time.Now().Unix(),
Risk: 0.2, // Medium risk for test
}
opportunities = append(opportunities, opportunity)
}
}
return opportunities, nil
}
type PoolData struct {
Token0 common.Address
Token1 common.Address
Fee uint32
Liquidity *big.Int
Price *big.Int
}
func queryUniswapV3Pool(client *ethclient.Client, poolAddress common.Address) (*PoolData, error) {
// In a real implementation, this would query the actual Uniswap V3 pool contract
// For testing, we'll return mock data based on known pool structure
// WETH/USDC pool data (mock but realistic)
return &PoolData{
Token0: common.HexToAddress("0x82af49447d8a07e3bd95bd0d56f35241523fbab1"), // WETH
Token1: common.HexToAddress("0xaf88d065e77c8cc2239327c5edb3a432268e5831"), // USDC
Fee: 500, // 0.05%
Liquidity: big.NewInt(1000000000000000000000), // 1000 ETH equivalent
Price: big.NewInt(2000000000), // ~2000 USDC per ETH
}, nil
}
func queryCamelotPrice(client *ethclient.Client, router common.Address, tokenIn, tokenOut common.Address, amountIn *big.Int) (*big.Int, error) {
// In a real implementation, this would query the actual Camelot router
// For testing, we'll return a slightly different price to simulate arbitrage opportunity
// Simulate 0.1% price difference (arbitrage opportunity)
basePrice := big.NewInt(2000000000) // 2000 USDC
priceDiff := big.NewInt(2000000) // 0.1% difference = 2 USDC
return new(big.Int).Add(basePrice, priceDiff), nil
}
func queryTokenSupply(client *ethclient.Client, tokenAddress common.Address) (*big.Int, error) {
// In a real implementation, this would query the actual token contract
// For testing, return a realistic WETH total supply
return big.NewInt(1000000000000000000000000), nil // 1M WETH
}
func calculateROI(profit, investment *big.Int) float64 {
if investment.Sign() == 0 {
return 0
}
profitFloat := new(big.Float).SetInt(profit)
investmentFloat := new(big.Float).SetInt(investment)
roi := new(big.Float).Quo(profitFloat, investmentFloat)
roiFloat, _ := roi.Float64()
return roiFloat * 100 // Convert to percentage
}
func validateRPCEndpoint(endpoint string) error {
// Copy of the validation logic from main code
if endpoint == "" {
return fmt.Errorf("RPC endpoint cannot be empty")
}
u, err := url.Parse(endpoint)
if err != nil {
return fmt.Errorf("invalid RPC endpoint URL: %w", err)
}
switch u.Scheme {
case "http", "https", "ws", "wss":
// Valid schemes
default:
return fmt.Errorf("invalid RPC scheme: %s", u.Scheme)
}
if strings.Contains(u.Hostname(), "localhost") || strings.Contains(u.Hostname(), "127.0.0.1") {
if os.Getenv("MEV_BOT_ALLOW_LOCALHOST") != "true" {
return fmt.Errorf("localhost RPC endpoints not allowed")
}
}
if u.Hostname() == "" {
return fmt.Errorf("RPC endpoint must have a valid hostname")
}
return nil
}
func validateAmount(amount *big.Int) error {
if amount == nil || amount.Sign() <= 0 {
return fmt.Errorf("amount must be greater than zero")
}
maxAmount := new(big.Int).Exp(big.NewInt(10), big.NewInt(28), nil)
if amount.Cmp(maxAmount) > 0 {
return fmt.Errorf("amount exceeds maximum allowed value")
}
return nil
}
func formatEther(wei *big.Int) string {
if wei == nil {
return "0.000000"
}
eth := new(big.Float).SetInt(wei)
eth.Quo(eth, big.NewFloat(1e18))
return fmt.Sprintf("%.6f", eth)
}
func formatGwei(wei *big.Int) string {
if wei == nil {
return "0.0"
}
gwei := new(big.Float).SetInt(wei)
gwei.Quo(gwei, big.NewFloat(1e9))
return fmt.Sprintf("%.2f", gwei)
}

View File

@@ -0,0 +1,137 @@
//go:build integration
// +build integration
package integration_test
import (
"context"
"fmt"
"log"
"os/exec"
"runtime"
"testing"
"time"
"github.com/ethereum/go-ethereum/ethclient"
)
// setupForkedArbitrum sets up a forked Arbitrum test environment using anvil
func setupForkedArbitrum(t testing.TB) (*ethclient.Client, func()) {
// Check if anvil is available
if _, err := exec.LookPath("anvil"); err != nil {
t.Skip("anvil not found in PATH - install Foundry to run fork tests")
}
// Start anvil with Arbitrum fork
arbitrumRPC := "https://arb1.arbitrum.io/rpc"
port := "8545"
cmd := exec.Command("anvil",
"--fork-url", arbitrumRPC,
"--port", port,
"--gas-limit", "30000000",
"--gas-price", "10000000000", // 10 gwei
"--block-time", "1", // 1 second blocks
"--accounts", "10", // 10 test accounts
"--balance", "1000", // 1000 ETH per account
)
// Start anvil in background
if err := cmd.Start(); err != nil {
t.Fatalf("Failed to start anvil: %v", err)
}
// Wait for anvil to be ready
time.Sleep(3 * time.Second)
// Connect to the forked network
client, err := ethclient.Dial(fmt.Sprintf("http://localhost:%s", port))
if err != nil {
cmd.Process.Kill()
t.Fatalf("Failed to connect to forked Arbitrum: %v", err)
}
// Verify connection by getting chain ID
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
chainID, err := client.ChainID(ctx)
if err != nil {
cmd.Process.Kill()
t.Fatalf("Failed to get chain ID: %v", err)
}
if chainID.Uint64() != 42161 {
t.Logf("Warning: Expected Arbitrum chain ID 42161, got %d", chainID.Uint64())
}
// Return cleanup function
cleanup := func() {
client.Close()
if cmd.Process != nil {
cmd.Process.Kill()
cmd.Wait()
}
}
return client, cleanup
}
// getMemStats returns current memory statistics
func getMemStats() runtime.MemStats {
var m runtime.MemStats
runtime.ReadMemStats(&m)
return m
}
// logMemoryUsage logs current memory usage for debugging
func logMemoryUsage(t testing.TB, label string) {
var m runtime.MemStats
runtime.ReadMemStats(&m)
t.Logf("%s - Memory: Alloc=%d KB, TotalAlloc=%d KB, Sys=%d KB, NumGC=%d",
label,
m.Alloc/1024,
m.TotalAlloc/1024,
m.Sys/1024,
m.NumGC,
)
}
// waitForAnvil waits for anvil to be ready and responsive
func waitForAnvil(port string, timeout time.Duration) error {
deadline := time.Now().Add(timeout)
for time.Now().Before(deadline) {
client, err := ethclient.Dial(fmt.Sprintf("http://localhost:%s", port))
if err == nil {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
_, err := client.ChainID(ctx)
cancel()
client.Close()
if err == nil {
return nil
}
}
time.Sleep(500 * time.Millisecond)
}
return fmt.Errorf("anvil not ready after %v", timeout)
}
// createTestLogger creates a test logger for debugging
func createTestLogger(t testing.TB) *log.Logger {
return log.New(&testWriter{t: t}, "[TEST] ", log.LstdFlags|log.Lshortfile)
}
// testWriter implements io.Writer for test logging
type testWriter struct {
t testing.TB
}
func (tw *testWriter) Write(p []byte) (n int, err error) {
tw.t.Log(string(p))
return len(p), nil
}