diff --git a/pkg/events/parser.go b/pkg/events/parser.go index c0b7d6d..cea6538 100644 --- a/pkg/events/parser.go +++ b/pkg/events/parser.go @@ -18,6 +18,27 @@ import ( "github.com/fraktal/mev-beta/pkg/uniswap" ) +// parseSignedInt256 correctly parses a signed 256-bit integer from 32 bytes +// This is critical for UniswapV3 events which use int256 for amounts +func parseSignedInt256(data []byte) *big.Int { + if len(data) != 32 { + return big.NewInt(0) + } + + value := new(big.Int).SetBytes(data) + + // Check if the value is negative (MSB set) + if len(data) > 0 && data[0]&0x80 != 0 { + // Convert from two's complement + // Subtract 2^256 to get the negative value + maxUint256 := new(big.Int) + maxUint256.Lsh(big.NewInt(1), 256) + value.Sub(value, maxUint256) + } + + return value +} + // EventType represents the type of DEX event type EventType int @@ -388,21 +409,13 @@ func (ep *EventParser) parseUniswapV3Swap(log *types.Log, blockNumber uint64, ti return nil, fmt.Errorf("invalid Uniswap V3 Swap event log") } - // Parse the data fields - amount0 := new(big.Int).SetBytes(log.Data[0:32]) - amount1 := new(big.Int).SetBytes(log.Data[32:64]) + // Parse the data fields - UniswapV3 uses signed int256 for amounts + amount0 := parseSignedInt256(log.Data[0:32]) + amount1 := parseSignedInt256(log.Data[32:64]) sqrtPriceX96 := new(big.Int).SetBytes(log.Data[64:96]) liquidity := new(big.Int).SetBytes(log.Data[96:128]) tick := new(big.Int).SetBytes(log.Data[128:160]) - // Convert to signed values if needed - if amount0.Cmp(big.NewInt(0)) > 0x7fffffffffffffff { - amount0 = amount0.Sub(amount0, new(big.Int).Lsh(big.NewInt(1), 256)) - } - if amount1.Cmp(big.NewInt(0)) > 0x7fffffffffffffff { - amount1 = amount1.Sub(amount1, new(big.Int).Lsh(big.NewInt(1), 256)) - } - event := &Event{ Type: Swap, Protocol: "UniswapV3", diff --git a/pkg/pools/discovery.go b/pkg/pools/discovery.go index 5601297..7e7fb5b 100644 --- a/pkg/pools/discovery.go +++ b/pkg/pools/discovery.go @@ -21,6 +21,27 @@ import ( "github.com/fraktal/mev-beta/pkg/uniswap" ) +// parseSignedInt256 correctly parses a signed 256-bit integer from 32 bytes +// This is critical for UniswapV3 events which use int256 for amounts +func parseSignedInt256(data []byte) *big.Int { + if len(data) != 32 { + return big.NewInt(0) + } + + value := new(big.Int).SetBytes(data) + + // Check if the value is negative (MSB set) + if len(data) > 0 && data[0]&0x80 != 0 { + // Convert from two's complement + // Subtract 2^256 to get the negative value + maxUint256 := new(big.Int) + maxUint256.Lsh(big.NewInt(1), 256) + value.Sub(value, maxUint256) + } + + return value +} + // Pool represents a discovered liquidity pool type Pool struct { Address string `json:"address"` @@ -412,9 +433,12 @@ func (pd *PoolDiscovery) parseSwapData(data, protocol string) *SwapData { } case "UniswapV3": - // Uniswap V3 has different swap event structure - amountIn = new(big.Int).SetBytes(dataBytes[0:32]) - amountOut = new(big.Int).SetBytes(dataBytes[32:64]) + // Uniswap V3 uses signed int256 for amounts + amountIn = parseSignedInt256(dataBytes[0:32]) + amountOut = parseSignedInt256(dataBytes[32:64]) + // Convert to absolute values for display + amountIn = new(big.Int).Abs(amountIn) + amountOut = new(big.Int).Abs(amountOut) default: // Generic parsing @@ -485,7 +509,7 @@ func (pd *PoolDiscovery) handleLiquidityEvent(poolAddress string, topics []inter return } - eventData := pd.parseLiquidityData(data, eventType) + eventData := pd.parseLiquidityData(data, eventType, pool.Protocol) if eventData == nil { return } @@ -661,7 +685,7 @@ func (pd *PoolDiscovery) discoverPoolFromSwap(poolAddress, txHash string) { } // parseLiquidityData parses liquidity event data -func (pd *PoolDiscovery) parseLiquidityData(data, eventType string) *SwapData { +func (pd *PoolDiscovery) parseLiquidityData(data, eventType, protocol string) *SwapData { if len(data) < 2 { return nil } @@ -675,8 +699,20 @@ func (pd *PoolDiscovery) parseLiquidityData(data, eventType string) *SwapData { return nil } - amount0 := new(big.Int).SetBytes(dataBytes[0:32]) - amount1 := new(big.Int).SetBytes(dataBytes[32:64]) + var amount0, amount1 *big.Int + + // UniswapV3 uses signed int256 for liquidity amounts + if protocol == "UniswapV3" { + amount0 = parseSignedInt256(dataBytes[0:32]) + amount1 = parseSignedInt256(dataBytes[32:64]) + // Convert to absolute values + amount0 = new(big.Int).Abs(amount0) + amount1 = new(big.Int).Abs(amount1) + } else { + // V2 and others use unsigned uint256 + amount0 = new(big.Int).SetBytes(dataBytes[0:32]) + amount1 = new(big.Int).SetBytes(dataBytes[32:64]) + } return &SwapData{ AmountIn: amount0,