fixed tests

This commit is contained in:
giteadmin 2025-07-17 21:35:18 +02:00
parent 352ec623f0
commit c5f0323df7
6 changed files with 151 additions and 631 deletions

View file

@ -1,200 +0,0 @@
# LiquidityManager Refactoring Analysis
## Executive Summary
The original `LiquidityManager.sol` (439 lines) has been successfully refactored into a modular architecture with 4 separate contracts totaling ~600 lines. This improves maintainability, testability, and separation of concerns while preserving all original functionality.
## Identified Issues in Original Code
### 1. **Single Responsibility Principle Violations**
- **Mathematical utilities** mixed with business logic
- **Oracle validation** embedded in main contract
- **Position strategy** tightly coupled with implementation details
- **Fee collection** intertwined with position management
### 2. **Testing and Maintenance Challenges**
- **116-line `_set()` function** difficult to test individual position logic
- **Mathematical functions** cannot be tested in isolation
- **Price validation** logic cannot be unit tested separately
- **No clear boundaries** between different responsibilities
### 3. **Code Reusability Issues**
- **Price/tick utilities** could benefit other contracts
- **Oracle logic** useful for other price-sensitive contracts
- **Position strategy** could be adapted for different tokens
## Refactoring Solution
### **Modular Architecture Overview**
```
LiquidityManagerV2
├── inherits from ThreePositionStrategy (anti-arbitrage logic)
│ ├── inherits from UniswapMath (mathematical utilities)
│ └── inherits from VWAPTracker (dormant whale protection)
└── inherits from PriceOracle (TWAP validation)
```
### **1. UniswapMath.sol (Mathematical Utilities)**
```solidity
abstract contract UniswapMath {
function _tickAtPrice(bool t0isWeth, uint256 tokenAmount, uint256 ethAmount) internal pure returns (int24);
function _tickAtPriceRatio(int128 priceRatioX64) internal pure returns (int24);
function _priceAtTick(int24 tick) internal pure returns (uint256);
function _clampToTickSpacing(int24 tick, int24 spacing) internal pure returns (int24);
}
```
**Benefits:**
- ✅ **Pure functions** easily unit testable
- ✅ **Reusable** across multiple contracts
- ✅ **Gas efficient** (no state variables)
- ✅ **Clear responsibility** (mathematical operations only)
### **2. PriceOracle.sol (TWAP Validation)**
```solidity
abstract contract PriceOracle {
function _isPriceStable(int24 currentTick) internal view returns (bool);
function _validatePriceMovement(int24 currentTick, int24 centerTick, int24 tickSpacing, bool token0isWeth)
internal pure returns (bool isUp, bool isEnough);
}
```
**Benefits:**
- ✅ **Isolated oracle logic** for independent testing
- ✅ **Configurable parameters** (intervals, deviations)
- ✅ **Reusable** for other price-sensitive contracts
- ✅ **Clear error handling** for oracle failures
### **3. ThreePositionStrategy.sol (Anti-Arbitrage Strategy)**
```solidity
abstract contract ThreePositionStrategy is UniswapMath, VWAPTracker {
function _setPositions(int24 currentTick, PositionParams memory params) internal;
function _setAnchorPosition(...) internal returns (uint256 pulledHarb);
function _setDiscoveryPosition(...) internal returns (uint256 discoveryAmount);
function _setFloorPosition(...) internal;
}
```
**Benefits:**
- ✅ **Separated position logic** for individual testing
- ✅ **Clear dependencies** (ANCHOR → DISCOVERY → FLOOR)
- ✅ **Economic rationale** documented in each function
- ✅ **VWAP exclusivity** clearly implemented (only floor position)
### **4. LiquidityManagerV2.sol (Main Contract)**
```solidity
contract LiquidityManagerV2 is ThreePositionStrategy, PriceOracle {
// Focused on:
// - Uniswap V3 integration (callbacks, minting)
// - Access control and fee management
// - Orchestration of inherited functionality
}
```
## Key Improvements
### **1. Separation of Concerns**
| Responsibility | Original Location | New Location |
|----------------|------------------|--------------|
| Mathematical utilities | Mixed throughout | `UniswapMath.sol` |
| Oracle validation | `_isPriceStable()` | `PriceOracle.sol` |
| Position strategy | 116-line `_set()` | `ThreePositionStrategy.sol` |
| Uniswap integration | Main contract | `LiquidityManagerV2.sol` |
### **2. Enhanced Testability**
- ✅ **Unit test** mathematical functions in isolation
- ✅ **Mock** oracle responses for price validation testing
- ✅ **Test** individual position strategies (anchor, discovery, floor)
- ✅ **Validate** position dependencies separately
### **3. Improved Maintainability**
- ✅ **Clear boundaries** between different concerns
- ✅ **Smaller functions** easier to understand and modify
- ✅ **Documented dependencies** between position types
- ✅ **Reusable components** for future development
### **4. Preserved Functionality**
- ✅ **Identical behavior** to original contract
- ✅ **Same gas efficiency** (abstract contracts have no overhead)
- ✅ **All events and errors** preserved
- ✅ **Complete API compatibility**
## Testing Strategy for Refactored Version
### **1. Unit Tests by Component**
```solidity
// UniswapMathTest.sol - Test mathematical utilities
function testTickAtPrice() { /* Test price → tick conversion */ }
function testPriceAtTick() { /* Test tick → price conversion */ }
function testClampToTickSpacing() { /* Test tick normalization */ }
// PriceOracleTest.sol - Test oracle validation
function testPriceStabilityValidation() { /* Mock oracle responses */ }
function testPriceMovementValidation() { /* Test movement detection */ }
// ThreePositionStrategyTest.sol - Test position logic
function testAnchorPositionSetting() { /* Test shallow liquidity */ }
function testDiscoveryPositionDependency() { /* Test pulledHarb dependency */ }
function testFloorPositionVWAPUsage() { /* Test VWAP exclusivity */ }
```
### **2. Integration Tests**
```solidity
// LiquidityManagerV2Test.sol - Test full integration
function testAntiArbitrageStrategyValidation() { /* Reuse existing test */ }
function testRecenteringOrchestration() { /* Test component coordination */ }
```
## Migration Path
### **Option 1: Gradual Migration**
1. Deploy new modular contracts alongside existing ones
2. Test extensively with same parameters
3. Gradually migrate functionality
4. Deprecate original contract
### **Option 2: Direct Replacement**
1. Comprehensive testing of refactored version
2. Deploy as upgrade/replacement
3. Maintain identical interface for existing integrations
## Performance Analysis
### **Gas Usage Comparison**
- ✅ **No overhead** from abstract contracts (compiled away)
- ✅ **Same function calls** (inheritance is compile-time)
- ✅ **Potential savings** from better optimization opportunities
- ✅ **Identical storage layout** (same state variables)
### **Code Size Comparison**
| Metric | Original | Refactored | Change |
|--------|----------|------------|--------|
| Main contract lines | 439 | 267 | -39% |
| Total lines | 439 | ~600 | +37% |
| Functions per file | 20+ | 5-8 | Better organization |
| Testable units | 1 | 4 | +400% |
## Recommendations
### **Immediate Actions**
1. ✅ **Deploy and test** refactored version
2. ✅ **Create comprehensive test suite** for each component
3. ✅ **Validate gas usage** against original contract
4. ✅ **Document migration strategy**
### **Future Enhancements**
1. **Library conversion**: Convert `UniswapMath` to library for even better reusability
2. **Interface extraction**: Create interfaces for position strategies
3. **Plugin architecture**: Allow swappable position strategies
4. **Enhanced monitoring**: Add more granular events per component
## Conclusion
The refactored `LiquidityManagerV2` maintains 100% functional compatibility while providing:
- **Better separation of concerns** for maintainability
- **Enhanced testability** for quality assurance
- **Improved reusability** for future development
- **Clearer documentation** of anti-arbitrage strategy
This modular architecture makes the sophisticated three-position anti-arbitrage strategy more understandable and maintainable while preserving the proven economic protection mechanisms.

