feat: OptimizerV3 with direct 2D staking-to-LP parameter mapping
Core protocol changes for launch readiness: - OptimizerV3: binary bear/bull mapping from (staking%, avgTax) — avoids exploitable AW 30-90 kill zone. Bear: AS=30%, AW=100, CI=0, DD=0.3e18. Bull: AS=100%, AW=20, CI=0, DD=1e18. UUPS upgradeable with __gap[48]. - Directional VWAP: only records prices on ETH inflow (buys), preventing sell-side dilution of price memory - Floor formula: unified max(scarcity, mirror, clamp) — VWAP mirror uses distance from adjusted VWAP as floor distance, no branching - PriceOracle (M-1 fix): correct fallback TWAP divisor (60000s, not 300s) - Access control (M-2 fix): deployer-only guard on one-time setters - Recenter rate limit (M-3 fix): 60-second cooldown for open recenters - Safe fallback params: recenter() optimizer-failure defaults changed from exploitable CI=50%/AW=50 to safe bear-mode CI=0/AW=100 - Recentered event for monitoring and indexing - VERSION bump to 2, kraiken-lib COMPATIBLE_CONTRACT_VERSIONS updated Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
21857ae8ca
commit
85350caf52
38 changed files with 3793 additions and 205 deletions
|
|
@ -264,42 +264,47 @@ contract ThreePositionStrategyTest is TestConstants {
|
|||
assertNotEq(centerTick, CURRENT_TICK, "Floor should not be positioned at current tick when VWAP available");
|
||||
}
|
||||
|
||||
function testFloorPositionEthScarcity() public {
|
||||
function testFloorPositionScarcityDominates() public {
|
||||
ThreePositionStrategy.PositionParams memory params = getDefaultParams();
|
||||
|
||||
// Set up scenario where ETH is insufficient for VWAP price
|
||||
// Set up scenario where ETH is insufficient → scarcity tick dominates
|
||||
uint256 vwapX96 = 79_228_162_514_264_337_593_543_950_336 * 10; // High VWAP price
|
||||
strategy.setVWAP(vwapX96, 1000 ether);
|
||||
|
||||
uint256 smallEthBalance = 1 ether; // Insufficient ETH
|
||||
uint256 smallEthBalance = 1 ether; // Very low ETH → scarcity tick far away
|
||||
uint256 pulledHarb = 1000 ether;
|
||||
uint256 discoveryAmount = 500 ether;
|
||||
|
||||
// Should emit EthScarcity event (check event type, not exact values)
|
||||
vm.expectEmit(true, false, false, false);
|
||||
emit ThreePositionStrategy.EthScarcity(CURRENT_TICK, 0, 0, 0, 0);
|
||||
|
||||
// Should not revert — floor placed using max(scarcity, mirror, clamp)
|
||||
strategy.setFloorPosition(CURRENT_TICK, smallEthBalance, pulledHarb, discoveryAmount, params);
|
||||
|
||||
// Floor should be minted (position exists)
|
||||
MockThreePositionStrategy.MintedPosition memory pos = strategy.getMintedPosition(0);
|
||||
assertTrue(pos.liquidity > 0, "Floor should have liquidity");
|
||||
}
|
||||
|
||||
function testFloorPositionEthAbundance() public {
|
||||
function testFloorPositionMirrorDominates() public {
|
||||
ThreePositionStrategy.PositionParams memory params = getDefaultParams();
|
||||
|
||||
// Set up scenario where ETH is sufficient for VWAP price
|
||||
// Set up scenario where VWAP is far from current → mirror tick dominates
|
||||
uint256 baseVwap = 79_228_162_514_264_337_593_543_950_336; // 1.0 in X96 format
|
||||
uint256 vwapX96 = baseVwap / 100_000; // Very low VWAP price to ensure abundance
|
||||
uint256 vwapX96 = baseVwap / 100_000; // Very low VWAP price → far from current
|
||||
strategy.setVWAP(vwapX96, 1000 ether);
|
||||
|
||||
uint256 largeEthBalance = 100_000 ether; // Very large ETH balance
|
||||
uint256 largeEthBalance = 100_000 ether; // Lots of ETH
|
||||
uint256 pulledHarb = 1000 ether;
|
||||
uint256 discoveryAmount = 500 ether;
|
||||
|
||||
// Should emit EthAbundance event (check event type, not exact values)
|
||||
// The exact VWAP and vwapTick values are calculated, so we just check the event type
|
||||
vm.expectEmit(true, false, false, false);
|
||||
emit ThreePositionStrategy.EthAbundance(CURRENT_TICK, 0, 0, 0, 0);
|
||||
|
||||
strategy.setFloorPosition(CURRENT_TICK, largeEthBalance, pulledHarb, discoveryAmount, params);
|
||||
|
||||
MockThreePositionStrategy.MintedPosition memory pos = strategy.getMintedPosition(0);
|
||||
assertTrue(pos.liquidity > 0, "Floor should have liquidity");
|
||||
|
||||
// Floor should be further than just anchorSpacing (mirror should push it)
|
||||
int24 anchorSpacing = 200 + (34 * 50 * 200 / 100); // 3600
|
||||
int24 floorCenter = (pos.tickLower + pos.tickUpper) / 2;
|
||||
// Mirror should push floor significantly beyond clamp minimum
|
||||
assertTrue(floorCenter > CURRENT_TICK + anchorSpacing + 200, "Mirror should push floor beyond clamp minimum");
|
||||
}
|
||||
|
||||
function testFloorPositionNoVWAP() public {
|
||||
|
|
@ -316,16 +321,34 @@ contract ThreePositionStrategyTest is TestConstants {
|
|||
|
||||
MockThreePositionStrategy.MintedPosition memory pos = strategy.getMintedPosition(0);
|
||||
|
||||
// Without VWAP, should default to current tick but adjusted for anchor spacing
|
||||
// Without VWAP, mirror = current tick, so floor uses max(scarcity, clamp)
|
||||
// With these balances, scarcity tick should dominate (low ETH relative to supply)
|
||||
int24 centerTick = (pos.tickLower + pos.tickUpper) / 2;
|
||||
// Expected spacing: TICK_SPACING + (34 * anchorWidth * TICK_SPACING / 100) = 200 + (34 * 50 * 200 / 100) = 3600
|
||||
int24 expectedSpacing = 200 + (34 * 50 * 200 / 100);
|
||||
assertApproxEqAbs(
|
||||
uint256(int256(centerTick)),
|
||||
uint256(int256(CURRENT_TICK + expectedSpacing)),
|
||||
200,
|
||||
"Floor should be positioned away from current tick to avoid anchor overlap"
|
||||
);
|
||||
// Floor should be above current tick (on KRK-cheap side)
|
||||
assertTrue(centerTick > CURRENT_TICK, "Floor should be on KRK-cheap side of current tick");
|
||||
assertTrue(pos.liquidity > 0, "Floor should have liquidity");
|
||||
}
|
||||
|
||||
function testFloorPositionNoVWAPClampOrScarcity() public {
|
||||
ThreePositionStrategy.PositionParams memory params = getDefaultParams();
|
||||
|
||||
// No VWAP data, large ETH balance, small supply
|
||||
strategy.setVWAP(0, 0);
|
||||
|
||||
uint256 floorEthBalance = 100_000 ether; // Very large
|
||||
uint256 pulledHarb = 100 ether; // Small supply
|
||||
uint256 discoveryAmount = 50 ether;
|
||||
|
||||
strategy.setFloorPosition(CURRENT_TICK, floorEthBalance, pulledHarb, discoveryAmount, params);
|
||||
|
||||
MockThreePositionStrategy.MintedPosition memory pos = strategy.getMintedPosition(0);
|
||||
|
||||
// With no VWAP: mirror = current. Floor uses max(scarcity, clamp).
|
||||
// The scarcity formula with small supply and large ETH may still push floor
|
||||
// significantly beyond the clamp minimum. Just verify floor is on correct side.
|
||||
int24 centerTick = (pos.tickLower + pos.tickUpper) / 2;
|
||||
int24 minSpacing = 200 + (34 * 50 * 200 / 100); // 3600
|
||||
assertTrue(centerTick >= CURRENT_TICK + minSpacing, "Floor should be positioned away from current tick to avoid anchor overlap");
|
||||
}
|
||||
|
||||
function testFloorPositionOutstandingSupplyCalculation() public {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue