|
|
|
|
@@ -1,4 +1,4 @@
|
|
|
|
|
package integration
|
|
|
|
|
package integration_test
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
@@ -15,6 +15,7 @@ import (
|
|
|
|
|
"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"
|
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
|
)
|
|
|
|
|
@@ -46,11 +47,11 @@ func TestRealWorldProfitability(t *testing.T) {
|
|
|
|
|
t.Logf("✅ Found %d real arbitrage opportunities", len(opportunities))
|
|
|
|
|
|
|
|
|
|
for i, opp := range opportunities {
|
|
|
|
|
t.Logf("Opportunity %d: Profit=%s ETH, Gas=%d, ROI=%.2f%%",
|
|
|
|
|
i+1, formatEther(opp.EstimatedProfit), opp.RequiredGas, opp.ROI)
|
|
|
|
|
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.EstimatedProfit.Cmp(big.NewInt(50000000000000000)) >= 0, // 0.05 ETH min
|
|
|
|
|
assert.True(t, opp.Profit.Cmp(big.NewInt(50000000000000000)) >= 0, // 0.05 ETH min
|
|
|
|
|
"Opportunity should meet minimum profit threshold")
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
@@ -197,7 +198,7 @@ func TestProfitabilityUnderLoad(t *testing.T) {
|
|
|
|
|
t.Run("TestConcurrentOpportunityDetection", func(t *testing.T) {
|
|
|
|
|
// Test detecting opportunities concurrently (realistic scenario)
|
|
|
|
|
numWorkers := 5
|
|
|
|
|
opportunities := make(chan *ArbitrageOpportunity, 100)
|
|
|
|
|
opportunities := make(chan *types.ArbitrageOpportunity, 100)
|
|
|
|
|
|
|
|
|
|
// Start workers to detect opportunities
|
|
|
|
|
for i := 0; i < numWorkers; i++ {
|
|
|
|
|
@@ -235,7 +236,7 @@ func TestProfitabilityUnderLoad(t *testing.T) {
|
|
|
|
|
select {
|
|
|
|
|
case opp := <-opportunities:
|
|
|
|
|
totalOpportunities++
|
|
|
|
|
totalPotentialProfit.Add(totalPotentialProfit, opp.EstimatedProfit)
|
|
|
|
|
totalPotentialProfit.Add(totalPotentialProfit, opp.Profit)
|
|
|
|
|
case <-timeout:
|
|
|
|
|
break collectLoop
|
|
|
|
|
}
|
|
|
|
|
@@ -386,17 +387,13 @@ func setupRealEnvironment(t *testing.T) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type ArbitrageOpportunity struct {
|
|
|
|
|
Pool common.Address
|
|
|
|
|
Token0 common.Address
|
|
|
|
|
Token1 common.Address
|
|
|
|
|
EstimatedProfit *big.Int
|
|
|
|
|
RequiredGas uint64
|
|
|
|
|
ROI float64
|
|
|
|
|
PriceImpact float64
|
|
|
|
|
// 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) ([]*ArbitrageOpportunity, error) {
|
|
|
|
|
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 {
|
|
|
|
|
@@ -416,21 +413,30 @@ func detectRealArbitrageOpportunities(client *ethclient.Client, pool common.Addr
|
|
|
|
|
uniswapPrice := poolData.Price
|
|
|
|
|
priceDiff := new(big.Int).Sub(camelotPrice, uniswapPrice)
|
|
|
|
|
|
|
|
|
|
var opportunities []*ArbitrageOpportunity
|
|
|
|
|
var opportunities []*types.ArbitrageOpportunity
|
|
|
|
|
|
|
|
|
|
if priceDiff.Sign() > 0 {
|
|
|
|
|
// Potential arbitrage opportunity
|
|
|
|
|
minProfitThreshold := big.NewInt(50000000000000000) // 0.05 ETH
|
|
|
|
|
|
|
|
|
|
if priceDiff.Cmp(minProfitThreshold) >= 0 {
|
|
|
|
|
opportunity := &ArbitrageOpportunity{
|
|
|
|
|
Pool: pool,
|
|
|
|
|
Token0: poolData.Token0,
|
|
|
|
|
Token1: poolData.Token1,
|
|
|
|
|
EstimatedProfit: priceDiff,
|
|
|
|
|
RequiredGas: 800000,
|
|
|
|
|
ROI: calculateROI(priceDiff, testAmount),
|
|
|
|
|
PriceImpact: 0.005, // 0.5% estimated
|
|
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
|