View file

@ -1,218 +0,0 @@
# Test Refactoring Summary
## Overview
Successfully refactored the LiquidityManager test suite to support the new modular architecture while maintaining all existing functionality and adding comprehensive unit tests for individual components.
## Test Files Created
### 1. **Unit Tests for Modular Components**
#### `/test/libraries/UniswapMath.t.sol`
- **15 comprehensive tests** for mathematical utilities
- **Tick/price conversion functions** tested in isolation
- **Boundary conditions** and **edge cases** covered
- **Fuzz testing** for robustness validation
- **Round-trip conversions** verified
**Key Tests:**
- `testTickAtPriceBasic()` - Basic price to tick conversion
- `testPriceAtTickSymmetry()` - Validates mathematical reciprocals
- `testClampToTickSpacing()` - Tick alignment and boundary checking
- `testFuzzTickAtPrice()` - Comprehensive input validation
#### `/test/abstracts/PriceOracle.t.sol`
- **15+ tests** for TWAP oracle validation
- **Mock Uniswap pool** for isolated testing
- **Price stability scenarios** with various deviations
- **Price movement validation** for different token orderings
- **Oracle failure fallback** behavior testing
**Key Tests:**
- `testPriceStableWithinDeviation()` - Oracle validation logic
- `testPriceMovementWethToken0Up()` - Token ordering edge cases
- `testPriceStabilityOracleFailureFallback()` - Error handling
#### `/test/abstracts/ThreePositionStrategy.t.sol`
- **20+ tests** for position strategy logic
- **Position dependencies** (ANCHOR → DISCOVERY → FLOOR) validated
- **VWAP exclusivity** for floor position confirmed
- **Asymmetric slippage profile** architecture tested
- **Mock implementation** for isolated component testing
**Key Tests:**
- `testAnchorPositionSymmetricAroundCurrentTick()` - Shallow liquidity validation
- `testDiscoveryPositionDependsOnAnchor()` - Dependency verification
- `testFloorPositionUsesVWAP()` - VWAP exclusivity validation
- `testSetPositionsAsymmetricProfile()` - Anti-arbitrage architecture
### 2. **Integration Tests**
#### `/test/ModularComponentsTest.t.sol`
- **Compilation verification** for all modular components
- **Basic functionality testing** of integrated architecture
- **Proof that refactoring maintains compatibility**
### 3. **Analysis Scripts Updated**
#### `/analysis/SimpleAnalysis.s.sol`
- **Updated to reference** modular architecture
- **Documentation updated** to reflect new components
- **Analysis capabilities preserved** for security research
#### `/analysis/README.md`
- **Comprehensive documentation** of analysis suite capabilities
- **Updated references** to LiquidityManagerV2 architecture
- **Enhanced coverage** of modular component interactions
## Test Results Validation
### ✅ **All Core Tests Pass**
```bash
forge test --match-test testModularArchitectureCompiles
forge test --match-test testUniswapMathCompilation
forge test --match-test testTickAtPriceBasic
forge test --match-test testAntiArbitrageStrategyValidation
```
### ✅ **Modular Architecture Verified**
- **Mathematical utilities** work independently
- **Price oracle logic** functions in isolation
- **Position strategy** maintains economic dependencies
- **Anti-arbitrage protection** preserved in original tests
### ✅ **Compatibility Maintained**
- **Existing anti-arbitrage test** still passes (80% slippage protection)
- **Original functionality** completely preserved
- **Test architecture** supports both original and modular versions
## Benefits Achieved
### **1. Enhanced Test Coverage**
| Component | Original | Refactored | Improvement |
|-----------|----------|------------|-------------|
| Mathematical utilities | Embedded | 15 unit tests | Isolated testing |
| Oracle logic | Integrated | 15+ unit tests | Mock-based testing |
| Position strategy | Monolithic | 20+ unit tests | Component validation |
| **Total test granularity** | **Low** | **High** | **+300% coverage** |
### **2. Improved Debugging**
- **Unit test failures** pinpoint exact component issues
- **Mock implementations** allow isolated problem diagnosis
- **Mathematical errors** can be caught without full integration
- **Oracle issues** testable without blockchain interaction
### **3. Development Velocity**
- **Fast unit tests** for individual components (milliseconds)
- **Slower integration tests** only when needed
- **Component changes** can be validated independently
- **Regression testing** more targeted and efficient
### **4. Documentation Through Tests**
- **Component boundaries** clearly defined through test structure
- **Dependencies** explicitly tested and documented
- **Economic logic** validated at component level
- **Anti-arbitrage strategy** broken down into testable parts
## Test Architecture Design
### **Inheritance Hierarchy**
```
LiquidityManagerTest (original)
├── Contains full integration tests
├── Anti-arbitrage validation test ✅
└── Supports both original and modular contracts
ModularComponentsTest
├── Quick validation of architecture
└── Compilation and basic functionality tests
Component-Specific Tests
├── UniswapMathTest (mathematical utilities)
├── PriceOracleTest (TWAP validation)
└── ThreePositionStrategyTest (position logic)
```
### **Test Separation Strategy**
- **Unit tests** for individual components (fast, isolated)
- **Integration tests** for full system behavior (comprehensive)
- **Compatibility tests** ensuring refactoring doesn't break functionality
- **Performance tests** validating no gas overhead from modular design
## Migration Path for Development
### **Current State**
- ✅ **Original LiquidityManager** fully tested and functional
- ✅ **Modular LiquidityManagerV2** compiles and basic functions work
- ✅ **Unit tests** provide comprehensive component coverage
- ✅ **Analysis scripts** updated for new architecture
### **Next Steps for Full Integration**
1. **Complete LiquidityManagerV2 integration tests**
- Create comprehensive test suite that exercises full contract
- Validate gas usage equivalent to original
- Confirm identical behavior across all scenarios
2. **Production deployment preparation**
- Extensive testing on testnets
- Security audit of modular architecture
- Performance benchmarking vs original contract
3. **Gradual migration strategy**
- Deploy modular version alongside original
- Comparative testing in production conditions
- Gradual migration of functionality
## Key Insights from Refactoring
### **1. Component Boundaries Well-Defined**
The refactoring revealed clear separation of concerns:
- **Mathematical utilities** are pure functions (no state)
- **Oracle logic** depends only on external data
- **Position strategy** has clear input/output boundaries
- **Main contract** orchestrates components effectively
### **2. Anti-Arbitrage Strategy More Understandable**
Breaking down the complex `_set()` function into component parts:
- **ANCHOR position** creates shallow liquidity (high slippage)
- **DISCOVERY position** depends on anchor minting
- **FLOOR position** uses VWAP for historical memory
- **Dependencies** follow economic logic precisely
### **3. Testing Strategy More Effective**
- **Component tests** catch issues early in development
- **Integration tests** validate system behavior
- **Mock implementations** enable isolated debugging
- **Fuzz testing** more targeted and effective
## Validation of Refactoring Success
### ✅ **Functional Equivalence**
- Original anti-arbitrage test still passes
- Mathematical calculations identical
- Position creation logic preserved
- Event emission maintained
### ✅ **Architectural Improvement**
- Clear component boundaries
- Enhanced testability
- Better code organization
- Maintained performance
### ✅ **Development Experience**
- Faster test execution for components
- Clearer error messages and debugging
- Better documentation through test structure
- Enhanced maintainability
## Conclusion
The test refactoring successfully demonstrates that the modular LiquidityManagerV2 architecture:
1. **✅ Maintains 100% functional compatibility** with the original design
2. **✅ Provides significantly enhanced testability** through component isolation
3. **✅ Improves code organization** without sacrificing performance
4. **✅ Enables better debugging and maintenance** through clear boundaries
5. **✅ Preserves the proven anti-arbitrage protection** mechanism
The comprehensive test suite provides confidence that the modular architecture can be safely deployed as a drop-in replacement for the original LiquidityManager while providing substantial benefits for ongoing development and maintenance.

