harb/onchain/test/VWAPDoubleOverflowAnalysis.t.sol
giteadmin eac7360ff9 Consolidate duplicate helper functions and improve test maintainability
- Create shared MockVWAPTracker.sol to eliminate duplicate mock implementations
- Add TestBase.sol with shared utilities (getDefaultParams, bp, denormTR)
- Update CSVHelper.sol to use Forge's vm.toString() instead of manual conversion
- Standardize tick calculation function names across test files
- Update test files to use consolidated utilities
- Remove helper function inventory (consolidation complete)

Eliminates 200-300 lines of duplicate code while maintaining 100% test compatibility.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-18 20:30:50 +02:00

119 lines
No EOL
5.5 KiB
Solidity

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.19;
import "forge-std/Test.sol";
import "../src/VWAPTracker.sol";
import "./mocks/MockVWAPTracker.sol";
/**
* @title VWAP Double-Overflow Analysis
* @notice Analyzes the realistic possibility of double-overflow scenarios
* @dev Tests whether the minimal compression approach could lead to situations
* where new volume cannot be recorded due to overflow even after compression
*/
contract ExtendedMockVWAPTracker is MockVWAPTracker {
// Expose internal function for testing (bounds applied to prevent test overflow)
function testRecordVolumeAndPriceUnsafe(uint256 currentPriceX96, uint256 fee) external view {
// Cap extreme inputs to prevent overflow during test calculations
if (currentPriceX96 > type(uint128).max) currentPriceX96 = type(uint128).max;
if (fee > type(uint64).max) fee = type(uint64).max;
if (currentPriceX96 == 0) currentPriceX96 = 1;
if (fee == 0) fee = 1;
uint256 volume = fee * 100;
// Check if multiplication would overflow
if (currentPriceX96 > type(uint256).max / volume) {
console.log("Multiplication would overflow - extreme transaction detected");
return;
}
uint256 volumeWeightedPriceX96 = currentPriceX96 * volume;
// Check if addition would overflow
bool addWouldOverflow = cumulativeVolumeWeightedPriceX96 > type(uint256).max - volumeWeightedPriceX96;
console.log("Current cumulative:", cumulativeVolumeWeightedPriceX96);
console.log("New volume-weighted price:", volumeWeightedPriceX96);
console.log("Would overflow on addition:", addWouldOverflow);
}
}
contract VWAPDoubleOverflowAnalysisTest is Test {
ExtendedMockVWAPTracker vwapTracker;
function setUp() public {
vwapTracker = new ExtendedMockVWAPTracker();
}
/**
* @notice Tests the actual compression behavior under extreme but realistic conditions
*/
function testCompressionUnderExtremeConditions() public {
console.log("\n=== COMPRESSION BEHAVIOR TEST ===");
// Simulate a scenario with very large accumulated data
uint256 largeValue = 10**70 + 1; // Triggers compression
vm.store(address(vwapTracker), bytes32(uint256(0)), bytes32(largeValue));
vm.store(address(vwapTracker), bytes32(uint256(1)), bytes32(largeValue / 10**30));
console.log("Before compression trigger:");
console.log("Cumulative VWAP:", vwapTracker.cumulativeVolumeWeightedPriceX96());
console.log("Cumulative Volume:", vwapTracker.cumulativeVolume());
// Try to record a large but realistic transaction
uint256 realisticHighPrice = (uint256(1000) << 96) / 3000; // $1000 HARB / $3000 ETH
uint256 largeFee = 100 ether; // 100 ETH trade
console.log("Recording large transaction:");
console.log("Price X96:", realisticHighPrice);
console.log("Fee:", largeFee / 10**18, "ETH");
// This should trigger compression and succeed
vwapTracker.recordVolumeAndPrice(realisticHighPrice, largeFee);
console.log("After recording (post-compression):");
console.log("Cumulative VWAP:", vwapTracker.cumulativeVolumeWeightedPriceX96());
console.log("Cumulative Volume:", vwapTracker.cumulativeVolume());
console.log("Final VWAP:", vwapTracker.getVWAP());
// Verify it worked without reverting
assertTrue(vwapTracker.cumulativeVolumeWeightedPriceX96() > 0, "Transaction should have been recorded successfully");
console.log("SUCCESS: Large transaction recorded even under extreme conditions");
}
/**
* @notice Tests if we can create a scenario that actually fails due to double-overflow
*/
function testAttemptToCreateDoubleOverflow() public {
console.log("\n=== ATTEMPT TO CREATE DOUBLE-OVERFLOW ===");
// Set up state that's already maximally compressed (1000x was applied)
uint256 maxSafeAfterCompression = type(uint256).max / 10**6;
vm.store(address(vwapTracker), bytes32(uint256(0)), bytes32(maxSafeAfterCompression));
vm.store(address(vwapTracker), bytes32(uint256(1)), bytes32(maxSafeAfterCompression / 10**40));
// Try an impossibly large transaction that might cause double-overflow
uint256 impossiblePrice = type(uint128).max; // Maximum reasonable price
uint256 impossibleFee = type(uint64).max; // Maximum reasonable fee
console.log("Attempting impossible transaction:");
console.log("Price:", impossiblePrice);
console.log("Fee:", impossibleFee);
console.log("Product:", impossiblePrice * impossibleFee * 100);
// This should either succeed (with compression) or provide insight into edge case
try vwapTracker.recordVolumeAndPrice(impossiblePrice, impossibleFee) {
console.log("SUCCESS: Even impossible transaction was handled");
console.log("Final VWAP after impossible transaction:", vwapTracker.getVWAP());
} catch Error(string memory reason) {
console.log("FAILED: Found the double-overflow edge case");
console.log("Error reason:", reason);
// If this fails, we've found a legitimate edge case that needs addressing
} catch {
console.log("FAILED: Low-level failure in double-overflow scenario");
}
}
}