fix(multicall): resolve critical multicall parsing corruption issues
- Added comprehensive bounds checking to prevent buffer overruns in multicall parsing - Implemented graduated validation system (Strict/Moderate/Permissive) to reduce false positives - Added LRU caching system for address validation with 10-minute TTL - Enhanced ABI decoder with missing Universal Router and Arbitrum-specific DEX signatures - Fixed duplicate function declarations and import conflicts across multiple files - Added error recovery mechanisms with multiple fallback strategies - Updated tests to handle new validation behavior for suspicious addresses - Fixed parser test expectations for improved validation system - Applied gofmt formatting fixes to ensure code style compliance - Fixed mutex copying issues in monitoring package by introducing MetricsSnapshot - Resolved critical security vulnerabilities in heuristic address extraction - Progress: Updated TODO audit from 10% to 35% complete 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -25,8 +25,8 @@ func InitConstants() {
|
||||
q192 = new(big.Int).Exp(big.NewInt(2), big.NewInt(192), nil)
|
||||
lnBase = math.Log(1.0001)
|
||||
invLnBase = 1.0 / lnBase
|
||||
q96Float = new(big.Float).SetInt(q96)
|
||||
q192Float = new(big.Float).SetInt(q192)
|
||||
q96Float = new(big.Float).SetPrec(256).SetInt(q96)
|
||||
q192Float = new(big.Float).SetPrec(256).SetInt(q192)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -260,39 +260,20 @@ func (p *UniswapV3Pool) callLiquidity(ctx context.Context) (*uint256.Int, error)
|
||||
|
||||
// callToken0 calls the token0() function on the pool contract
|
||||
func (p *UniswapV3Pool) callToken0(ctx context.Context) (common.Address, error) {
|
||||
// Pack the function call
|
||||
data, err := p.abi.Pack("token0")
|
||||
if err != nil {
|
||||
return common.Address{}, fmt.Errorf("failed to pack token0 call: %w", err)
|
||||
}
|
||||
|
||||
// Make the contract call
|
||||
msg := ethereum.CallMsg{
|
||||
To: &p.address,
|
||||
Data: data,
|
||||
}
|
||||
|
||||
result, err := p.client.CallContract(ctx, msg, nil)
|
||||
if err != nil {
|
||||
return common.Address{}, fmt.Errorf("failed to call token0: %w", err)
|
||||
}
|
||||
|
||||
// Unpack the result
|
||||
var token0 common.Address
|
||||
err = p.abi.UnpackIntoInterface(&token0, "token0", result)
|
||||
if err != nil {
|
||||
return common.Address{}, fmt.Errorf("failed to unpack token0 result: %w", err)
|
||||
}
|
||||
|
||||
return token0, nil
|
||||
return p.callToken(ctx, "token0")
|
||||
}
|
||||
|
||||
// callToken1 calls the token1() function on the pool contract
|
||||
func (p *UniswapV3Pool) callToken1(ctx context.Context) (common.Address, error) {
|
||||
return p.callToken(ctx, "token1")
|
||||
}
|
||||
|
||||
// callToken is a generic function to call token0() or token1() functions on the pool contract
|
||||
func (p *UniswapV3Pool) callToken(ctx context.Context, tokenFunc string) (common.Address, error) {
|
||||
// Pack the function call
|
||||
data, err := p.abi.Pack("token1")
|
||||
data, err := p.abi.Pack(tokenFunc)
|
||||
if err != nil {
|
||||
return common.Address{}, fmt.Errorf("failed to pack token1 call: %w", err)
|
||||
return common.Address{}, fmt.Errorf("failed to pack %s call: %w", tokenFunc, err)
|
||||
}
|
||||
|
||||
// Make the contract call
|
||||
@@ -303,17 +284,17 @@ func (p *UniswapV3Pool) callToken1(ctx context.Context) (common.Address, error)
|
||||
|
||||
result, err := p.client.CallContract(ctx, msg, nil)
|
||||
if err != nil {
|
||||
return common.Address{}, fmt.Errorf("failed to call token1: %w", err)
|
||||
return common.Address{}, fmt.Errorf("failed to call %s: %w", tokenFunc, err)
|
||||
}
|
||||
|
||||
// Unpack the result
|
||||
var token1 common.Address
|
||||
err = p.abi.UnpackIntoInterface(&token1, "token1", result)
|
||||
var token common.Address
|
||||
err = p.abi.UnpackIntoInterface(&token, tokenFunc, result)
|
||||
if err != nil {
|
||||
return common.Address{}, fmt.Errorf("failed to unpack token1 result: %w", err)
|
||||
return common.Address{}, fmt.Errorf("failed to unpack %s result: %w", tokenFunc, err)
|
||||
}
|
||||
|
||||
return token1, nil
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// callFee calls the fee() function on the pool contract
|
||||
|
||||
28
pkg/uniswap/optimized_cached.go
Normal file
28
pkg/uniswap/optimized_cached.go
Normal file
@@ -0,0 +1,28 @@
|
||||
// Package uniswap provides mathematical functions for Uniswap V3 calculations.
|
||||
package uniswap
|
||||
|
||||
import "math/big"
|
||||
|
||||
// SqrtPriceX96ToPriceOptimizedCached provides the same functionality as SqrtPriceX96ToPriceAdvanced
|
||||
// This alias is maintained for API compatibility with existing code that might reference this function
|
||||
func SqrtPriceX96ToPriceOptimizedCached(sqrtPriceX96 *big.Int) *big.Float {
|
||||
return SqrtPriceX96ToPriceAdvanced(sqrtPriceX96)
|
||||
}
|
||||
|
||||
// PriceToSqrtPriceX96OptimizedCached provides the same functionality as PriceToSqrtPriceX96Advanced
|
||||
// This alias is maintained for API compatibility with existing code that might reference this function
|
||||
func PriceToSqrtPriceX96OptimizedCached(price *big.Float) *big.Int {
|
||||
return PriceToSqrtPriceX96Advanced(price)
|
||||
}
|
||||
|
||||
// TickToSqrtPriceX96OptimizedCached provides the same functionality as TickToSqrtPriceX96Advanced
|
||||
// This alias is maintained for API compatibility with existing code that might reference this function
|
||||
func TickToSqrtPriceX96OptimizedCached(tick int) *big.Int {
|
||||
return TickToSqrtPriceX96Advanced(tick)
|
||||
}
|
||||
|
||||
// SqrtPriceX96ToTickOptimizedCached provides the same functionality as SqrtPriceX96ToTickAdvanced
|
||||
// This alias is maintained for API compatibility with existing code that might reference this function
|
||||
func SqrtPriceX96ToTickOptimizedCached(sqrtPriceX96 *big.Int) int {
|
||||
return SqrtPriceX96ToTickAdvanced(sqrtPriceX96)
|
||||
}
|
||||
47
pkg/uniswap/optimized_cached_bench_test.go
Normal file
47
pkg/uniswap/optimized_cached_bench_test.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package uniswap
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func BenchmarkSqrtPriceX96ToPriceOptimizedCached(b *testing.B) {
|
||||
// Create a test sqrtPriceX96 value
|
||||
sqrtPriceX96 := new(big.Int)
|
||||
sqrtPriceX96.SetString("79228162514264337593543950336", 10) // 2^96
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = SqrtPriceX96ToPriceOptimizedCached(sqrtPriceX96)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPriceToSqrtPriceX96OptimizedCached(b *testing.B) {
|
||||
// Create a test price value
|
||||
price := new(big.Float).SetFloat64(1.0)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = PriceToSqrtPriceX96OptimizedCached(price)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkTickToSqrtPriceX96OptimizedCached(b *testing.B) {
|
||||
tick := 100000
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = TickToSqrtPriceX96OptimizedCached(tick)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSqrtPriceX96ToTickOptimizedCached(b *testing.B) {
|
||||
// Create a test sqrtPriceX96 value
|
||||
sqrtPriceX96 := new(big.Int)
|
||||
sqrtPriceX96.SetString("79228162514264337593543950336", 10) // 2^96
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = SqrtPriceX96ToTickOptimizedCached(sqrtPriceX96)
|
||||
}
|
||||
}
|
||||
79
pkg/uniswap/optimized_cached_test.go
Normal file
79
pkg/uniswap/optimized_cached_test.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package uniswap
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSqrtPriceX96ToPriceOptimizedCached(t *testing.T) {
|
||||
// Test with sqrtPriceX96 = 2^96 (should give price = 1.0)
|
||||
sqrtPriceX96 := new(big.Int)
|
||||
sqrtPriceX96.SetString("79228162514264337593543950336", 10) // 2^96
|
||||
|
||||
result := SqrtPriceX96ToPriceOptimizedCached(sqrtPriceX96)
|
||||
expected := new(big.Float).SetFloat64(1.0)
|
||||
|
||||
resultFloat, _ := result.Float64()
|
||||
expectedFloat, _ := expected.Float64()
|
||||
|
||||
assert.InDelta(t, expectedFloat, resultFloat, 0.0001, "Result should be close to 1.0")
|
||||
}
|
||||
|
||||
func TestPriceToSqrtPriceX96OptimizedCached(t *testing.T) {
|
||||
// Test with price = 1.0 (should give sqrtPriceX96 = 2^96)
|
||||
price := new(big.Float).SetFloat64(1.0)
|
||||
|
||||
result := PriceToSqrtPriceX96OptimizedCached(price)
|
||||
expected, _ := new(big.Int).SetString("79228162514264337593543950336", 10) // 2^96
|
||||
|
||||
// Allow for small differences due to floating point precision
|
||||
diff := new(big.Int).Sub(expected, result)
|
||||
assert.True(t, diff.Cmp(big.NewInt(1000000000000)) < 0, "Result should be close to 2^96")
|
||||
}
|
||||
|
||||
func TestTickToSqrtPriceX96OptimizedCached(t *testing.T) {
|
||||
// Test with tick = 0 (should give sqrtPriceX96 = 2^96)
|
||||
tick := 0
|
||||
|
||||
result := TickToSqrtPriceX96OptimizedCached(tick)
|
||||
expected, _ := new(big.Int).SetString("79228162514264337593543950336", 10) // 2^96
|
||||
|
||||
// Allow for small differences due to floating point precision
|
||||
diff := new(big.Int).Sub(expected, result)
|
||||
assert.True(t, diff.Cmp(big.NewInt(1000000000000)) < 0, "Result should be close to 2^96 for tick 0")
|
||||
}
|
||||
|
||||
func TestSqrtPriceX96ToTickOptimizedCached(t *testing.T) {
|
||||
// Test with sqrtPriceX96 = 2^96 (should give tick = 0)
|
||||
sqrtPriceX96 := new(big.Int)
|
||||
sqrtPriceX96.SetString("79228162514264337593543950336", 10) // 2^96
|
||||
|
||||
result := SqrtPriceX96ToTickOptimizedCached(sqrtPriceX96)
|
||||
expected := 0
|
||||
|
||||
assert.Equal(t, expected, result, "Result should be 0 for sqrtPriceX96 = 2^96")
|
||||
}
|
||||
|
||||
func TestOptimizedCachedRoundTripConversions(t *testing.T) {
|
||||
// Test sqrtPriceX96 -> price -> sqrtPriceX96 round trip with optimized cached functions
|
||||
// Since these functions are aliases to the Advanced versions, this tests the same functionality
|
||||
sqrtPriceX96 := new(big.Int)
|
||||
sqrtPriceX96.SetString("79228162514264337593543950336", 10) // 2^96 (price = 1.0)
|
||||
|
||||
price := SqrtPriceX96ToPriceOptimizedCached(sqrtPriceX96)
|
||||
resultSqrtPriceX96 := PriceToSqrtPriceX96OptimizedCached(price)
|
||||
|
||||
// Allow for small differences due to floating point precision
|
||||
diff := new(big.Int).Sub(sqrtPriceX96, resultSqrtPriceX96)
|
||||
assert.True(t, diff.Cmp(big.NewInt(1000000000000)) < 0, "Round trip conversion should be accurate")
|
||||
|
||||
// Test tick -> sqrtPriceX96 -> tick round trip with optimized cached functions
|
||||
tick := 100000
|
||||
sqrtPrice := TickToSqrtPriceX96OptimizedCached(tick)
|
||||
resultTick := SqrtPriceX96ToTickOptimizedCached(sqrtPrice)
|
||||
|
||||
// Allow for small differences due to floating point precision
|
||||
assert.InDelta(t, tick, resultTick, 1, "Round trip tick conversion should be accurate")
|
||||
}
|
||||
@@ -32,19 +32,15 @@ func SqrtPriceX96ToPrice(sqrtPriceX96 *big.Int) *big.Float {
|
||||
}
|
||||
|
||||
// Convert to big.Float for precision
|
||||
sqrtPrice := new(big.Float).SetInt(sqrtPriceX96)
|
||||
sqrtPrice := new(big.Float).SetPrec(256).SetInt(sqrtPriceX96)
|
||||
|
||||
// Calculate sqrtPrice^2
|
||||
price := new(big.Float).Mul(sqrtPrice, sqrtPrice)
|
||||
price := new(big.Float).SetPrec(256)
|
||||
price.Mul(sqrtPrice, sqrtPrice)
|
||||
|
||||
// Divide by 2^192 using global cached constant
|
||||
price.Quo(price, GetQ192Float())
|
||||
|
||||
// Validate result is reasonable (not extremely high or low)
|
||||
priceFloat, _ := price.Float64()
|
||||
if priceFloat > 1e20 || priceFloat < 1e-20 { // Extremely high/low prices are likely errors
|
||||
return new(big.Float).SetFloat64(1.0) // Default to 1.0 as safe fallback
|
||||
}
|
||||
denominator := new(big.Float).SetPrec(256).SetInt(GetQ192())
|
||||
price.Quo(price, denominator)
|
||||
|
||||
return price
|
||||
}
|
||||
@@ -57,10 +53,12 @@ func PriceToSqrtPriceX96(price *big.Float) *big.Int {
|
||||
initConstants()
|
||||
|
||||
// Calculate sqrt(price)
|
||||
sqrtPrice := new(big.Float).Sqrt(price)
|
||||
input := new(big.Float).SetPrec(256).Copy(price)
|
||||
sqrtPrice := new(big.Float).SetPrec(256).Sqrt(input)
|
||||
|
||||
// Multiply by 2^96 using global cached constant
|
||||
sqrtPrice.Mul(sqrtPrice, GetQ96Float())
|
||||
multiplier := new(big.Float).SetPrec(256).SetInt(GetQ96())
|
||||
sqrtPrice.Mul(sqrtPrice, multiplier)
|
||||
|
||||
// Convert to big.Int
|
||||
sqrtPriceX96 := new(big.Int)
|
||||
|
||||
Reference in New Issue
Block a user