View file

@ -1,120 +0,0 @@
# VWAP Test Gaps Analysis - Post Dormant Whale Fix
## Executive Summary
With the dormant whale protection implementation, comprehensive VWAPTracker test suite, and anti-arbitrage strategy validation, the most critical security gaps have been addressed. Remaining gaps are lower-priority LiquidityManager integration tests.
## Context: Anti-Arbitrage Strategy
**Trade-Recenter-Reverse Attack**: Trader profits by exploiting predictable rebalancing through: large trade → trigger recenter() → reverse trade at new configuration.
**Protection**: Asymmetric slippage profile where ANCHOR (shallow liquidity, high slippage) forces attackers through expensive slippage twice, while FLOOR/DISCOVERY (deep liquidity) provide edge protection.
**Key Architectural Principle**: Only FLOOR uses VWAP (historical memory), while ANCHOR/DISCOVERY use current tick (immediate response).
---
## VWAPTracker Tests - COMPREHENSIVE ✅
### Core VWAP Logic ✅
- ✅ **Basic VWAP calculation** (`testSinglePriceRecording`, `testMultiplePriceRecording`)
- ✅ **Volume recording from fees** (`testSinglePriceRecording` - validates `volume = fee * 100`)
- ✅ **Multi-trade accumulation** (`testConcreteMultipleRecordings` - mathematical precision)
- ✅ **70% discount + capital inefficiency** (`testAdjustedVWAPCalculation`)
- ✅ **Zero volume edge cases** (`testZeroVolumeHandling`, `testInitialState`)
### Security & Edge Cases ✅
- ✅ **Dormant whale protection** (`testDormantWhaleProtection` - core security)
- ✅ **Overflow handling** (`testOverflowHandling`, `testOverflowCompressionRatio`)
- ✅ **Compression preserves history** (prevents whale manipulation)
- ✅ **Extreme values** (`testLargeButSafeValues`, `testMinimalValues`)
- ✅ **Fuzz testing** (`testFuzzVWAPCalculation` - random inputs)
### Capital Inefficiency ✅
- ✅ **Zero capital inefficiency** (`testAdjustedVWAPWithZeroCapitalInefficiency`)
- ✅ **Max capital inefficiency** (`testAdjustedVWAPWithMaxCapitalInefficiency`)
- ✅ **Variable impact testing** (`testAdjustedVWAPCalculation`)
**Result**: VWAPTracker has comprehensive test coverage. No gaps remain.
---
## LiquidityManager Integration Tests
### Priority: HIGH
#### 1. Anti-Arbitrage Strategy Validation ✅
**Status**: COMPLETED
**Goal**: Prove asymmetric slippage profile protects against trade-recenter-reverse attacks
**Details**: Verified ANCHOR (shallow) vs FLOOR/DISCOVERY (deep) liquidity creates expensive round-trip slippage
**Test Location**: `test/LiquidityManager.t.sol::testAntiArbitrageStrategyValidation()`
**Results**: 80% slippage loss, 17% anchor ratio, validates protection mechanism
**Priority Justification**: Core protection mechanism against sophisticated arbitrage
#### 2. Position Dependency Order Test
**Status**: MISSING
**Goal**: Verify _set() function order: ANCHOR → DISCOVERY → FLOOR with correct dependencies
**Details**: Test that discovery amount depends on anchor's pulledHarb, floor depends on final outstanding supply after all minting
**Test Location**: Should be in `test/LiquidityManager.t.sol`
**Priority Justification**: Validates economic logic behind position ordering
#### 3. Floor Position VWAP Exclusivity Test
**Status**: MISSING
**Goal**: Prove only floor position uses VWAP, anchor/discovery use current tick
**Details**: Critical architectural verification - ensure VWAP only affects floor positioning for historical memory
**Test Location**: Should be in `test/LiquidityManager.t.sol`
**Priority Justification**: Validates separation of historical (floor) vs immediate (anchor/discovery) price responses
#### 4. EthScarcity vs EthAbundance Scenarios
**Status**: MISSING
**Goal**: Test EthScarcity vs EthAbundance event emission and VWAP application logic
**Details**: Verify different VWAP usage when ETH reserves < vs > required buyback amount
**Test Location**: Should be in `test/LiquidityManager.t.sol`
### Priority: MEDIUM
#### 5. Floor Position Discount Verification
**Status**: PARTIALLY COVERED
**Goal**: Verify floor position pricing uses adjusted VWAP (70% + capital inefficiency)
**Details**: Current `testVWAPIntegrationValidation` touches this but needs dedicated precision testing.
**Gap**: Need exact discount formula verification in isolation.
### Priority: LOW
#### 6. Cross-Position Independence
**Status**: MISSING
**Goal**: Verify anchor/discovery positions unaffected by VWAP changes
**Details**: Ensure VWAP compression/changes don't affect non-floor positions.
**Note**: Low priority as architecture makes this unlikely to break.
## Completed ✅
### ✅ Anti-Arbitrage Strategy Validation
**Status**: Completed
**Goal**: Prove asymmetric slippage profile protects against trade-recenter-reverse attacks
**File**: `test/LiquidityManager.t.sol::testAntiArbitrageStrategyValidation()`
**Results**: 80% round-trip slippage loss, 17% anchor liquidity ratio, validates protection mechanism
**What it tests**: Trade-recenter-reverse attack simulation, asymmetric liquidity profile validation, economic attack deterrent
### ✅ VWAP Integration Validation
**Status**: Completed
**Goal**: Validate VWAP system stability and cross-component behavior
**File**: `test/LiquidityManager.t.sol::testVWAPIntegrationValidation()`
**What it tests**: System stability, reasonable value ranges, floor positioning differences, cumulative data accumulation
---
## Notes for Implementation
- Current test file: `/home/ubuntu/dev/harb/onchain/test/LiquidityManager.t.sol`
- Next recommended task: **Position Dependency Order Test** (highest remaining priority)
- Key insight discovered: Floor positioned at tick -176700 vs current -113852 shows 62k tick difference, confirming VWAP-based positioning
- Token ordering: HARB is token0 in test setup (`DEFAULT_TOKEN0_IS_WETH = false`)
- **_set() Function Analysis**: Position order ANCHOR → DISCOVERY → FLOOR reflects economic dependencies for anti-arbitrage protection
## Test Architecture Pattern
Each test should:
1. Use `_setupCustom()` for custom scenarios
2. Include proper assertions (not just console.log)
3. Test specific functionality, not general integration
4. Consider token ordering implications (HARB/WETH vs WETH/HARB)
5. Verify economic logic makes sense

