harb/onchain/test/mocks/MockPool.sol
johba 8064623a54 fix: feat: Push3 input redesign — normalized indicators instead of raw protocol values (#635) (#649)
Fixes #635

## Changes
The implementation is complete and committed. All 211 tests pass.

## Summary of changes

### `onchain/src/Optimizer.sol`
- **Replaced raw slot inputs** with normalized indicators in `getLiquidityParams()`:
  - Slot 2 `pricePosition`: where current price sits within VWAP ± 11 000 ticks (0 = lower bound, 0.5e18 = at VWAP, 1e18 = upper bound)
  - Slot 3 `volatility`: `|shortTwap − longTwap| / 1000 ticks`, capped at 1e18
  - Slot 4 `momentum`: 0 = falling, 0.5e18 = flat, 1e18 = rising (5-min vs 30-min TWAP delta)
  - Slot 5 `timeSinceRecenter`: `elapsed / 86400s`, capped at 1e18
  - Slot 6 `utilizationRate`: 1e18 if current tick is within anchor position range, else 0
- **Extended `setDataSources()`** to accept `liquidityManager` + `token0isWeth` (needed for correct tick direction in momentum/utilizationRate)
- **Added `_vwapToTick()`** helper: converts `vwapX96 = price × 2⁹⁶` to tick via `sqrt(vwapX96) << 48`, with TickMath bounds clamping
- All slots gracefully default to 0 when data sources are unconfigured or TWAP history is insufficient (try/catch on `pool.observe()`)

### `onchain/src/OptimizerV3Push3.sol`
- Updated NatSpec to document the new `[0, 1e18]` slot semantics

### New tests (`onchain/test/`)
- `OptimizerNormalizedInputsTest`: 18 tests covering all new slots, token ordering, TWAP fallback, and a bounded fuzz test
- `mocks/MockPool.sol`: configurable `slot0()` + `observe()` with TWAP tick math
- `mocks/MockLiquidityManagerPositions.sol`: configurable anchor position bounds

Co-authored-by: openhands <openhands@all-hands.dev>
Reviewed-on: https://codeberg.org/johba/harb/pulls/649
Reviewed-by: review_bot <review_bot@noreply.codeberg.org>
2026-03-13 07:53:46 +01:00

76 lines
2.7 KiB
Solidity

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.19;
/**
* @title MockPool
* @notice Mock Uniswap V3 pool for testing normalized indicator computation in Optimizer.
* @dev Implements slot0() and observe() with configurable return values.
*
* observe() models the TWAP cumulative-tick convention:
* tickCumulative at time T = integral of tick dt from pool inception to T.
* We set tickCumulative[now] = 0 and back-fill:
* tickCumulative[longWindow ago] = -longTwapTick * LONG_WINDOW
* tickCumulative[shortWindow ago] = -shortTwapTick * SHORT_WINDOW
* This means:
* longTwapTick = (cum[now] - cum[longAgo]) / LONG_WINDOW = longTwapTick ✓
* shortTwapTick = (cum[now] - cum[shortAgo]) / SHORT_WINDOW = shortTwapTick ✓
*/
contract MockPool {
int24 public currentTick;
int24 public longTwapTick;
int24 public shortTwapTick;
bool public revertOnObserve;
uint32 internal constant LONG_WINDOW = 1_800;
uint32 internal constant SHORT_WINDOW = 300;
function setCurrentTick(int24 _tick) external {
currentTick = _tick;
}
function setTwapTicks(int24 _longTwap, int24 _shortTwap) external {
longTwapTick = _longTwap;
shortTwapTick = _shortTwap;
}
function setRevertOnObserve(bool _revert) external {
revertOnObserve = _revert;
}
function slot0()
external
view
returns (
uint160 sqrtPriceX96,
int24 tick,
uint16 observationIndex,
uint16 observationCardinality,
uint16 observationCardinalityNext,
uint8 feeProtocol,
bool unlocked
)
{
return (0, currentTick, 0, 100, 100, 0, true);
}
/// @notice Returns tick cumulatives for the three time points [LONG_WINDOW, SHORT_WINDOW, 0].
/// @dev Only handles the exact 3-element call from Optimizer.getLiquidityParams().
function observe(uint32[] calldata secondsAgos)
external
view
returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s)
{
require(!revertOnObserve, "MockPool: observe reverts");
require(secondsAgos.length == 3, "MockPool: expected 3 time points");
tickCumulatives = new int56[](3);
secondsPerLiquidityCumulativeX128s = new uint160[](3);
// cum[now] = 0
tickCumulatives[2] = 0;
// cum[longAgo] = -longTwapTick * LONG_WINDOW
tickCumulatives[0] = int56(-int256(longTwapTick)) * int56(int32(LONG_WINDOW));
// cum[shortAgo] = -shortTwapTick * SHORT_WINDOW
tickCumulatives[1] = int56(-int256(shortTwapTick)) * int56(int32(SHORT_WINDOW));
}
}