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:
Krypto Kajun
2025-10-17 00:12:55 -05:00
parent f358f49aa9
commit 850223a953
8621 changed files with 79808 additions and 7340 deletions

View File

@@ -3,11 +3,14 @@ package math
import (
"context"
"fmt"
"math"
"math/big"
"sort"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/fraktal/mev-beta/pkg/security"
"github.com/fraktal/mev-beta/pkg/types"
)
@@ -73,10 +76,10 @@ func NewArbitrageCalculator(gasEstimator GasEstimator) *ArbitrageCalculator {
dc := NewDecimalConverter()
// Default configuration
minProfit, _ := dc.FromString("0.01", 18, "ETH") // 0.01 ETH minimum
maxImpact, _ := dc.FromString("2", 4, "PERCENT") // 2% max price impact
maxSlip, _ := dc.FromString("1", 4, "PERCENT") // 1% max slippage
maxGas, _ := dc.FromString("50", 9, "GWEI") // 50 gwei max gas
minProfit, _ := dc.FromString("0.01", 18, "ETH") // 0.01 ETH minimum
maxImpact, _ := dc.FromString("0.02", 4, "PERCENT") // 2% max price impact
maxSlip, _ := dc.FromString("0.01", 4, "PERCENT") // 1% max slippage
maxGas, _ := dc.FromString("50", 9, "GWEI") // 50 gwei max gas
return &ArbitrageCalculator{
pricingEngine: NewExchangePricingEngine(),
@@ -89,6 +92,17 @@ func NewArbitrageCalculator(gasEstimator GasEstimator) *ArbitrageCalculator {
}
}
func toDecimalAmount(ud *UniversalDecimal) types.DecimalAmount {
if ud == nil {
return types.DecimalAmount{}
}
return types.DecimalAmount{
Value: ud.Value.String(),
Decimals: ud.Decimals,
Symbol: ud.Symbol,
}
}
// CalculateArbitrageOpportunity performs comprehensive arbitrage analysis
func (calc *ArbitrageCalculator) CalculateArbitrageOpportunity(
path []*PoolData,
@@ -117,7 +131,7 @@ func (calc *ArbitrageCalculator) CalculateArbitrageOpportunity(
}
// Step 4: Calculate profits (convert to common denomination - ETH)
_, netProfit, profitPercentage, err := calc.calculateProfits(
grossProfit, netProfit, profitPercentage, err := calc.calculateProfits(
inputAmount, finalOutput, totalGasCost, inputToken, outputToken)
if err != nil {
return nil, fmt.Errorf("error calculating profits: %w", err)
@@ -155,22 +169,43 @@ func (calc *ArbitrageCalculator) CalculateArbitrageOpportunity(
}
opportunity := &types.ArbitrageOpportunity{
Path: pathStrings,
Pools: poolStrings,
AmountIn: inputAmount.Value,
Profit: netProfit.Value,
NetProfit: netProfit.Value,
GasEstimate: totalGasCost.Value,
ROI: func() float64 { f, _ := profitPercentage.Value.Float64(); return f }(),
Path: pathStrings,
Pools: poolStrings,
AmountIn: inputAmount.Value,
RequiredAmount: inputAmount.Value,
Profit: grossProfit.Value,
NetProfit: netProfit.Value,
EstimatedProfit: grossProfit.Value,
GasEstimate: totalGasCost.Value,
ROI: func() float64 {
// Convert percentage from 4-decimal format to actual percentage
f, _ := profitPercentage.Value.Float64()
return f / 10000.0 // Convert from 4-decimal format to actual percentage
}(),
Protocol: "multi", // Default protocol for multi-step arbitrage
ExecutionTime: executionTime,
Confidence: confidence,
PriceImpact: func() float64 { f, _ := totalPriceImpact.Value.Float64(); return f }(),
MaxSlippage: 0.01, // Default 1% max slippage
TokenIn: common.HexToAddress(inputToken.Address),
TokenOut: common.HexToAddress(outputToken.Address),
Timestamp: time.Now().Unix(),
Risk: riskAssessment.OverallRisk,
PriceImpact: func() float64 {
// Convert percentage from 4-decimal format to actual percentage
f, _ := totalPriceImpact.Value.Float64()
return f / 10000.0 // Convert from 4-decimal format to actual percentage
}(),
MaxSlippage: 0.01, // Default 1% max slippage
TokenIn: common.HexToAddress(inputToken.Address),
TokenOut: common.HexToAddress(outputToken.Address),
Timestamp: time.Now().Unix(),
DetectedAt: time.Now(),
ExpiresAt: time.Now().Add(5 * time.Minute),
Risk: riskAssessment.OverallRisk,
}
opportunity.Quantities = &types.OpportunityQuantities{
AmountIn: toDecimalAmount(inputAmount),
AmountOut: toDecimalAmount(finalOutput),
GrossProfit: toDecimalAmount(grossProfit),
NetProfit: toDecimalAmount(netProfit),
GasCost: toDecimalAmount(totalGasCost),
ProfitPercent: toDecimalAmount(profitPercentage),
PriceImpact: toDecimalAmount(totalPriceImpact),
}
return opportunity, nil
@@ -260,7 +295,13 @@ func (calc *ArbitrageCalculator) calculateTotalGasCost(route []ExchangeStep) (*U
}
// Convert to gas cost in ETH
totalGasBig := big.NewInt(int64(totalGas))
totalGasInt64, err := security.SafeUint64ToInt64(totalGas)
if err != nil {
// This is very unlikely for gas calculations, but handle safely
// Use maximum safe value as fallback
totalGasInt64 = math.MaxInt64
}
totalGasBig := big.NewInt(totalGasInt64)
totalGasDecimal, err := NewUniversalDecimal(totalGasBig, 0, "GAS")
if err != nil {
return nil, err
@@ -551,38 +592,107 @@ func (calc *ArbitrageCalculator) convertToETH(amount *UniversalDecimal, token To
}
// IsOpportunityProfitable checks if opportunity meets minimum criteria
// IsOpportunityProfitable checks if an opportunity meets profitability criteria
func (calc *ArbitrageCalculator) IsOpportunityProfitable(opportunity *types.ArbitrageOpportunity) bool {
// Check minimum profit threshold (simplified comparison)
if opportunity.NetProfit.Cmp(big.NewInt(1000000000000000)) < 0 { // 0.001 ETH minimum
if opportunity == nil {
return false
}
// Check maximum price impact threshold (5% max)
if opportunity.PriceImpact > 0.05 {
// Check minimum profit threshold
if !calc.checkProfitThreshold(opportunity) {
return false
}
// Check maximum price impact
if !calc.checkPriceImpactThreshold(opportunity) {
return false
}
// Check risk level
if opportunity.Risk >= 0.8 { // High risk threshold
if !calc.checkRiskLevel(opportunity) {
return false
}
// Check confidence threshold
if opportunity.Confidence < 0.3 {
if !calc.checkConfidenceThreshold(opportunity) {
return false
}
return true
}
// checkProfitThreshold checks if the opportunity meets minimum profit requirements
func (calc *ArbitrageCalculator) checkProfitThreshold(opportunity *types.ArbitrageOpportunity) bool {
if opportunity.Quantities != nil {
if netProfitUD, err := calc.decimalAmountToUniversal(opportunity.Quantities.NetProfit); err == nil {
if cmp, err := calc.decimalConverter.Compare(netProfitUD, calc.minProfitThreshold); err == nil && cmp < 0 {
return false
}
}
} else if opportunity.NetProfit != nil {
if opportunity.NetProfit.Cmp(calc.minProfitThreshold.Value) < 0 {
return false
}
} else {
return false
}
return true
}
// checkPriceImpactThreshold checks if the opportunity is below maximum price impact
func (calc *ArbitrageCalculator) checkPriceImpactThreshold(opportunity *types.ArbitrageOpportunity) bool {
if opportunity.Quantities != nil {
if impactUD, err := calc.decimalAmountToUniversal(opportunity.Quantities.PriceImpact); err == nil {
if cmp, err := calc.decimalConverter.Compare(impactUD, calc.maxPriceImpact); err == nil && cmp > 0 {
return false
}
}
} else {
maxImpactFloat := float64(calc.maxPriceImpact.Value.Int64()) / math.Pow10(int(calc.maxPriceImpact.Decimals))
if opportunity.PriceImpact > maxImpactFloat {
return false
}
}
return true
}
// checkRiskLevel checks if the opportunity's risk is acceptable
func (calc *ArbitrageCalculator) checkRiskLevel(opportunity *types.ArbitrageOpportunity) bool {
return opportunity.Risk < 0.8 // High risk threshold
}
// checkConfidenceThreshold checks if the opportunity has sufficient confidence
func (calc *ArbitrageCalculator) checkConfidenceThreshold(opportunity *types.ArbitrageOpportunity) bool {
return opportunity.Confidence >= 0.3
}
// SortOpportunitiesByProfitability sorts opportunities by net profit descending
func (calc *ArbitrageCalculator) SortOpportunitiesByProfitability(opportunities []*types.ArbitrageOpportunity) {
sort.Slice(opportunities, func(i, j int) bool {
// Simple comparison using big.Int.Cmp for sorting
return opportunities[i].NetProfit.Cmp(opportunities[j].NetProfit) > 0 // Descending order (highest profit first)
left, errL := calc.decimalAmountToUniversal(opportunities[i].Quantities.NetProfit)
right, errR := calc.decimalAmountToUniversal(opportunities[j].Quantities.NetProfit)
if errL == nil && errR == nil {
cmp, err := calc.decimalConverter.Compare(left, right)
if err == nil {
return cmp > 0
}
}
// Fallback to canonical big.Int comparison
return opportunities[i].NetProfit.Cmp(opportunities[j].NetProfit) > 0 // Descending order
})
}
func (calc *ArbitrageCalculator) decimalAmountToUniversal(dec types.DecimalAmount) (*UniversalDecimal, error) {
if dec.Value == "" {
return nil, fmt.Errorf("decimal amount empty")
}
val, ok := new(big.Int).SetString(dec.Value, 10)
if !ok {
return nil, fmt.Errorf("invalid decimal amount %s", dec.Value)
}
return NewUniversalDecimal(val, dec.Decimals, dec.Symbol)
}
// CalculateArbitrage calculates arbitrage opportunity for a given path and input amount
func (calc *ArbitrageCalculator) CalculateArbitrage(ctx context.Context, inputAmount *UniversalDecimal, path []*PoolData) (*types.ArbitrageOpportunity, error) {
if len(path) == 0 {

View File

@@ -0,0 +1,175 @@
package math
import (
"math/big"
"testing"
"github.com/fraktal/mev-beta/pkg/types"
)
type stubGasEstimator struct {
price *UniversalDecimal
}
func (s stubGasEstimator) EstimateSwapGas(exchange ExchangeType, poolData *PoolData) (uint64, error) {
return 100_000, nil
}
func (s stubGasEstimator) EstimateFlashSwapGas(route []*PoolData) (uint64, error) {
return 50_000, nil
}
func (s stubGasEstimator) GetCurrentGasPrice() (*UniversalDecimal, error) {
return s.price, nil
}
func TestIsOpportunityProfitableRespectsThreshold(t *testing.T) {
estimator := stubGasEstimator{price: func() *UniversalDecimal {
ud, _ := NewUniversalDecimal(big.NewInt(1_000_000_000), 9, "GWEI")
return ud
}()}
calc := NewArbitrageCalculator(estimator)
belowThreshold, _ := NewUniversalDecimal(big.NewInt(9_000_000_000_000_000), 18, "ETH")
priceImpact, _ := NewUniversalDecimal(big.NewInt(100), 4, "PERCENT")
opportunity := &types.ArbitrageOpportunity{
NetProfit: belowThreshold.Value,
PriceImpact: 0.01,
Confidence: 0.5,
Quantities: &types.OpportunityQuantities{
NetProfit: toDecimalAmount(belowThreshold),
PriceImpact: toDecimalAmount(priceImpact),
AmountIn: toDecimalAmount(belowThreshold),
AmountOut: toDecimalAmount(belowThreshold),
GrossProfit: toDecimalAmount(belowThreshold),
GasCost: toDecimalAmount(belowThreshold),
ProfitPercent: toDecimalAmount(priceImpact),
},
}
if calc.IsOpportunityProfitable(opportunity) {
t.Fatalf("expected below-threshold opportunity to be rejected")
}
aboveThreshold, _ := NewUniversalDecimal(big.NewInt(2_000_000_000_000_0000), 18, "ETH")
opportunity.NetProfit = aboveThreshold.Value
opportunity.Quantities.NetProfit = toDecimalAmount(aboveThreshold)
if !calc.IsOpportunityProfitable(opportunity) {
t.Fatalf("expected opportunity above threshold to be accepted")
}
}
func TestSortOpportunitiesByProfitabilityUsesDecimals(t *testing.T) {
estimator := stubGasEstimator{price: func() *UniversalDecimal {
ud, _ := NewUniversalDecimal(big.NewInt(1_000_000_000), 9, "GWEI")
return ud
}()}
calc := NewArbitrageCalculator(estimator)
a, _ := NewUniversalDecimal(big.NewInt(1_500_000_000_000_0000), 18, "ETH")
b, _ := NewUniversalDecimal(big.NewInt(5_000_000_000_000_000), 18, "ETH")
oppA := &types.ArbitrageOpportunity{
NetProfit: a.Value,
Quantities: &types.OpportunityQuantities{
NetProfit: toDecimalAmount(a),
},
}
oppB := &types.ArbitrageOpportunity{
NetProfit: b.Value,
Quantities: &types.OpportunityQuantities{
NetProfit: toDecimalAmount(b),
},
}
opps := []*types.ArbitrageOpportunity{oppB, oppA}
calc.SortOpportunitiesByProfitability(opps)
if opps[0] != oppA {
t.Fatalf("expected higher decimal profit opportunity first")
}
}
func TestCalculateArbitrageOpportunitySetsQuantities(t *testing.T) {
estimator := stubGasEstimator{price: func() *UniversalDecimal {
ud, _ := NewUniversalDecimal(big.NewInt(1_000_000_000), 9, "GWEI")
return ud
}()}
calc := NewArbitrageCalculator(estimator)
pool := &PoolData{
Address: "0xpool",
ExchangeType: ExchangeUniswapV2,
Token0: TokenInfo{Address: "0x0", Symbol: "TOKEN0", Decimals: 18},
Token1: TokenInfo{Address: "0x1", Symbol: "TOKEN1", Decimals: 18},
}
amountIn, _ := NewUniversalDecimal(big.NewInt(1_000_000_000_000_000), 18, "TOKEN0")
opportunity, err := calc.CalculateArbitrageOpportunity([]*PoolData{pool}, amountIn, pool.Token0, pool.Token1)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if opportunity.Quantities == nil {
t.Fatalf("expected quantities to be populated")
}
if opportunity.Quantities.NetProfit.Value == "" {
t.Fatalf("expected net profit decimal to have value")
}
}
func TestCalculateMinimumOutputAppliesSlippage(t *testing.T) {
estimator := stubGasEstimator{price: func() *UniversalDecimal {
ud, _ := NewUniversalDecimal(big.NewInt(1_000_000_000), 9, "GWEI")
return ud
}()}
calc := NewArbitrageCalculator(estimator)
expected, _ := NewUniversalDecimal(big.NewInt(1_000_000_000_000_000_000), 18, "ETH")
minOutput, err := calc.calculateMinimumOutput(expected)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
// Default max slippage is 1% -> expect 0.99 ETH
expectedMin, _ := NewUniversalDecimal(big.NewInt(990000000000000000), 18, "ETH")
cmp, err := calc.decimalConverter.Compare(minOutput, expectedMin)
if err != nil || cmp != 0 {
t.Fatalf("expected min output 0.99 ETH, got %s", calc.decimalConverter.ToHumanReadable(minOutput))
}
}
func TestCalculateProfitsCapturesSpread(t *testing.T) {
estimator := stubGasEstimator{price: func() *UniversalDecimal {
ud, _ := NewUniversalDecimal(big.NewInt(1_000_000_000), 9, "GWEI")
return ud
}()}
calc := NewArbitrageCalculator(estimator)
amountIn, _ := NewUniversalDecimal(big.NewInt(10_000_000_000_000_000), 18, "ETH") // 0.01
amountOut, _ := NewUniversalDecimal(big.NewInt(12_000_000_000_000_000), 18, "ETH")
gasCost, _ := NewUniversalDecimal(big.NewInt(500_000_000_000_000), 18, "ETH")
gross, net, pct, err := calc.calculateProfits(amountIn, amountOut, gasCost, TokenInfo{Symbol: "ETH", Decimals: 18}, TokenInfo{Symbol: "ETH", Decimals: 18})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
expectedGross, _ := NewUniversalDecimal(big.NewInt(2_000_000_000_000_000), 18, "ETH")
cmp, err := calc.decimalConverter.Compare(gross, expectedGross)
if err != nil || cmp != 0 {
t.Fatalf("expected gross profit 0.002 ETH, got %s", calc.decimalConverter.ToHumanReadable(gross))
}
expectedNet, _ := NewUniversalDecimal(big.NewInt(1_500_000_000_000_000), 18, "ETH")
cmp, err = calc.decimalConverter.Compare(net, expectedNet)
if err != nil || cmp != 0 {
t.Fatalf("expected net profit 0.0015 ETH, got %s", calc.decimalConverter.ToHumanReadable(net))
}
if pct == nil || pct.Value.Sign() <= 0 {
t.Fatalf("expected positive profit percentage")
}
}

View File

@@ -4,8 +4,9 @@ import (
"math/big"
"sync"
"github.com/fraktal/mev-beta/pkg/uniswap"
"github.com/holiman/uint256"
"github.com/fraktal/mev-beta/pkg/uniswap"
)
// Cached mathematical constants to avoid recomputation

View File

@@ -33,12 +33,31 @@ func NewDecimalConverter() *DecimalConverter {
return dc
}
// NewUniversalDecimal creates a new universal decimal with validation
// NewUniversalDecimal creates a new universal decimal with comprehensive validation
func NewUniversalDecimal(value *big.Int, decimals uint8, symbol string) (*UniversalDecimal, error) {
// Validate decimal places
if decimals > 18 {
return nil, fmt.Errorf("decimal places cannot exceed 18, got %d for token %s", decimals, symbol)
}
// Validate symbol
if symbol == "" {
return nil, fmt.Errorf("symbol cannot be empty")
}
// Validate value bounds - prevent extremely large values that could cause overflow
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)
if absValue.Cmp(maxValue) > 0 {
return nil, fmt.Errorf("value %s exceeds maximum safe value for token %s", value.String(), symbol)
}
}
if value == nil {
value = big.NewInt(0)
}
@@ -83,18 +102,37 @@ func (dc *DecimalConverter) FromString(valueStr string, decimals uint8, symbol s
return nil, fmt.Errorf("invalid number format: %s for token %s", valueStr, symbol)
}
// Heuristic: if the string length is >= decimals, treat as raw value
// This handles cases like "1000000000000000000" (18 chars, 18 decimals) as raw
// But treats "1" (1 char, 18 decimals) as human-readable
// Improved heuristic for distinguishing raw vs human-readable values:
// 1. If value is very large relative to what a human would typically enter, treat as raw
// 2. If value is small (< 1000), treat as human-readable
// 3. Use length as secondary indicator
valueInt := value.Int64() // Safe since we parsed it successfully
// If the value is very small (less than 1000), it's likely human-readable
if valueInt < 1000 {
// Treat as human-readable value - convert to smallest unit
scalingFactor := dc.getScalingFactor(decimals)
scaledValue := new(big.Int).Mul(value, scalingFactor)
return NewUniversalDecimal(scaledValue, decimals, symbol)
}
// If the value looks like it could be raw wei (very large), treat as raw
if len(valueStr) >= int(decimals) && decimals > 0 {
// Treat as raw value in smallest unit
return NewUniversalDecimal(value, decimals, symbol)
}
// Treat as human-readable value - convert to smallest unit
// For intermediate values, use a more sophisticated check
// If the number would represent more than 1000 tokens when treated as human-readable,
// it's probably meant to be raw
if valueInt > 1000 {
return NewUniversalDecimal(value, decimals, symbol)
}
// Default: treat as human-readable
scalingFactor := dc.getScalingFactor(decimals)
scaledValue := new(big.Int).Mul(value, scalingFactor)
return NewUniversalDecimal(scaledValue, decimals, symbol)
}
@@ -226,8 +264,16 @@ func (dc *DecimalConverter) ConvertTo(from *UniversalDecimal, toDecimals uint8,
return NewUniversalDecimal(convertedValue, toDecimals, toSymbol)
}
// Multiply performs precise multiplication between different decimal tokens
// Multiply performs precise multiplication between different decimal tokens with overflow protection
func (dc *DecimalConverter) Multiply(a, b *UniversalDecimal, resultDecimals uint8, resultSymbol string) (*UniversalDecimal, error) {
// Check for overflow potential before multiplication
maxSafeValue := new(big.Int)
maxSafeValue.Exp(big.NewInt(10), big.NewInt(30), nil) // Conservative limit for multiplication
if a.Value.Cmp(maxSafeValue) > 0 || b.Value.Cmp(maxSafeValue) > 0 {
return nil, fmt.Errorf("values too large for safe multiplication: %s * %s", a.Symbol, b.Symbol)
}
// Multiply raw values
product := new(big.Int).Mul(a.Value, b.Value)
@@ -268,13 +314,21 @@ func (dc *DecimalConverter) Divide(numerator, denominator *UniversalDecimal, res
return NewUniversalDecimal(quotient, resultDecimals, resultSymbol)
}
// Add adds two UniversalDecimals with same precision
// Add adds two UniversalDecimals with same precision and overflow protection
func (dc *DecimalConverter) Add(a, b *UniversalDecimal) (*UniversalDecimal, error) {
if a.Decimals != b.Decimals {
return nil, fmt.Errorf("cannot add tokens with different decimals: %s(%d) + %s(%d)",
a.Symbol, a.Decimals, b.Symbol, b.Decimals)
}
// Check for potential overflow before performing addition
maxSafeValue := new(big.Int)
maxSafeValue.Exp(big.NewInt(10), big.NewInt(59), nil) // 10^59 for safety margin
if a.Value.Cmp(maxSafeValue) > 0 || b.Value.Cmp(maxSafeValue) > 0 {
return nil, fmt.Errorf("values too large for safe addition: %s + %s", a.Symbol, b.Symbol)
}
sum := new(big.Int).Add(a.Value, b.Value)
resultSymbol := a.Symbol
if a.Symbol != b.Symbol {

View File

@@ -17,6 +17,8 @@ const (
ExchangeTraderJoe ExchangeType = "traderjoe"
ExchangeRamses ExchangeType = "ramses"
ExchangeCurve ExchangeType = "curve"
ExchangeKyber ExchangeType = "kyber"
ExchangeUniswapV4 ExchangeType = "uniswap_v4"
)
// ExchangePricer interface for exchange-specific price calculations

View File

@@ -158,11 +158,15 @@ func TestPercentageCalculations(t *testing.T) {
percentageFloat, _ := percentage.Value.Float64()
t.Logf("Calculated percentage: %.6f%%", percentageFloat)
// Convert from raw value to actual percentage (divide by 10^decimals)
// Since percentage has 4 decimals, divide by 10000 to get actual percentage value
actualPercentage := percentageFloat / 10000.0
if percentageFloat < tc.expectedRange[0] || percentageFloat > tc.expectedRange[1] {
t.Logf("Calculated percentage: %.6f%%", actualPercentage)
if actualPercentage < tc.expectedRange[0] || actualPercentage > tc.expectedRange[1] {
t.Errorf("Percentage %.6f%% outside expected range [%.3f%%, %.3f%%]",
percentageFloat, tc.expectedRange[0], tc.expectedRange[1])
actualPercentage, tc.expectedRange[0], tc.expectedRange[1])
}
})
}