From 64e73e55701489914d553bdf5c8d9b64b7186d89 Mon Sep 17 00:00:00 2001 From: tim Date: Tue, 25 Nov 2025 13:26:24 -0400 Subject: [PATCH] price() view bugfix --- deployment/liqp-deployments.json | 4 +- src/PartyInfo.sol | 2 +- test/Deploy.sol | 67 ++++++++++++++++++++++++++++++++ test/PartyPool.t.sol | 38 ++++++++++++++++++ 4 files changed, 108 insertions(+), 3 deletions(-) diff --git a/deployment/liqp-deployments.json b/deployment/liqp-deployments.json index 18cd857..a9fbece 100644 --- a/deployment/liqp-deployments.json +++ b/deployment/liqp-deployments.json @@ -2,7 +2,7 @@ "1": { "v1": { "PartyPlanner": "0x227B7A552d5E55a582dbB8431B116f59E3506bcF", - "PartyInfo": "0x7fb08EAE59e6260B6d62fC1bC199F6B8a9993246", + "PartyInfo": "0x4c02Faf88CC90bE56B478e4c693F0d5baE6353A9", "PartyPoolMintImpl": "0xe0C0ab90126D9e37b1Ba0D858e5714b6dabd84FF", "PartyPoolSwapImpl": "0xE5B51FCBfD6BFF29b56b0BAbC974353675d8B335", "PartyPoolInitCode": "0xC31774FDE0BB4718f6cE9A36f178717C71bE09Fa", @@ -12,7 +12,7 @@ "11155111": { "v1": { "PartyPlanner": "0x1071097336Fd33298975efeC9C0bbc0bfB669da6", - "PartyInfo": "0xF583966f4a5D5D18Ec21da86DAaBd5e40c4Fa761", + "PartyInfo": "0x72582cD75AE8CF2934dc2909565A5858d30Eb2F8", "PartyPoolMintImpl": "0xE40D300B7Dfff73b457BBB5A0d42dd4D88Ae9dD1", "PartyPoolSwapImpl": "0x1b60fa6a7625477EC04B5FaA857e5022Ab1DF6E9", "PartyPoolInitCode": "", diff --git a/src/PartyInfo.sol b/src/PartyInfo.sol index ec47650..7cd15d7 100644 --- a/src/PartyInfo.sol +++ b/src/PartyInfo.sol @@ -51,7 +51,7 @@ contract PartyInfo is PartyPoolHelpers, IPartyInfo { // Convert to external units uint256 bd = pool.denominators()[baseTokenIndex]; uint256 qd = pool.denominators()[quoteTokenIndex]; - return internalPrice.mul(ABDKMath64x64.divu(bd, qd)); + return internalPrice.mul(ABDKMath64x64.divu(qd, bd)); } /// @notice Price of one LP token denominated in `quote` as Q64.64. diff --git a/test/Deploy.sol b/test/Deploy.sol index 1516d72..74e70fc 100644 --- a/test/Deploy.sol +++ b/test/Deploy.sol @@ -87,6 +87,73 @@ library Deploy { return newPartyPool2(NPPArgs(name_, symbol_, tokens_, _kappa, _swapFeePpm, _flashFeePpm, wrapper, _stable, _initialBalance, _lpTokens)); } + /// @notice Deploy a pool using explicit per-token initial deposits (useful for non-uniform initial balances) + function newPartyPoolWithDeposits( + string memory name_, + string memory symbol_, + IERC20[] memory tokens_, + int128 _kappa, + uint256 _swapFeePpm, + uint256 _flashFeePpm, + bool _stable, + uint256[] memory initialDeposits, + uint256 _lpTokens + ) internal returns (IPartyPool pool, uint256 lpTokens) { + require(initialDeposits.length == tokens_.length, "mismatched deposits length"); + NativeWrapper wrapper = new WETH9(); + + // Prepare planner and arrays + NPPVars memory v = NPPVars( + address(newPartyPlanner(address(this), wrapper)), + new uint256[](tokens_.length), + new uint256[](tokens_.length) + ); + address self = address(this); + + // Build per-asset fee vector from scalar for tests + for (uint256 i = 0; i < tokens_.length; i++) { v.feesArr[i] = _swapFeePpm; } + + // Mint/prepare the specified deposits for each token and approve the planner + for (uint256 i = 0; i < tokens_.length; i++) { + v.deposits[i] = initialDeposits[i]; + if (address(tokens_[i]) == address(wrapper)) { + // Wrap native value and approve planner + if (initialDeposits[i] > 0) { + argsWrapperDeposit(wrapper, v.planner, initialDeposits[i]); + } + } else { + MockERC20 t = MockERC20(address(tokens_[i])); + if (initialDeposits[i] > 0) { + t.mint(self, initialDeposits[i]); + t.approve(v.planner, initialDeposits[i]); + } + } + } + + // Create pool with provided deposits + (pool, lpTokens) = IPartyPlanner(v.planner).newPool( + name_, + symbol_, + tokens_, + _kappa, + _swapFeePpm, + _flashFeePpm, + _stable, + self, + self, + v.deposits, + _lpTokens, + 0 + ); + } + + // Helper to deposit native wrapper value and approve planner (kept separate for clarity) + function argsWrapperDeposit(NativeWrapper wrapper, address planner, uint256 amount) internal { + if (amount == 0) return; + wrapper.deposit{value: amount}(); + wrapper.approve(planner, amount); + } + struct NPPVars { address planner; uint256[] feesArr; diff --git a/test/PartyPool.t.sol b/test/PartyPool.t.sol index 2de539b..5b26d26 100644 --- a/test/PartyPool.t.sol +++ b/test/PartyPool.t.sol @@ -1107,5 +1107,43 @@ contract PartyPoolTest is Test { assertEq(uint256(uint128(priceAfter)), uint256(uint128(expected)), "Pool price should remain 1.0000000 after mint"); } + /// @notice Create a 3-token pool where token0 has 3x the balance of token1 and verify + /// that the relative price token1/token0 equals 3 (in ABDK 64.64 fixed point). + function testPriceWhenToken0HasThreeTimesToken1() public { + // Build tokens array (reuse test tokens) + IERC20[] memory tokens = new IERC20[](3); + tokens[0] = IERC20(address(token0)); + tokens[1] = IERC20(address(token1)); + tokens[2] = IERC20(address(token2)); + + uint256 feePpm = 1000; + // Compute kappa using existing tradeFrac/targetSlippage setup + int128 kappa = LMSRStabilized.computeKappaFromSlippage(tokens.length, tradeFrac, targetSlippage); + + // Prepare explicit per-token initial deposits so token0 starts at 3x token1 + uint256[] memory deposits = new uint256[](3); + deposits[0] = INIT_BAL * 3; // token0 = 3 * INIT_BAL + deposits[1] = INIT_BAL; // token1 = INIT_BAL + deposits[2] = INIT_BAL; // token2 = INIT_BAL + + // Deploy a fresh pool with the specified deposits (no subsequent minting required) + (IPartyPool poolCustom, ) = Deploy.newPartyPoolWithDeposits("LP3X", "LP3X", tokens, kappa, feePpm, feePpm, false, deposits, 0); + + // Sanity-check balances + uint256 b0 = token0.balanceOf(address(poolCustom)); + uint256 b1 = token1.balanceOf(address(poolCustom)); + assertEq(b0, INIT_BAL * 3, "token0 balance should be 3x INIT_BAL"); + assertEq(b1, INIT_BAL, "token1 balance should be INIT_BAL"); + + // Query the price of token1 in terms of token0 (token1/token0) + int128 price = info.price(poolCustom, 1, 0); + + // Expected price is 3.0 in ABDK 64.64 fixed point + int128 expected = ABDKMath64x64.fromInt(3); + + // Compare as uint representation (values are non-negative) + assertEq(uint256(uint128(price)), uint256(uint128(expected)), "Price token1/token0 should be 3.0000000"); + } + } /* solhint-enable */