poolPrice() bugfix; burn() and mint() precision bugfixes
This commit is contained in:
@@ -776,19 +776,19 @@ library LMSRStabilized {
|
||||
return _exp(qInternal[baseTokenIndex].sub(qInternal[quoteTokenIndex]).mul(invB));
|
||||
}
|
||||
|
||||
/// @notice Price of one unit of the LP size-metric (S = sum q_i) denominated in `quote` asset (Q64.64)
|
||||
/// @dev Computes: poolPrice_quote = (1 / S) * sum_j q_j * exp((q_j - q_quote) / b)
|
||||
function poolPrice(State storage s, uint256 quoteTokenIndex) internal view returns (int128) {
|
||||
return poolPrice(s.kappa, s.qInternal, quoteTokenIndex);
|
||||
/// @notice Total pool value denominated in `quote` asset (Q64.64, internal quote units)
|
||||
/// @dev Computes: poolValue_quote = sum_j q_j * exp((q_j - q_quote) / b)
|
||||
function poolValue(State storage s, uint256 quoteTokenIndex) internal view returns (int128) {
|
||||
return poolValue(s.kappa, s.qInternal, quoteTokenIndex);
|
||||
}
|
||||
|
||||
/// @notice Pure version: Price of one unit of the LP size-metric (S = sum q_i) denominated in `quote` asset (Q64.64)
|
||||
/// @dev Computes: poolPrice_quote = (1 / S) * sum_j q_j * exp((q_j - q_quote) / b)
|
||||
/// @notice Pure version: Total pool value denominated in `quote` asset (Q64.64, internal quote units)
|
||||
/// @dev Computes: poolValue_quote = sum_j q_j * exp((q_j - q_quote) / b)
|
||||
/// @param kappa Liquidity parameter κ (64.64 fixed point)
|
||||
/// @param qInternal Cached internal balances in 64.64 fixed-point format
|
||||
/// @param quoteTokenIndex Index of quote token
|
||||
/// @return Pool price in 64.64 fixed-point format
|
||||
function poolPrice(int128 kappa, int128[] memory qInternal, uint256 quoteTokenIndex) internal pure returns (int128) {
|
||||
/// @return Total pool value in 64.64 fixed-point format (internal quote units)
|
||||
function poolValue(int128 kappa, int128[] memory qInternal, uint256 quoteTokenIndex) internal pure returns (int128) {
|
||||
// Compute b and ensure positivity
|
||||
int128 sizeMetric = _computeSizeMetric(qInternal);
|
||||
require(sizeMetric > int128(0), "LMSR: size metric zero");
|
||||
@@ -814,8 +814,7 @@ library LMSRStabilized {
|
||||
unchecked { j++; }
|
||||
}
|
||||
|
||||
// pool price in units of quote = (1 / S) * acc
|
||||
return acc.div(S);
|
||||
return acc;
|
||||
}
|
||||
|
||||
/* --------------------
|
||||
|
||||
@@ -67,8 +67,12 @@ contract PartyInfo is PartyPoolHelpers, IPartyInfo {
|
||||
require(nAssets > 0, "poolPrice: uninit");
|
||||
require(quoteTokenIndex < nAssets, "poolPrice: idx");
|
||||
|
||||
// price per unit of qTotal (Q64.64) from LMSR
|
||||
return LMSRStabilized.poolPrice( pool.kappa(), lmsr.qInternal, quoteTokenIndex);
|
||||
// LMSR total value of pool in terms of quote token
|
||||
int128 value = LMSRStabilized.poolValue(pool.kappa(), lmsr.qInternal, quoteTokenIndex);
|
||||
|
||||
uint256 qd = pool.denominators()[quoteTokenIndex];
|
||||
uint256 supply = pool.totalSupply();
|
||||
return value.mul(ABDKMath64x64.divu(qd * 10**18, supply));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -224,13 +224,13 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
||||
return depositAmounts; // Return zeros, initial deposit handled differently
|
||||
}
|
||||
|
||||
// lpTokenAmount / totalLpSupply = depositAmount / currentBalance
|
||||
// Therefore: depositAmount = (lpTokenAmount * currentBalance) / totalLpSupply
|
||||
// We round up to protect the pool
|
||||
// Compute mint ratio in Q64.64: ratio = lpTokenAmount / totalSupply
|
||||
int128 ratio = ABDKMath64x64.divu(lpTokenAmount, totalSupply);
|
||||
|
||||
// depositAmount_i = ceil(ratio * currentBalance_i)
|
||||
for (uint i = 0; i < numAssets; i++) {
|
||||
uint256 currentBalance = cachedUintBalances[i];
|
||||
// Calculate with rounding up: (a * b + c - 1) / c
|
||||
depositAmounts[i] = (lpTokenAmount * currentBalance + totalSupply - 1) / totalSupply;
|
||||
depositAmounts[i] = _internalToUintCeilPure(ratio, currentBalance);
|
||||
}
|
||||
|
||||
return depositAmounts;
|
||||
@@ -247,10 +247,10 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
||||
return withdrawAmounts; // Return zeros, nothing to withdraw
|
||||
}
|
||||
|
||||
// withdrawAmount = floor(lpTokenAmount * currentBalance / totalLpSupply)
|
||||
int128 ratio = ABDKMath64x64.divu(lpTokenAmount, totalSupply);
|
||||
for (uint i = 0; i < numAssets; i++) {
|
||||
uint256 currentBalance = cachedUintBalances[i];
|
||||
withdrawAmounts[i] = (lpTokenAmount * currentBalance) / totalSupply;
|
||||
withdrawAmounts[i] = ratio.mulu(currentBalance);
|
||||
}
|
||||
|
||||
return withdrawAmounts;
|
||||
@@ -536,7 +536,6 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
||||
IERC20 outputToken = _tokens[outputTokenIndex];
|
||||
_sendTokenTo(outputToken, receiver, amountOut, unwrap);
|
||||
|
||||
|
||||
// Update cached balances using computed payout and protocol fee; no on-chain reads
|
||||
|
||||
int128[] memory newQInternal = new int128[](n);
|
||||
@@ -574,15 +573,24 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
||||
/// @notice Pure version of _internalToUintCeil for use in view functions
|
||||
function _internalToUintCeilPure(int128 amount, uint256 base) internal pure returns (uint256) {
|
||||
// Convert Q64.64 to uint with ceiling: ceil(amount * base)
|
||||
// Use mulu which floors, then add remainder check for ceiling
|
||||
// Fast path: compute floor using mulu, then detect fractional remainder via low 64-bit check
|
||||
uint256 floored = ABDKMath64x64.mulu(amount, base);
|
||||
// Check if there's a fractional part by computing amount * base - floored
|
||||
int128 baseQ64 = ABDKMath64x64.fromUInt(base);
|
||||
int128 flooredQ64 = ABDKMath64x64.fromUInt(floored);
|
||||
int128 product = amount.mul(baseQ64);
|
||||
if (product > flooredQ64) {
|
||||
return floored + 1; // Ceiling
|
||||
|
||||
// Extract fractional 64 bits of `amount`; if zero, product is already an integer after scaling
|
||||
uint64 frac = uint64(uint128(amount));
|
||||
if (frac == 0) {
|
||||
return floored;
|
||||
}
|
||||
|
||||
unchecked {
|
||||
// Remainder exists iff (frac * (base mod 2^64)) mod 2^64 != 0
|
||||
uint64 baseL = uint64(base);
|
||||
uint128 low = uint128(frac) * uint128(baseL);
|
||||
if (uint64(low) != 0) {
|
||||
return floored + 1; // Ceiling
|
||||
}
|
||||
}
|
||||
|
||||
return floored;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user