price() and poolPrice() fixes
This commit is contained in:
@@ -94,6 +94,7 @@ contract PartyPool is PartyPoolBase, OwnableExternal, ERC20External, IPartyPool
|
||||
/// @inheritdoc IPartyPool
|
||||
function denominators() external view returns (uint256[] memory) { return _bases; }
|
||||
|
||||
/// @inheritdoc IPartyPool
|
||||
function LMSR() external view returns (LMSRStabilized.State memory) { return _lmsr; }
|
||||
|
||||
|
||||
|
||||
@@ -139,13 +139,13 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
||||
return actualLpToMint;
|
||||
}
|
||||
|
||||
/// @notice Burn LP _tokens and withdraw the proportional basket to receiver. Functional even if the pool has been
|
||||
/// killed.
|
||||
/// @dev Payer must own or approve the LP _tokens being burned. The function updates LMSR state
|
||||
/// @notice Burn LP tokens and withdraw the proportional basket to receiver. Functional even if the pool
|
||||
/// has been killed.
|
||||
/// @dev Payer must own or approve the LP tokens being burned. The function updates LMSR state
|
||||
/// proportionally to reflect the reduced pool size after the withdrawal.
|
||||
/// @param payer address that provides the LP _tokens to burn
|
||||
/// @param receiver address that receives the withdrawn _tokens
|
||||
/// @param lpAmount amount of LP _tokens to burn (proportional withdrawal)
|
||||
/// @param payer address that provides the LP tokens to burn
|
||||
/// @param receiver address that receives the withdrawn tokens
|
||||
/// @param lpAmount amount of LP tokens to burn (proportional withdrawal)
|
||||
/// @param deadline timestamp after which the transaction will revert. Pass 0 to ignore.
|
||||
/// @param unwrap if true and the native token is being withdrawn, it is unwraped and sent as native currency
|
||||
function burn(address payer, address receiver, uint256 lpAmount, uint256 deadline, bool unwrap) external nonReentrant
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
// SPDX-License-Identifier: UNLICENSED
|
||||
pragma solidity ^0.8.30;
|
||||
|
||||
import "forge-std/console2.sol";
|
||||
import {ABDKMath64x64} from "../lib/abdk-libraries-solidity/ABDKMath64x64.sol";
|
||||
import {IERC20} from "../lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
|
||||
import {IPartyPool} from "./IPartyPool.sol";
|
||||
import {IPartyPoolViewer} from "./IPartyPoolViewer.sol";
|
||||
import {LMSRStabilized} from "./LMSRStabilized.sol";
|
||||
import {PartyPoolHelpers} from "./PartyPoolHelpers.sol";
|
||||
import {PartyPoolMintImpl} from "./PartyPoolMintImpl.sol";
|
||||
import {PartyPoolSwapImpl} from "./PartyPoolSwapImpl.sol";
|
||||
import {IPartyPoolViewer} from "./IPartyPoolViewer.sol";
|
||||
|
||||
contract PartyPoolViewer is PartyPoolHelpers, IPartyPoolViewer {
|
||||
using ABDKMath64x64 for int128;
|
||||
@@ -36,7 +37,11 @@ contract PartyPoolViewer is PartyPoolHelpers, IPartyPoolViewer {
|
||||
uint256 nAssets = lmsr.qInternal.length;
|
||||
require(nAssets > 0, "price: uninit");
|
||||
require(baseTokenIndex < nAssets && quoteTokenIndex < nAssets, "price: idx");
|
||||
return LMSRStabilized.price(pool.kappa(), lmsr.qInternal, baseTokenIndex, quoteTokenIndex);
|
||||
int128 internalPrice = LMSRStabilized.price(pool.kappa(), lmsr.qInternal, baseTokenIndex, quoteTokenIndex);
|
||||
// Convert to external units
|
||||
uint256 bd = pool.denominators()[baseTokenIndex];
|
||||
uint256 qd = pool.denominators()[quoteTokenIndex];
|
||||
return internalPrice.mul(ABDKMath64x64.divu(bd, qd));
|
||||
}
|
||||
|
||||
/// @notice Price of one LP token denominated in `quote` as Q64.64.
|
||||
@@ -52,22 +57,7 @@ contract PartyPoolViewer is PartyPoolHelpers, IPartyPoolViewer {
|
||||
require(quoteTokenIndex < nAssets, "poolPrice: idx");
|
||||
|
||||
// price per unit of qTotal (Q64.64) from LMSR
|
||||
int128 pricePerQ = LMSRStabilized.poolPrice( pool.kappa(), lmsr.qInternal, quoteTokenIndex);
|
||||
|
||||
// total internal q (qTotal) as Q64.64
|
||||
int128 qTotal = LMSRStabilized._computeSizeMetric(lmsr.qInternal);
|
||||
require(qTotal > int128(0), "poolPrice: qTotal zero");
|
||||
|
||||
// totalSupply as Q64.64
|
||||
uint256 supply = pool.totalSupply();
|
||||
require(supply > 0, "poolPrice: zero supply");
|
||||
int128 supplyQ64 = ABDKMath64x64.fromUInt(supply);
|
||||
|
||||
// factor = totalSupply / qTotal (Q64.64)
|
||||
int128 factor = supplyQ64.div(qTotal);
|
||||
|
||||
// price per LP token = pricePerQ * factor (Q64.64)
|
||||
return pricePerQ.mul(factor);
|
||||
return LMSRStabilized.poolPrice( pool.kappa(), lmsr.qInternal, quoteTokenIndex);
|
||||
}
|
||||
|
||||
|
||||
@@ -171,11 +161,9 @@ contract PartyPoolViewer is PartyPoolHelpers, IPartyPoolViewer {
|
||||
return IERC20(token).balanceOf(address(pool));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev The fee to be charged for a given loan.
|
||||
* @param amount The amount of _tokens lent.
|
||||
* @return fee The amount of `token` to be charged for the loan, on top of the returned principal.
|
||||
*/
|
||||
/// @dev The fee to be charged for a given loan.
|
||||
/// @param amount The amount of _tokens lent.
|
||||
/// @return fee The amount of `token` to be charged for the loan, on top of the returned principal.
|
||||
function flashFee(
|
||||
IPartyPool pool,
|
||||
address /*token*/,
|
||||
|
||||
@@ -176,7 +176,7 @@ contract PartyPoolTest is Test {
|
||||
token2.transfer(address(pool), INIT_BAL);
|
||||
|
||||
// Perform initial mint (initial deposit); receiver is this contract
|
||||
pool.initialMint(address(this), 0);
|
||||
pool.initialMint(address(this), INIT_BAL * tokens.length * 10**18);
|
||||
|
||||
// Set up pool10 with 10 _tokens
|
||||
IERC20[] memory tokens10 = new IERC20[](10);
|
||||
@@ -1124,5 +1124,52 @@ contract PartyPoolTest is Test {
|
||||
vm.stopPrank();
|
||||
}
|
||||
|
||||
/// @notice Verify that the initial relative price between token0 and token1 is 1.0000000
|
||||
function testInitialPriceIsOne() public view {
|
||||
// Query the viewer for the relative price between token index 0 and 1
|
||||
int128 price = viewer.price(pool, 0, 1);
|
||||
// Expected price is 1.0 in ABDK 64.64 fixed point
|
||||
int128 expected = ABDKMath64x64.fromInt(1);
|
||||
|
||||
// Cast int128 to uint128 then to uint256 for assertEq (values are non-negative)
|
||||
assertEq(uint256(uint128(price)), uint256(uint128(expected)), "Initial relative price must be 1.0000000");
|
||||
}
|
||||
|
||||
/// @notice Verify that the initial LP price in terms of token0 is 1.0000000
|
||||
function testInitialPoolPriceIsOne() public {
|
||||
// Query the viewer for the pool price for token0
|
||||
int128 price = viewer.poolPrice(pool, 0);
|
||||
// Expected price is 1.0 in ABDK 64.64 fixed point
|
||||
int128 expected = ABDKMath64x64.fromInt(1);
|
||||
|
||||
// Cast int128 to uint128 then to uint256 for assertEq (values are non-negative)
|
||||
assertEq(uint256(uint128(price)), uint256(uint128(expected)), "Initial pool price must be 1.0000000");
|
||||
|
||||
// Mint a small amount of LP into the pool from alice and verify price remains 1.0
|
||||
vm.startPrank(alice);
|
||||
// Approve tokens for pool to pull
|
||||
token0.approve(address(pool), type(uint256).max);
|
||||
token1.approve(address(pool), type(uint256).max);
|
||||
token2.approve(address(pool), type(uint256).max);
|
||||
|
||||
// Choose a small LP request (1% of supply or at least 1)
|
||||
uint256 lpRequest = pool.totalSupply() / 100;
|
||||
if (lpRequest == 0) lpRequest = 1;
|
||||
|
||||
// Compute required deposits and perform mint if not trivial
|
||||
uint256[] memory deposits = viewer.mintAmounts(pool, lpRequest);
|
||||
bool allZero = true;
|
||||
for (uint i = 0; i < deposits.length; i++) { if (deposits[i] != 0) { allZero = false; break; } }
|
||||
|
||||
if (!allZero) {
|
||||
pool.mint(alice, alice, lpRequest, 0);
|
||||
}
|
||||
vm.stopPrank();
|
||||
|
||||
// Re-query the pool price and ensure it remains 1.0 (within exact fixed-point equality)
|
||||
int128 priceAfter = viewer.poolPrice(pool, 0);
|
||||
assertEq(uint256(uint128(priceAfter)), uint256(uint128(expected)), "Pool price should remain 1.0000000 after mint");
|
||||
}
|
||||
|
||||
}
|
||||
/* solhint-enable */
|
||||
|
||||
Reference in New Issue
Block a user