365 lines
11 KiB
Markdown
365 lines
11 KiB
Markdown
# Mathematical Fixes - Code Examples
|
|
|
|
## Issue #1: Profit Margin Cap at 100%
|
|
|
|
### BEFORE (WRONG):
|
|
```go
|
|
// profit_calc.go: Lines 199-210
|
|
if profitMarginFloat > 1.0 { // 100% profit margin is unrealistic
|
|
opportunity.ProfitMargin = 0.0
|
|
opportunity.IsExecutable = false
|
|
opportunity.RejectReason = "unrealistic profit margin"
|
|
opportunity.Confidence = 0.0
|
|
} else {
|
|
opportunity.ProfitMargin = profitMarginFloat
|
|
}
|
|
```
|
|
|
|
**Problem**: This rejects ALL opportunities with >100% profit margin, which is mathematically possible in arbitrage.
|
|
|
|
### AFTER (CORRECT):
|
|
```go
|
|
// Remove the arbitrary 100% cap
|
|
// Only reject if profit is negative or margin is NaN
|
|
if math.IsNaN(profitMarginFloat) {
|
|
opportunity.ProfitMargin = 0.0
|
|
opportunity.IsExecutable = false
|
|
opportunity.RejectReason = "invalid profit margin calculation"
|
|
opportunity.Confidence = 0.0
|
|
} else if profitMarginFloat < 0 {
|
|
opportunity.ProfitMargin = 0.0
|
|
opportunity.IsExecutable = false
|
|
opportunity.RejectReason = "negative profit margin"
|
|
opportunity.Confidence = 0.0
|
|
} else {
|
|
opportunity.ProfitMargin = profitMarginFloat
|
|
// Confidence increases with margin
|
|
if profitMarginFloat > 0.5 { // 50% margin
|
|
opportunity.Confidence += 0.2
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Issue #2: Slippage Calculation Formula
|
|
|
|
### BEFORE (WRONG):
|
|
```go
|
|
// slippage_protection.go: Lines 59-67
|
|
estimatedSlippage := tradeSizeFloat / 2.0
|
|
|
|
// Apply curve adjustment for larger trades (non-linear slippage)
|
|
if tradeSizeFloat > 0.1 { // > 10% of pool
|
|
// Quadratic increase for large trades
|
|
estimatedSlippage = estimatedSlippage * (1 + tradeSizeFloat)
|
|
}
|
|
```
|
|
|
|
**Problems**:
|
|
1. Formula is not based on actual AMM mechanics
|
|
2. The "curve adjustment" is arbitrary
|
|
3. Doesn't account for fees
|
|
|
|
### AFTER (CORRECT):
|
|
```go
|
|
// Implement proper Uniswap V2 constant product formula
|
|
// For swap: amountOut = (amountIn * 997 * reserveOut) / (reserveIn * 1000 + amountIn * 997)
|
|
// Price impact = 1 - (amountOut / (amountIn * spotPrice))
|
|
|
|
func (sp *SlippageProtector) CalculateAccurateSlippage(
|
|
amountIn *big.Float,
|
|
reserveIn *big.Float,
|
|
reserveOut *big.Float,
|
|
fee uint32, // In basis points, e.g., 3000 for 0.3%
|
|
) float64 {
|
|
if amountIn.Sign() <= 0 || reserveIn.Sign() <= 0 || reserveOut.Sign() <= 0 {
|
|
return 0
|
|
}
|
|
|
|
// Apply fee: amountInWithFee = amountIn * (10000 - fee) / 10000
|
|
feeMultiplier := new(big.Float).SetInt64(int64(10000 - fee))
|
|
tenThousand := new(big.Float).SetInt64(10000)
|
|
feeFactor := new(big.Float).Quo(feeMultiplier, tenThousand)
|
|
|
|
amountInWithFee := new(big.Float).Mul(amountIn, feeFactor)
|
|
|
|
// Numerator = amountInWithFee * reserveOut
|
|
numerator := new(big.Float).Mul(amountInWithFee, reserveOut)
|
|
|
|
// Denominator = reserveIn * 10000 + amountInWithFee
|
|
reserveInTenK := new(big.Float).Mul(reserveIn, tenThousand)
|
|
denominator := new(big.Float).Add(reserveInTenK, amountInWithFee)
|
|
|
|
// amountOut = numerator / denominator
|
|
amountOut := new(big.Float).Quo(numerator, denominator)
|
|
|
|
// Spot price = reserveOut / reserveIn
|
|
spotPrice := new(big.Float).Quo(reserveOut, reserveIn)
|
|
|
|
// Expected output = amountIn * spotPrice
|
|
expectedOut := new(big.Float).Mul(amountIn, spotPrice)
|
|
|
|
// Slippage = (expectedOut - amountOut) / expectedOut
|
|
if expectedOut.Sign() <= 0 {
|
|
return 0
|
|
}
|
|
|
|
diff := new(big.Float).Sub(expectedOut, amountOut)
|
|
slippageRatio := new(big.Float).Quo(diff, expectedOut)
|
|
|
|
slippage, _ := slippageRatio.Float64()
|
|
if slippage < 0 {
|
|
return 0
|
|
}
|
|
if slippage > 1.0 {
|
|
return 1.0
|
|
}
|
|
return slippage
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Issue #3: Float to Int Conversion for Profit Comparison
|
|
|
|
### BEFORE (WRONG):
|
|
```go
|
|
// profit_calc.go: Lines 214-216
|
|
if netProfit.Sign() > 0 {
|
|
netProfitWei, _ := netProfit.Int(nil) // Truncates decimal places!
|
|
if netProfitWei.Cmp(spc.minProfitThreshold) >= 0 {
|
|
// Execute opportunity
|
|
}
|
|
}
|
|
```
|
|
|
|
**Problem**:
|
|
- Converts `big.Float` to `big.Int`, truncating all decimal places
|
|
- 0.00005 ETH becomes 0 when converted to Int
|
|
- Valid opportunities are incorrectly rejected
|
|
|
|
### AFTER (CORRECT):
|
|
```go
|
|
// profit_calc.go: Keep netProfit as big.Float
|
|
if netProfit.Sign() > 0 {
|
|
minThresholdFloat := new(big.Float).SetInt(spc.minProfitThreshold)
|
|
minThresholdFloat.Quo(minThresholdFloat, new(big.Float).SetInt64(1e18)) // Convert wei to ETH
|
|
|
|
if netProfit.Cmp(minThresholdFloat) >= 0 {
|
|
// Execute opportunity
|
|
}
|
|
}
|
|
|
|
// Or better yet, use UniversalDecimal throughout:
|
|
netProfitDecimal := &math.UniversalDecimal{
|
|
Value: netProfit.Int(nil), // Only convert when absolutely necessary
|
|
Decimals: 18,
|
|
Symbol: "ETH",
|
|
}
|
|
|
|
if netProfitDecimal.Cmp(spc.minProfitThresholdDecimal) >= 0 {
|
|
// Execute opportunity
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Issue #4: Price Impact Missing Fee Adjustment
|
|
|
|
### BEFORE (WRONG):
|
|
```go
|
|
// exchange_math.go: Lines 128-146
|
|
amountOut, err := u.CalculateAmountOut(amountIn, reserveIn, reserveOut, 3000)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
// WRONG: Using raw amountIn instead of amountInWithFee
|
|
newReserveIn := new(big.Int).Add(reserveIn, amountIn) // ← BUG HERE
|
|
newReserveOut := new(big.Int).Sub(reserveOut, amountOut)
|
|
|
|
priceAfter := new(big.Float).Quo(new(big.Float).SetInt(newReserveOut), new(big.Float).SetInt(newReserveIn))
|
|
```
|
|
|
|
**Problem**: Uses raw `amountIn` instead of the fee-adjusted amount, underestimating price impact
|
|
|
|
### AFTER (CORRECT):
|
|
```go
|
|
// exchange_math.go: Lines 128-146
|
|
amountOut, err := u.CalculateAmountOut(amountIn, reserveIn, reserveOut, 3000)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
// Apply fee BEFORE calculating new reserves
|
|
if fee == 0 {
|
|
fee = 3000
|
|
}
|
|
feeFactor := big.NewInt(int64(10000 - fee))
|
|
amountInWithFee := new(big.Int).Mul(amountIn, feeFactor)
|
|
amountInWithFee.Div(amountInWithFee, big.NewInt(10000))
|
|
|
|
// Use fee-adjusted amount in reserve calculation
|
|
newReserveIn := new(big.Int).Add(reserveIn, amountInWithFee) // ← FIXED
|
|
newReserveOut := new(big.Int).Sub(reserveOut, amountOut)
|
|
|
|
priceAfter := new(big.Float).Quo(new(big.Float).SetInt(newReserveOut), new(big.Float).SetInt(newReserveIn))
|
|
```
|
|
|
|
---
|
|
|
|
## Issue #5: Division by Zero Protection
|
|
|
|
### BEFORE (WRONG):
|
|
```go
|
|
// slippage_protection.go: Line 56
|
|
tradeSizeRatio := new(big.Float).Quo(tradeAmount, poolLiquidity)
|
|
```
|
|
|
|
**Problem**: No check if `poolLiquidity` is zero
|
|
|
|
### AFTER (CORRECT):
|
|
```go
|
|
// Add proper validation
|
|
if poolLiquidity == nil || poolLiquidity.Sign() <= 0 {
|
|
return &SlippageAnalysis{
|
|
IsAcceptable: false,
|
|
RiskLevel: "Extreme",
|
|
Recommendation: "Invalid pool liquidity for slippage calculation",
|
|
}
|
|
}
|
|
|
|
tradeSizeRatio := new(big.Float).Quo(tradeAmount, poolLiquidity)
|
|
```
|
|
|
|
---
|
|
|
|
## Issue #6: Arbitrary Gas Cost Buffer
|
|
|
|
### BEFORE (WRONG):
|
|
```go
|
|
// profit_calc.go: Lines 271-273
|
|
// Add 20% buffer for MEV competition
|
|
buffer := new(big.Int).Div(gasCostWei, big.NewInt(5)) // 20%
|
|
gasCostWei.Add(gasCostWei, buffer)
|
|
```
|
|
|
|
**Problem**: 20% is arbitrary, not based on actual network conditions
|
|
|
|
### AFTER (CORRECT):
|
|
```go
|
|
// Dynamic gas adjustment based on network state
|
|
func (spc *ProfitCalculator) calculateGasCostWithNetworkAdjustment() *big.Float {
|
|
gasLimitInt64, _ := security.SafeUint64ToInt64(spc.gasLimit)
|
|
gasLimit := big.NewInt(gasLimitInt64)
|
|
currentGasPrice := spc.GetCurrentGasPrice()
|
|
gasCostWei := new(big.Int).Mul(currentGasPrice, gasLimit)
|
|
|
|
// Dynamic buffer based on gas price volatility
|
|
gasPrice := spc.GetCurrentGasPrice()
|
|
|
|
// Get recent gas price trend (this would need implementation)
|
|
recentAvgGasPrice := spc.GetRecentAverageGasPrice() // Needs implementation
|
|
|
|
if gasPrice == nil || recentAvgGasPrice == nil || recentAvgGasPrice.Sign() == 0 {
|
|
// Default conservative estimate
|
|
buffer := new(big.Int).Div(gasCostWei, big.NewInt(5)) // 20%
|
|
gasCostWei.Add(gasCostWei, buffer)
|
|
} else {
|
|
// Calculate buffer as percentage of price change
|
|
priceDiff := new(big.Int).Sub(gasPrice, recentAvgGasPrice)
|
|
if priceDiff.Sign() > 0 {
|
|
// Gas price is above average - add 10-30% buffer
|
|
bufferPercent := calculateBufferPercent(priceDiff, recentAvgGasPrice)
|
|
buffer := new(big.Int).Mul(gasCostWei, big.NewInt(int64(bufferPercent)))
|
|
buffer.Div(buffer, big.NewInt(100))
|
|
gasCostWei.Add(gasCostWei, buffer)
|
|
} else {
|
|
// Gas price is below average - use minimal buffer
|
|
buffer := new(big.Int).Div(gasCostWei, big.NewInt(20)) // 5%
|
|
gasCostWei.Add(gasCostWei, buffer)
|
|
}
|
|
}
|
|
|
|
// Convert to big.Float for easier calculation
|
|
gasCostFloat := new(big.Float).SetInt(gasCostWei)
|
|
etherDenominator := new(big.Float).SetInt(big.NewInt(1e18))
|
|
return new(big.Float).Quo(gasCostFloat, etherDenominator)
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Issue #7: Proper Rounding for Amount Calculations
|
|
|
|
### BEFORE (WRONG):
|
|
```go
|
|
// exchange_math.go: Lines 107-109
|
|
// amountIn = numerator / denominator + 1 (round up)
|
|
amountIn := new(big.Int).Div(numerator, denominator)
|
|
amountIn.Add(amountIn, big.NewInt(1)) // Always adds 1
|
|
```
|
|
|
|
**Problem**: Always adds 1, even when exact division occurs
|
|
|
|
### AFTER (CORRECT):
|
|
```go
|
|
// Implement proper banker's rounding (round-half-to-even)
|
|
amountIn := new(big.Int).Div(numerator, denominator)
|
|
|
|
// Check if there's a remainder
|
|
remainder := new(big.Int).Mod(numerator, denominator)
|
|
if remainder.Sign() > 0 {
|
|
// There's a remainder, round up
|
|
amountIn.Add(amountIn, big.NewInt(1))
|
|
}
|
|
|
|
// Alternative: Always round up for safety
|
|
amountIn := new(big.Int).Add(numerator, new(big.Int).Sub(denominator, big.NewInt(1)))
|
|
amountIn.Div(amountIn, denominator)
|
|
```
|
|
|
|
---
|
|
|
|
## Testing the Fixes
|
|
|
|
Add these test cases:
|
|
|
|
```go
|
|
func TestProfitMarginWithHighReturns(t *testing.T) {
|
|
// Should allow 200% profit margin
|
|
profitMargin := 2.0 // 200%
|
|
|
|
// Should NOT be rejected
|
|
if profitMargin > 1.0 {
|
|
t.Fatal("Valid 200% profit margin incorrectly rejected")
|
|
}
|
|
}
|
|
|
|
func TestSlippageCalculation(t *testing.T) {
|
|
// Test against known Uniswap V2 swap
|
|
amountIn, _ := new(big.Float).SetString("1000000000000000000") // 1 ETH
|
|
reserveIn, _ := new(big.Float).SetString("10000000000000000000") // 10 ETH
|
|
reserveOut, _ := new(big.Float).SetString("100000000000000000000") // 100 tokens
|
|
|
|
slippage := CalculateAccurateSlippage(amountIn, reserveIn, reserveOut, 3000)
|
|
|
|
// Expected: ~8.26% slippage for this swap
|
|
if slippage < 0.08 || slippage > 0.09 {
|
|
t.Errorf("Slippage calculation incorrect: got %f, expected ~0.0826", slippage)
|
|
}
|
|
}
|
|
|
|
func TestProfitThresholdComparison(t *testing.T) {
|
|
// Should accept profits > 0 wei, even very small ones
|
|
netProfit := big.NewFloat(0.00000001) // 0.00000001 ETH (10 wei)
|
|
minThreshold := big.NewFloat(0.00000000) // 0 wei
|
|
|
|
if netProfit.Cmp(minThreshold) <= 0 {
|
|
t.Fatal("Valid small profit incorrectly rejected")
|
|
}
|
|
}
|
|
```
|
|
|