poolPrice() bugfix; burn() and mint() precision bugfixes

This commit is contained in:
tim
2025-12-01 15:42:12 -04:00
parent ea54059337
commit 4e56f54f27
6 changed files with 120 additions and 73 deletions

View File

@@ -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;
}