View file

@ -31,16 +31,23 @@ import "../test/mocks/MockOptimizer.sol";
uint24 constant FEE = uint24(10_000); // 1% fee
int24 constant TICK_SPACING = 200;
int24 constant ANCHOR_SPACING = 5 * TICK_SPACING;
int24 constant EXTREME_PRICE_MARGIN = 12000; // Safety margin from tick boundaries
// Time constants
uint256 constant ORACLE_UPDATE_INTERVAL = 5 hours;
// Trading constants
uint256 constant NORMALIZATION_SELL_PERCENTAGE = 100; // 1%
uint256 constant NORMALIZATION_BUY_AMOUNT = 0.01 ether;
uint256 constant BALANCE_DIVISOR = 2;
uint256 constant MIN_TRADE_AMOUNT = 1 ether;
uint256 constant FALLBACK_TRADE_DIVISOR = 10;
// Test bounds constants
uint8 constant MIN_FUZZ_ACTIONS = 5;
uint8 constant MAX_FUZZ_ACTIONS = 50;
uint8 constant MIN_FUZZ_FREQUENCY = 1;
uint8 constant MAX_FUZZ_FREQUENCY = 20;
// Test setup constants
uint256 constant INITIAL_LM_ETH_BALANCE = 10 ether;
uint256 constant OVERFLOW_TEST_BALANCE = 201 ether;
uint256 constant FUZZ_TEST_BALANCE = 20 ether;
uint256 constant VWAP_TEST_BALANCE = 100 ether;
// Error handling constants
bytes32 constant AMPLITUDE_ERROR = keccak256("amplitude not reached.");
@ -154,7 +161,7 @@ contract LiquidityManagerTest is UniswapTestBase {
harberg.setStakingPool(address(stake));
vm.prank(feeDestination);
harberg.setLiquidityManager(address(lm));
vm.deal(address(lm), 10 ether);
vm.deal(address(lm), INITIAL_LM_ETH_BALANCE);
}
/// @notice Intelligent recenter function that handles extreme price conditions
@ -173,15 +180,8 @@ contract LiquidityManagerTest is UniswapTestBase {
/// @notice Handles extreme price conditions with normalizing swaps
function _handleExtremePrice() internal {
(, int24 currentTick,,,,,) = pool.slot0();
if (_isExtremelyExpensive(currentTick)) {
console.log("Detected extremely expensive HARB, performing normalizing swap...");
_performNormalizingSwap(currentTick, true);
} else if (_isExtremelyCheap(currentTick)) {
console.log("Detected extremely cheap HARB, performing normalizing swap...");
_performNormalizingSwap(currentTick, false);
}
// Use the unified extreme price handling from UniswapTestBase
handleExtremePrice();
}
/// @notice Attempts the recenter operation with proper error handling
@ -194,19 +194,6 @@ contract LiquidityManagerTest is UniswapTestBase {
}
}
/// @notice Checks if HARB price is extremely expensive
/// @param currentTick The current price tick
/// @return True if HARB is extremely expensive
function _isExtremelyExpensive(int24 currentTick) internal pure returns (bool) {
return currentTick >= TickMath.MAX_TICK - EXTREME_PRICE_MARGIN;
}
/// @notice Checks if HARB price is extremely cheap
/// @param currentTick The current price tick
/// @return True if HARB is extremely cheap
function _isExtremelyCheap(int24 currentTick) internal pure returns (bool) {
return currentTick <= TickMath.MIN_TICK + EXTREME_PRICE_MARGIN;
}
/// @notice Validates recenter operation results
/// @param isUp Whether the recenter moved positions up or down
@ -248,55 +235,6 @@ contract LiquidityManagerTest is UniswapTestBase {
}
}
/// @notice Performs a normalizing swap to bring extreme prices back to manageable levels
/// @param currentTick The current tick position
/// @param isExpensive True if HARB is extremely expensive, false if extremely cheap
function _performNormalizingSwap(int24 currentTick, bool isExpensive) internal {
console.log("Current tick before normalization:", vm.toString(currentTick));
if (isExpensive) {
// HARB is extremely expensive - we need to bring the price DOWN
// This means we need to SELL HARB for ETH (not buy HARB with ETH)
// Get HARB balance from account (who has been buying) to use for normalization
uint256 accountHarbBalance = harberg.balanceOf(account);
if (accountHarbBalance > 0) {
uint256 harbToSell = accountHarbBalance / NORMALIZATION_SELL_PERCENTAGE; // Sell 1% of account's HARB balance
if (harbToSell == 0) harbToSell = 1; // Minimum 1 wei
vm.prank(account);
harberg.transfer(address(this), harbToSell);
console.log("Performing normalizing swap: selling", vm.toString(harbToSell), "HARB to bring price down");
// Approve for swap
harberg.approve(address(pool), harbToSell);
// Swap should work - if it doesn't, there's a fundamental problem
performSwap(harbToSell, false); // false = selling HARB for ETH
} else {
console.log("No HARB balance available for normalization");
}
} else {
// HARB is extremely cheap - we need to bring the price UP
// This means we need to BUY HARB with ETH (not sell HARB)
uint256 ethToBuy = NORMALIZATION_BUY_AMOUNT; // Small amount for price normalization
// Ensure we have enough ETH
if (weth.balanceOf(address(this)) < ethToBuy) {
vm.deal(address(this), ethToBuy);
weth.deposit{value: ethToBuy}();
}
console.log("Performing normalizing swap: buying HARB with", vm.toString(ethToBuy), "ETH to bring price up");
performSwap(ethToBuy, true); // true = buying HARB with ETH
}
// Check the new price
(, int24 newTick,,,,,) = pool.slot0();
console.log("New tick after normalization:", vm.toString(newTick));
console.log("Price change:", vm.toString(newTick - currentTick), "ticks");
}
/// @notice Retrieves liquidity position information for a specific stage
/// @param s The liquidity stage (FLOOR, ANCHOR, DISCOVERY)
@ -411,7 +349,7 @@ contract LiquidityManagerTest is UniswapTestBase {
/// @notice Tests overflow handling in cumulative calculations
/// @dev Simulates extreme values that could cause arithmetic overflow
function testHandleCumulativeOverflow() public {
_setupCustom(false, 201 ether);
_setupCustom(false, OVERFLOW_TEST_BALANCE);
vm.store(address(lm), bytes32(uint256(0)), bytes32(uint256(type(uint256).max - 10)));
@ -507,7 +445,7 @@ contract LiquidityManagerTest is UniswapTestBase {
// Test client-side detection and normalization
console.log("\n=== PHASE 2: Test client-side normalization ===");
if (postBuyTick >= TickMath.MAX_TICK - EXTREME_PRICE_MARGIN) {
if (postBuyTick >= TickMath.MAX_TICK - 15000) {
console.log("[SUCCESS] Successfully pushed to extreme expensive range");
console.log("[SUCCESS] Client-side detection should trigger normalization swap");
} else {
@ -740,9 +678,9 @@ contract LiquidityManagerTest is UniswapTestBase {
// Diagnose the scenario type
console.log("\n=== SCENARIO DIAGNOSIS ===");
if (postBuyTick >= TickMath.MAX_TICK - EXTREME_PRICE_MARGIN) {
if (postBuyTick >= TickMath.MAX_TICK - 15000) {
console.log("[DIAGNOSIS] EXTREME EXPENSIVE HARB - should trigger normalization");
} else if (postBuyTick <= TickMath.MIN_TICK + EXTREME_PRICE_MARGIN) {
} else if (postBuyTick <= TickMath.MIN_TICK + 15000) {
console.log("[DIAGNOSIS] EXTREME CHEAP HARB - potential protocol death");
} else {
console.log("[DIAGNOSIS] NORMAL RANGE - may still have arithmetic issues");
@ -796,7 +734,7 @@ contract LiquidityManagerTest is UniswapTestBase {
function _calculateBuyAmount(uint256 baseAmount) internal view returns (uint256) {
uint256 wethBalance = weth.balanceOf(account);
uint256 amount = baseAmount % (wethBalance / BALANCE_DIVISOR);
return amount == 0 ? wethBalance / 10 : amount;
return amount == 0 ? wethBalance / FALLBACK_TRADE_DIVISOR : amount;
}
// ========================================
@ -810,11 +748,11 @@ contract LiquidityManagerTest is UniswapTestBase {
/// @param frequency How often to trigger recentering operations
/// @param amounts Array of trade amounts to use (bounded automatically)
function testFuzzRobustness(uint8 numActions, uint8 frequency, uint8[] calldata amounts) public {
vm.assume(numActions > 5 && numActions < 50); // Reasonable bounds for unit testing
vm.assume(frequency > 0 && frequency < 20);
vm.assume(numActions > MIN_FUZZ_ACTIONS && numActions < MAX_FUZZ_ACTIONS); // Reasonable bounds for unit testing
vm.assume(frequency > MIN_FUZZ_FREQUENCY && frequency < MAX_FUZZ_FREQUENCY);
vm.assume(amounts.length >= numActions);
_setupCustom(numActions % 2 == 0 ? true : false, 20 ether);
_setupCustom(numActions % 2 == 0 ? true : false, FUZZ_TEST_BALANCE);
uint256 traderBalanceBefore = weth.balanceOf(account);
@ -879,7 +817,7 @@ contract LiquidityManagerTest is UniswapTestBase {
/// @dev Validates VWAP accumulation, floor positioning, and system stability across trading sequences
function testVWAPIntegrationValidation() public {
// Setup with known initial conditions
_setupCustom(false, 100 ether);
_setupCustom(false, VWAP_TEST_BALANCE);
// Record initial state - should be zero volume
assertEq(lm.cumulativeVolumeWeightedPriceX96(), 0, "Initial VWAP should be zero");
@ -1006,7 +944,7 @@ contract LiquidityManagerTest is UniswapTestBase {
/// @notice Tests the asymmetric slippage profile that protects against trade-recenter-reverse attacks
/// @dev Validates that ANCHOR (shallow) vs FLOOR/DISCOVERY (deep) liquidity creates expensive round-trip slippage
function testAntiArbitrageStrategyValidation() public {
_setupCustom(false, 100 ether); // HARB is token0, large balance for meaningful slippage testing
_setupCustom(false, VWAP_TEST_BALANCE); // HARB is token0, large balance for meaningful slippage testing
// Phase 1: Record initial state and execute first large trade
(, int24 initialTick,,,,,) = pool.slot0();

View file

@ -12,7 +12,7 @@ library CSVHelper {
*/
function createPositionsHeader() internal pure returns (string memory) {
return
"precedingAction, currentTick, floorTickLower, floorTickUpper, floorEth, floorHarb, anchorTickLower, anchorTickUpper, anchorEth, anchorHarb, discoveryTickLower, discoveryTickUpper, discoveryEth, discoveryHarb";
"precedingAction, currentTick, floorTickLower, floorTickUpper, floorEth, floorHarb, anchorTickLower, anchorTickUpper, anchorEth, anchorHarb, discoveryTickLower, discoveryTickUpper, discoveryEth, discoveryHarb, token0isWeth";
}
function createTimeSeriesHeader() internal pure returns (string memory) {

View file

@ -43,27 +43,73 @@ abstract contract UniswapTestBase is Test {
if (zeroForOne) {
// Swapping token0 for token1 - price goes down
// sqrtPriceLimitX96 must be less than current price but greater than MIN_SQRT_RATIO
limit = TickMath.MIN_SQRT_RATIO + 1;
uint160 minAllowedLimit = TickMath.MIN_SQRT_RATIO + 1;
if (currentSqrtPrice <= minAllowedLimit + PRICE_LIMIT_BUFFER) {
// If we're very close to the min, use the absolute minimum
limit = minAllowedLimit;
} else {
// Use a limit that's reasonably below current price to avoid SPL
// Set limit to be halfway between MIN_SQRT_RATIO and current price
limit = minAllowedLimit + (currentSqrtPrice - minAllowedLimit) / 2;
}
} else {
// Swapping token1 for token0 - price goes up
// sqrtPriceLimitX96 must be greater than current price but less than MAX_SQRT_RATIO
// At extreme high prices, we need to be more conservative with the limit
uint160 maxAllowedLimit = TickMath.MAX_SQRT_RATIO - 1;
if (currentSqrtPrice >= maxAllowedLimit - 1000) {
if (currentSqrtPrice >= maxAllowedLimit - PRICE_LIMIT_BUFFER) {
// If we're very close to the max, use a more conservative limit
limit = currentSqrtPrice + (maxAllowedLimit - currentSqrtPrice) / 2;
} else {
limit = maxAllowedLimit;
// Use a limit that's reasonably above current price to avoid SPL
// Set limit to be halfway between current price and MAX_SQRT_RATIO
limit = currentSqrtPrice + (maxAllowedLimit - currentSqrtPrice) / 2;
}
}
pool.swap(account, zeroForOne, int256(amount), limit, abi.encode(account, int256(amount), isBuy));
}
/**
* @notice Performs a swap with aggressive price limits for extreme price normalization
* @param amount The amount to swap
* @param isBuy True if buying HARB, false if selling HARB
*/
function performSwapWithAggressiveLimits(uint256 amount, bool isBuy) internal {
uint160 limit;
// Determine the swap direction
bool zeroForOne = isBuy ? token0isWeth : !token0isWeth;
if (isBuy) {
vm.prank(account);
weth.transfer(address(this), amount);
} else {
vm.prank(account);
harberg.approve(address(this), amount);
}
// Set aggressive price limits that allow price to move to liquidity ranges
if (zeroForOne) {
// Swapping token0 for token1 - price goes down
// Use very aggressive limit close to MIN_SQRT_RATIO
limit = TickMath.MIN_SQRT_RATIO + 1;
} else {
// Swapping token1 for token0 - price goes up
// Use very aggressive limit close to MAX_SQRT_RATIO
limit = TickMath.MAX_SQRT_RATIO - 1;
}
pool.swap(account, zeroForOne, int256(amount), limit, abi.encode(account, int256(amount), isBuy));
}
/**
* @dev The Uniswap V3 swap callback.
*/
function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata _data) external {
// Handle the case where no swap occurred (both deltas are 0)
if (amount0Delta == 0 && amount1Delta == 0) {
return;
}
require(amount0Delta > 0 || amount1Delta > 0);
(address seller,, bool isBuy) = abi.decode(_data, (address, uint256, bool));
@ -99,4 +145,78 @@ abstract contract UniswapTestBase is Test {
weth.transfer(msg.sender, amount1Owed);
}
}
// ========================================
// EXTREME PRICE HANDLING
// ========================================
// Safety margin to prevent tick boundary violations (conservative approach)
int24 constant TICK_BOUNDARY_SAFETY_MARGIN = 15000;
// Price normalization constants
uint256 constant NORMALIZATION_HARB_PERCENTAGE = 100; // 1% of HARB balance
uint256 constant NORMALIZATION_ETH_AMOUNT = 0.01 ether; // Fixed ETH amount for normalization
uint256 constant MAX_NORMALIZATION_ATTEMPTS = 3; // Prevent infinite loops
uint256 constant PRICE_LIMIT_BUFFER = 1000; // Buffer from sqrt price limits
/**
* @notice Handles extreme price conditions by executing normalizing trades
* @dev This function should be called before any recenter operation to ensure
* the price is within safe boundaries for liquidity position creation
*/
function handleExtremePrice() internal {
uint256 attempts = 0;
while (attempts < MAX_NORMALIZATION_ATTEMPTS) {
(, int24 currentTick,,,,,) = pool.slot0();
if (currentTick >= TickMath.MAX_TICK - TICK_BOUNDARY_SAFETY_MARGIN) {
_executeNormalizingTrade(true); // Move price down
attempts++;
} else if (currentTick <= TickMath.MIN_TICK + TICK_BOUNDARY_SAFETY_MARGIN) {
_executeNormalizingTrade(false); // Move price up
attempts++;
} else {
// Price is now safe, exit loop
break;
}
}
}
/**
* @notice Executes a small trade to move price away from tick boundaries
* @param moveDown True to move price down (sell HARB), false to move price up (buy HARB)
*/
function _executeNormalizingTrade(bool moveDown) internal {
if (moveDown) {
// Need to move price DOWN (reduce HARB price)
// This means: sell HARB for ETH (increase HARB supply in pool)
uint256 harbBalance = harberg.balanceOf(account);
if (harbBalance > 0) {
// Use 1% of account's HARB balance (conservative approach like original)
uint256 harbToSell = harbBalance / NORMALIZATION_HARB_PERCENTAGE;
if (harbToSell == 0) harbToSell = 1;
vm.prank(account);
harberg.transfer(address(this), harbToSell);
harberg.approve(address(pool), harbToSell);
// Sell HARB for ETH with aggressive price limits for normalization
performSwapWithAggressiveLimits(harbToSell, false);
}
} else {
// Need to move price UP (increase HARB price)
// This means: buy HARB with ETH (reduce HARB supply in pool)
uint256 ethBalance = weth.balanceOf(account);
if (ethBalance > 0) {
// Use small amount for normalization (like original)
uint256 ethToBuy = NORMALIZATION_ETH_AMOUNT;
if (ethToBuy > ethBalance) ethToBuy = ethBalance;
// Buy HARB with ETH with aggressive price limits for normalization
performSwapWithAggressiveLimits(ethToBuy, true);
}
}
}
}