From 9dfd10aff7396eeaa40b85847bc2bba12ed8d126 Mon Sep 17 00:00:00 2001 From: Uncle Fatso Date: Mon, 6 Oct 2025 15:34:25 +0300 Subject: [PATCH] draft fixes for native-only support; tests broken Signed-off-by: Uncle Fatso --- src/StandardBondingCalculator.sol | 19 +++++--- src/Treasury.sol | 69 +++++++++++++++++++++++++-- src/interfaces/IBondingCalculator.sol | 1 + test/treasury/Treasury.t.sol | 2 +- 4 files changed, 81 insertions(+), 10 deletions(-) diff --git a/src/StandardBondingCalculator.sol b/src/StandardBondingCalculator.sol index 962f942..2818c3c 100644 --- a/src/StandardBondingCalculator.sol +++ b/src/StandardBondingCalculator.sol @@ -16,27 +16,33 @@ import "./interfaces/IBondingCalculator.sol"; contract GhostBondingCalculator is IBondingCalculator { using FixedPoint for *; + uint256 public override immutable fraction; address internal immutable ftso; - constructor(address _ftso) { + constructor(address _ftso, uint256 _numerator, uint256 _denominator) { ftso = _ftso; + fraction = FixedPoint.fraction(_numerator, _denominator).decode112with18(); } - function getKValue(address pair) public view returns (uint256 k) { + function getKValue(address pair, bool isCoefficient) public view returns (uint256 k) { uint256 token0 = IERC20Metadata(IUniswapV2Pair(pair).token0()).decimals(); uint256 token1 = IERC20Metadata(IUniswapV2Pair(pair).token1()).decimals(); uint256 decimals = token0 + token1 - IERC20Metadata(pair).decimals(); (uint256 reserve0, uint256 reserve1, ) = IUniswapV2Pair(pair).getReserves(); k = reserve0 * reserve1 / (10**decimals); + + if (isCoefficient) { + k = k * 1e18 / fraction; + } } - function getTotalValue(address pair) public view returns (uint256 value) { - return _sqrt(getKValue(pair)) * 2; + function getTotalValue(address pair, bool isCoefficient) public view returns (uint256) { + return _sqrt(getKValue(pair, isCoefficient)) * 2; } function valuation(address pair, uint256 amount) external view override returns (uint256 value) { - uint256 totalValue = getTotalValue(pair); + uint256 totalValue = getTotalValue(pair, true); uint256 totalSupply = IUniswapV2Pair(pair).totalSupply(); value = totalValue * FixedPoint.fraction(amount, totalSupply).decode112with18() / 1e18; @@ -52,7 +58,8 @@ contract GhostBondingCalculator is IBondingCalculator { reserve = reserve0; } - reserve = reserve * (2 * 1e9) / getTotalValue(pair); + reserve = reserve * (2 * 1e9) / getTotalValue(pair, false); + reserve = reserve * _sqrt(fraction) / 1e9; } function _sqrt(uint256 a) private pure returns (uint256 c) { diff --git a/src/Treasury.sol b/src/Treasury.sol index 7916717..ca679fe 100644 --- a/src/Treasury.sol +++ b/src/Treasury.sol @@ -190,7 +190,7 @@ contract GhostTreasury is GhostAccessControlled, ITreasury { stnk = someAddress; } else { permissions[status][someAddress] = true; - if (status == STATUS.LIQUIDITYTOKEN) { + if (status == STATUS.LIQUIDITYTOKEN || status == STATUS.RESERVETOKEN) { bondCalculator[someAddress] = calculatorAddress; } @@ -211,6 +211,66 @@ contract GhostTreasury is GhostAccessControlled, ITreasury { emit Permissioned(toDisable, status, false); } + // function redeemReserve( + // address router, + // address token, + // uint256 amountIn + // ) external onlyGovernor { + // uint256 balance; + // uint256 amountOutMin; + // uint256 deadline = block.timestamp; // No delays in timestamp + // address weth = IUniswapV2Router(router).WETH(); + // + // // Reconstruct path based on token address. + // // Avoid user's input error. + // address[2] memory path = [token, weth]; + // if (token >= weth) { + // path[0] = weth; + // path[1] = token; + // } + // + // for (uint256 i; i < path.length;) { + // if (path[i] != WETH) { + // balance = IERC20(path[i]).balanceOf(address(this)); + // // amountOutMin = ... + // // put big sqrt formula here + // } + // unchecked { ++i; } + // } + // + // // Approve amountIn for swap only. + // if (IERC20(token).allowance(router) <= amountIn) { + // IERC20(token).approve(router, 1e64); + // } + // + // (amountIn, amountOut) = IUniswapV2Router(router).swapExactTokensForTokens( + // amountIn, + // amountOutMin, + // path, + // address(this), + // deadline + // ); + // + // if (IERC20(token).allowance(router) <= desiredA) { + // IERC20(token).approve(router, 1e64); + // } + // + // if (IERC20(weth).allowance(router) <= desiredB) { + // IERC20(token).approve(router, 1e64); + // } + // + // IUniswapV2Router(router).addLiquidity( + // path[0], + // path[1], + // desiredA, // could be calculated with sqrt formula + // desiredB, // same as above + // minA, // zeroed or calculated or precomputed? + // minB, // zeroed or calculated or precomputed? + // address(this), + // deadline + // ); + // } + function indexInRegistry( address someAddress, STATUS status @@ -266,7 +326,7 @@ contract GhostTreasury is GhostAccessControlled, ITreasury { } else { permissions[info.managing][info.toPermit] = true; - if (info.managing == STATUS.LIQUIDITYTOKEN) { + if (info.managing == STATUS.LIQUIDITYTOKEN || info.managing == STATUS.RESERVETOKEN) { bondCalculator[info.toPermit] = info.calculator; } @@ -301,10 +361,13 @@ contract GhostTreasury is GhostAccessControlled, ITreasury { address token, uint256 amount ) public view override returns (uint256 value) { - value = amount * 1e9 / (10**IERC20Metadata(token).decimals()); + address currentBondCalculator = bondCalculator[token]; if (permissions[STATUS.LIQUIDITYTOKEN][token]) { value = IBondingCalculator(bondCalculator[token]).valuation(token, amount); + } else { + value = amount * 1e9 / (10**IERC20Metadata(token).decimals()); + value = value * IBondingCalculator(currentBondCalculator).fraction() / 1e18; } } diff --git a/src/interfaces/IBondingCalculator.sol b/src/interfaces/IBondingCalculator.sol index a083e69..4e769fb 100644 --- a/src/interfaces/IBondingCalculator.sol +++ b/src/interfaces/IBondingCalculator.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.20; interface IBondingCalculator { error InvalidPair(); + function fraction() external view returns (uint256); function markdown(address _lp) external view returns (uint256); function valuation(address pair_, uint256 amount_) external view returns (uint256 _value); } diff --git a/test/treasury/Treasury.t.sol b/test/treasury/Treasury.t.sol index 79ecf3e..91cb346 100644 --- a/test/treasury/Treasury.t.sol +++ b/test/treasury/Treasury.t.sol @@ -37,7 +37,7 @@ contract GhostTreasuryTest is Test { liquidity = new ERC20Mock("Liquidity Token", "LDT"); ftso = new Fatso(address(authority), "Fatso", "FTSO"); treasury = new GhostTreasury(address(ftso), 69, address(authority)); - calculator = new GhostBondingCalculator(address(ftso)); + calculator = new GhostBondingCalculator(address(ftso), 1, 1); vm.stopPrank(); vm.prank(governor);