378 lines
12 KiB
Markdown
378 lines
12 KiB
Markdown
# MEV Bot Mathematical Audit Report
|
|
## Comprehensive Review of Calculation Correctness
|
|
|
|
**Date**: November 1, 2025
|
|
**Scope**: All mathematical calculations in MEV bot
|
|
**Status**: CRITICAL ISSUES IDENTIFIED
|
|
|
|
---
|
|
|
|
## Executive Summary
|
|
|
|
The MEV bot contains **multiple critical mathematical errors** that could lead to:
|
|
- Incorrect profit calculations (overestimation)
|
|
- Invalid arbitrage execution decisions
|
|
- Gas cost underestimation
|
|
- Price impact miscalculations
|
|
- Precision loss and rounding errors
|
|
- Potential for negative profits despite calculations showing positive
|
|
|
|
**Severity**: HIGH - Immediate fixes required before production execution
|
|
|
|
---
|
|
|
|
## CRITICAL ISSUES FOUND
|
|
|
|
### 1. PROFIT CALCULATION - ROUNDING ERROR IN profit_calc.go
|
|
|
|
**Location**: `/home/administrator/projects/mev-beta/pkg/profitcalc/profit_calc.go:199-210`
|
|
|
|
**Issue**: Profit margin calculation uses integer division which truncates decimal places
|
|
|
|
```go
|
|
// PROBLEMATIC CODE (Line 199-210)
|
|
if amountOut.Sign() > 0 && amountOut.Cmp(big.NewFloat(0)) != 0 {
|
|
profitMargin := new(big.Float).Quo(netProfit, amountOut)
|
|
profitMarginFloat, _ := profitMargin.Float64()
|
|
// Ensure the profit margin is reasonable and not extremely high
|
|
if profitMarginFloat > 1.0 { // 100% profit margin is unrealistic
|
|
opportunity.ProfitMargin = 0.0 // Set to 0 if profit margin is unrealistic
|
|
opportunity.IsExecutable = false
|
|
opportunity.RejectReason = "unrealistic profit margin"
|
|
opportunity.Confidence = 0.0
|
|
} else {
|
|
opportunity.ProfitMargin = profitMarginFloat
|
|
}
|
|
}
|
|
```
|
|
|
|
**Problem**:
|
|
- The condition `profitMarginFloat > 1.0` is checking if profit margin exceeds 100%
|
|
- However, this is mathematically incorrect: a 1:1 profit (100% margin) IS possible in arbitrage
|
|
- This check REJECTS valid opportunities with 100%+ margins
|
|
- The code incorrectly assumes profitable arbitrage cannot exceed 100% margin
|
|
|
|
**Impact**: HIGH - Rejects all highly profitable arbitrage opportunities
|
|
|
|
**Fix Required**: Remove the artificial 100% profit margin cap and implement proper validation
|
|
|
|
---
|
|
|
|
### 2. SLIPPAGE CALCULATION ERROR - slippage_protection.go
|
|
|
|
**Location**: `/home/administrator/projects/mev-beta/pkg/profitcalc/slippage_protection.go:59-67`
|
|
|
|
**Issue**: Slippage formula is oversimplified and incorrect for actual AMM pools
|
|
|
|
```go
|
|
// PROBLEMATIC CODE (Line 59-67)
|
|
// Estimate slippage using simplified AMM formula
|
|
// For constant product AMMs: slippage ≈ trade_size / (2 * liquidity)
|
|
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 `tradeSize / (2 * liquidity)` is NOT the correct slippage formula
|
|
- Actual formula for Uniswap V2: `slippage = tradeSize / (2 * liquidity + tradeSize)` (more complex)
|
|
|
|
2. The curve adjustment `(1 + tradeSizeFloat)` is arbitrary
|
|
- When tradeSizeFloat = 0.5 (50% of pool), multiplier = 1.5
|
|
- This doesn't match actual AMM behavior
|
|
|
|
3. Does NOT account for:
|
|
- Fee structures (different by DEX)
|
|
- Token decimals
|
|
- Actual liquidity state changes
|
|
|
|
**Impact**: CRITICAL - Slippage estimates are wildly inaccurate, leading to:
|
|
- Overestimation of profit margins
|
|
- Approval of trades that will result in losses
|
|
- Risk assessment is completely unreliable
|
|
|
|
**Math Error**: Should use proper AMM invariant formulas instead of linear approximation
|
|
|
|
---
|
|
|
|
### 3. GAS COST CALCULATION - ARBITRARY BUFFER
|
|
|
|
**Location**: `/home/administrator/projects/mev-beta/pkg/profitcalc/profit_calc.go:271-273`
|
|
|
|
**Issue**: Gas cost calculation adds arbitrary 20% buffer
|
|
|
|
```go
|
|
// PROBLEMATIC CODE (Line 271-273)
|
|
// Add 20% buffer for MEV competition
|
|
buffer := new(big.Int).Div(gasCostWei, big.NewInt(5)) // 20%
|
|
gasCostWei.Add(gasCostWei, buffer)
|
|
```
|
|
|
|
**Problems**:
|
|
1. 20% buffer is ARBITRARY and not based on:
|
|
- Actual MEV competition on Arbitrum
|
|
- Historical gas price volatility
|
|
- Current network conditions
|
|
|
|
2. May be:
|
|
- TOO HIGH: Underestimates profitability unnecessarily
|
|
- TOO LOW: Insufficient for actual MEV competition
|
|
|
|
3. No dynamic adjustment based on:
|
|
- Network congestion
|
|
- Price impact size
|
|
- Complexity of transaction
|
|
|
|
**Impact**: MEDIUM - Profit calculations are systematically offset
|
|
|
|
**Fix**: Calculate actual expected gas based on transaction complexity and network state
|
|
|
|
---
|
|
|
|
### 4. PRICE IMPACT CALCULATION - MISSING FEE DEDUCTION
|
|
|
|
**Location**: `/home/administrator/projects/mev-beta/pkg/math/exchange_math.go:114-147`
|
|
|
|
**Issue**: Price impact is calculated WITHOUT considering trading fees in initial step
|
|
|
|
```go
|
|
// PROBLEMATIC CODE (Line 128-146)
|
|
// Calculate amount out
|
|
amountOut, err := u.CalculateAmountOut(amountIn, reserveIn, reserveOut, 3000)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
// New reserves after swap
|
|
newReserveIn := new(big.Int).Add(reserveIn, amountIn) // ← WRONG: doesn't account for fee deduction
|
|
newReserveOut := new(big.Int).Sub(reserveOut, amountOut)
|
|
|
|
// Price after = newReserveOut / newReserveIn
|
|
priceAfter := new(big.Float).Quo(new(big.Float).SetInt(newReserveOut), new(big.Float).SetInt(newReserveIn))
|
|
|
|
// Price impact = (priceBefore - priceAfter) / priceBefore
|
|
impact := new(big.Float).Sub(priceBefore, priceAfter)
|
|
impact.Quo(impact, priceBefore)
|
|
```
|
|
|
|
**Problem**:
|
|
- `newReserveIn` is calculated as `reserveIn + amountIn`
|
|
- But in reality, the input amount is `amountInWithFee` after fee deduction
|
|
- This causes price impact to be UNDERESTIMATED
|
|
- The actual price movement is larger than calculated
|
|
|
|
**Impact**: CRITICAL - Price impact is underestimated by 0.3-2% depending on fee tier
|
|
|
|
**Fix**: Use `amountInWithFee` in reserve calculation instead of raw `amountIn`
|
|
|
|
---
|
|
|
|
### 5. DIVISION BY ZERO RISK - Multiple Locations
|
|
|
|
**Location 1**: `/home/administrator/projects/mev-beta/pkg/profitcalc/slippage_protection.go:56`
|
|
|
|
```go
|
|
// PROBLEMATIC CODE
|
|
tradeSizeRatio := new(big.Float).Quo(tradeAmount, poolLiquidity)
|
|
```
|
|
|
|
**Issue**: If `poolLiquidity` is zero or extremely small, division fails silently
|
|
|
|
**Location 2**: `/home/administrator/projects/mev-beta/pkg/math/decimal_handler.go:308-312`
|
|
|
|
```go
|
|
// PROBLEMATIC CODE (Line 308-312)
|
|
// Scale numerator to maintain precision
|
|
totalDecimals := numerator.Decimals + resultDecimals
|
|
scalingFactor := dc.getScalingFactor(totalDecimals - denominator.Decimals)
|
|
|
|
scaledNumerator := new(big.Int).Mul(numerator.Value, scalingFactor)
|
|
quotient := new(big.Int).Div(scaledNumerator, denominator.Value) // ← No zero check
|
|
```
|
|
|
|
**Impact**: MEDIUM - Silent failures in calculations
|
|
|
|
---
|
|
|
|
### 6. OVERFLOW RISK IN MULTIPLICATION - arbitrage_calculator.go
|
|
|
|
**Location**: `/home/administrator/projects/mev-beta/pkg/math/arbitrage_calculator.go:278`
|
|
|
|
**Issue**: Large number multiplication without overflow checking
|
|
|
|
```go
|
|
// PROBLEMATIC CODE
|
|
product := new(big.Int).Mul(a.Value, b.Value)
|
|
|
|
// Adjust for decimal places
|
|
totalInputDecimals := a.Decimals + b.Decimals
|
|
```
|
|
|
|
**Problem**:
|
|
- While Go's `big.Int` can handle large numbers, the multiplication is done BEFORE overflow check
|
|
- For very large token amounts (18 decimals), multiplying two large numbers can be slow
|
|
- No explicit overflow detection before performing expensive multiplication
|
|
|
|
**Impact**: LOW - Go handles this, but inefficient
|
|
|
|
---
|
|
|
|
### 7. NEGATIVE VALUE HANDLING - decimal_handler.go
|
|
|
|
**Location**: `/home/administrator/projects/mev-beta/pkg/math/decimal_handler.go:54-58`
|
|
|
|
```go
|
|
// PROBLEMATIC CODE
|
|
if value != nil {
|
|
// Check for reasonable bounds - max value should not exceed what can be represented
|
|
// in financial calculations (roughly 2^256 / 10^18 for safety)
|
|
maxValue := new(big.Int)
|
|
maxValue.Exp(big.NewInt(10), big.NewInt(60), nil) // 10^60 max value for safety
|
|
|
|
absValue := new(big.Int).Abs(value) // ← Using Abs() is correct but...
|
|
```
|
|
|
|
**Problem**:
|
|
- Code correctly checks `Abs(value)` for upper bound
|
|
- But `NewUniversalDecimal` doesn't properly handle NEGATIVE values
|
|
- Negative amounts should not be allowed in profit calculations
|
|
- No check for negative amounts in critical functions
|
|
|
|
**Impact**: MEDIUM - Could allow invalid negative profit amounts
|
|
|
|
---
|
|
|
|
### 8. UNISWAP V3 PRICE CALCULATION - PRECISION LOSS
|
|
|
|
**Location**: `/home/administrator/projects/mev-beta/pkg/uniswap/pricing.go:22-45`
|
|
|
|
**Issue**: SqrtPriceX96 to Price conversion loses precision
|
|
|
|
```go
|
|
// PROBLEMATIC CODE
|
|
// Convert to big.Float for precision
|
|
sqrtPrice := new(big.Float).SetPrec(256).SetInt(sqrtPriceX96)
|
|
|
|
// Calculate sqrtPrice^2
|
|
price := new(big.Float).SetPrec(256)
|
|
price.Mul(sqrtPrice, sqrtPrice)
|
|
|
|
// Divide by 2^192 using global cached constant
|
|
denominator := new(big.Float).SetPrec(256).SetInt(GetQ192())
|
|
price.Quo(price, denominator)
|
|
```
|
|
|
|
**Problem**:
|
|
- Precision of 256 bits = ~77 decimal digits
|
|
- Actual prices are often in 10-18 decimal range
|
|
- Converting from `big.Int` to `big.Float` and back loses lower bits
|
|
- SetFloat64(0.0) for invalid input silently returns 0
|
|
|
|
**Impact**: MEDIUM - Slight precision loss in V3 price calculations
|
|
|
|
---
|
|
|
|
### 9. PROFIT THRESHOLD COMPARISON - TYPE MISMATCH
|
|
|
|
**Location**: `/home/administrator/projects/mev-beta/pkg/profitcalc/profit_calc.go:214-216`
|
|
|
|
```go
|
|
// PROBLEMATIC CODE
|
|
if netProfit.Sign() > 0 {
|
|
netProfitWei, _ := netProfit.Int(nil) // ← Converts big.Float to big.Int
|
|
if netProfitWei.Cmp(spc.minProfitThreshold) >= 0 {
|
|
```
|
|
|
|
**Problem**:
|
|
- `netProfit` is `*big.Float`
|
|
- Converting to `big.Int` truncates ALL decimal places
|
|
- For ETH with 18 decimals: 0.0001 ETH becomes 0 (100 wei becomes 0 in Int representation)
|
|
- This causes profits < 1 wei to be rejected as "below threshold"
|
|
|
|
**Impact**: CRITICAL - Legitimate small profit opportunities are rejected
|
|
|
|
**Example Bug**:
|
|
```
|
|
netProfit = 0.00005 ETH (50000 wei as float)
|
|
netProfitWei, _ := netProfit.Int(nil) // Returns 0 (rounds down)
|
|
netProfitWei.Cmp(0.01 ETH) >= 0 // FALSE - rejects valid opportunity
|
|
```
|
|
|
|
---
|
|
|
|
### 10. MISSING ROUND-UP IN AMOUNT CALCULATIONS
|
|
|
|
**Location**: `/home/administrator/projects/mev-beta/pkg/math/exchange_math.go:107-109`
|
|
|
|
```go
|
|
// PROBLEMATIC CODE
|
|
// amountIn = numerator / denominator + 1 (round up)
|
|
amountIn := new(big.Int).Div(numerator, denominator)
|
|
amountIn.Add(amountIn, big.NewInt(1))
|
|
```
|
|
|
|
**Problem**:
|
|
- Comment says "round up" but this ALWAYS adds 1
|
|
- Should use proper banker's rounding or conditional rounding
|
|
- Adding 1 to every result causes slight overestimation
|
|
|
|
**Impact**: LOW-MEDIUM - Slight overestimation of required input amounts
|
|
|
|
---
|
|
|
|
## SUMMARY TABLE
|
|
|
|
| Issue | Severity | Type | Impact |
|
|
|-------|----------|------|--------|
|
|
| 1. Profit margin cap at 100% | HIGH | Logic Error | Rejects valid opportunities |
|
|
| 2. Slippage formula incorrect | CRITICAL | Math Error | Wildly inaccurate slippage |
|
|
| 3. Gas cost buffer arbitrary | MEDIUM | Configuration | Systematic bias in profits |
|
|
| 4. Price impact misses fees | CRITICAL | Calculation Error | Underestimates price movement |
|
|
| 5. Division by zero risk | MEDIUM | Input Validation | Silent failures |
|
|
| 6. Overflow in multiplication | LOW | Performance | Slow for large numbers |
|
|
| 7. Negative value handling | MEDIUM | Validation | Invalid states allowed |
|
|
| 8. V3 price precision loss | MEDIUM | Precision | Slight accuracy loss |
|
|
| 9. Float to Int conversion | CRITICAL | Type Error | Legitimate profits rejected |
|
|
| 10. Always round up by 1 | LOW | Rounding | Slight overestimation |
|
|
|
|
---
|
|
|
|
## RECOMMENDATIONS
|
|
|
|
### Immediate (Critical Priority)
|
|
1. **Fix profit margin check** - Remove 100% cap, allow any positive profit
|
|
2. **Fix slippage calculation** - Implement proper AMM invariant formulas
|
|
3. **Fix profit threshold comparison** - Properly compare float values without truncation
|
|
4. **Fix price impact** - Use fee-adjusted amounts in reserve calculations
|
|
|
|
### Short-term (High Priority)
|
|
5. Add division-by-zero checks everywhere
|
|
6. Implement proper rounding strategies
|
|
7. Add overflow detection before large multiplications
|
|
8. Test against known Arbitrum DEX prices
|
|
|
|
### Medium-term (Medium Priority)
|
|
9. Dynamic gas cost calculation based on network state
|
|
10. More accurate slippage models for each DEX type
|
|
11. Proper handling of negative values and edge cases
|
|
|
|
---
|
|
|
|
## TESTING RECOMMENDATIONS
|
|
|
|
Create test cases for:
|
|
- Arbitrage with >100% profit margins
|
|
- Small profit amounts (<0.001 ETH)
|
|
- Very large trade sizes (>50% of pool)
|
|
- Extreme price impacts (>10%)
|
|
- Zero and near-zero liquidity
|
|
- Fee-adjusted amount calculations
|
|
|
|
---
|
|
|
|
Generated: November 1, 2025
|