From defa1bfb6c72b331bf2c1fe3edaebb3415970063 Mon Sep 17 00:00:00 2001 From: openhands Date: Fri, 13 Mar 2026 09:11:31 +0000 Subject: [PATCH] fix: fix: Fitness metric should measure ETH only, not token value (#670) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace _positionEthValue() with _positionEthOnly() in FitnessEvaluator.t.sol. The new function returns only the WETH component of each position (amount0 if token0isWeth, else amount1), ignoring KRK token value entirely. This prevents evolution from gaming the fitness metric by inflating KRK price through position placement — the score now reflects actual ETH reserves only. Also removes the now-unused FullMath import. Co-Authored-By: Claude Sonnet 4.6 --- onchain/test/FitnessEvaluator.t.sol | 34 +++++++++-------------------- 1 file changed, 10 insertions(+), 24 deletions(-) diff --git a/onchain/test/FitnessEvaluator.t.sol b/onchain/test/FitnessEvaluator.t.sol index 20cdf40..2fc5052 100644 --- a/onchain/test/FitnessEvaluator.t.sol +++ b/onchain/test/FitnessEvaluator.t.sol @@ -43,7 +43,6 @@ import { UUPSUpgradeable } from "@openzeppelin/proxy/utils/UUPSUpgradeable.sol"; import { IERC20 } from "@openzeppelin/token/ERC20/IERC20.sol"; import { IUniswapV3Factory } from "@uniswap-v3-core/interfaces/IUniswapV3Factory.sol"; import { IUniswapV3Pool } from "@uniswap-v3-core/interfaces/IUniswapV3Pool.sol"; -import { FullMath } from "@aperture/uni-v3-lib/FullMath.sol"; import { LiquidityAmounts } from "@aperture/uni-v3-lib/LiquidityAmounts.sol"; import { TickMath } from "@aperture/uni-v3-lib/TickMath.sol"; import { UniswapHelpers } from "../src/helpers/UniswapHelpers.sol"; @@ -561,8 +560,9 @@ contract FitnessEvaluator is Test { // ─── Score computation ──────────────────────────────────────────────────── /** - * @notice Compute lm_eth_total = free ETH + free WETH + sum(position ETH values). - * Mirrors AttackRunner._logSnapshot's lm_eth_total calculation. + * @notice Compute lm_eth_total = free ETH + free WETH + sum(WETH in each position). + * Only counts real ETH reserves — KRK token value is excluded to prevent + * evolution from gaming the metric by inflating token price through positioning. */ function _computeLmEthTotal() internal view returns (uint256) { (uint160 sqrtPriceX96,,,,,,) = pool.slot0(); @@ -576,16 +576,16 @@ contract FitnessEvaluator is Test { return lmEthFree + lmWethFree - + _positionEthValue(sqrtPriceX96, fLo, fHi, fLiq) - + _positionEthValue(sqrtPriceX96, aLo, aHi, aLiq) - + _positionEthValue(sqrtPriceX96, dLo, dHi, dLiq); + + _positionEthOnly(sqrtPriceX96, fLo, fHi, fLiq) + + _positionEthOnly(sqrtPriceX96, aLo, aHi, aLiq) + + _positionEthOnly(sqrtPriceX96, dLo, dHi, dLiq); } /** - * @notice ETH-equivalent value of a Uniswap V3 position at the current price. - * Copied verbatim from AttackRunner._positionEthValue. + * @notice WETH-only component of a Uniswap V3 position at the current price. + * Ignores KRK token value entirely — counts only the actual ETH backing. */ - function _positionEthValue( + function _positionEthOnly( uint160 sqrtPriceX96, int24 tickLower, int24 tickUpper, @@ -601,21 +601,7 @@ contract FitnessEvaluator is Test { (uint256 amount0, uint256 amount1) = LiquidityAmounts.getAmountsForLiquidity(sqrtPriceX96, sqrtRatioAX96, sqrtRatioBX96, liquidity); - uint256 ethAmount = token0isWeth ? amount0 : amount1; - uint256 krkAmount = token0isWeth ? amount1 : amount0; - - if (krkAmount == 0 || sqrtPriceX96 == 0) return ethAmount; - - uint256 krkInEth; - if (token0isWeth) { - // token0=WETH, token1=KRK: 1 KRK = 2^192 / sqrtP^2 WETH - krkInEth = FullMath.mulDiv(FullMath.mulDiv(krkAmount, 1 << 96, sqrtPriceX96), 1 << 96, sqrtPriceX96); - } else { - // token0=KRK, token1=WETH: 1 KRK = sqrtP^2 / 2^192 WETH - krkInEth = FullMath.mulDiv(FullMath.mulDiv(krkAmount, sqrtPriceX96, 1 << 96), sqrtPriceX96, 1 << 96); - } - - return ethAmount + krkInEth; + return token0isWeth ? amount0 : amount1; } // ─── Utilities ────────────────────────────────────────────────────────────