LMSRStabilized pure refactor; swapMintAmounts
This commit is contained in:
@@ -125,6 +125,117 @@ contract PartyPoolSwapMintImpl is PartyPoolBase {
|
||||
return actualLpToMint;
|
||||
}
|
||||
|
||||
/// @notice Calculate the amounts for a swap mint operation
|
||||
/// @dev This is a pure view function that computes swap mint amounts from provided state
|
||||
/// @param inputTokenIndex index of the input token
|
||||
/// @param maxAmountIn maximum amount of token to deposit (inclusive of fee)
|
||||
/// @param swapFeePpm fee in parts-per-million
|
||||
/// @param lmsrState current LMSR state
|
||||
/// @param bases_ scaling bases for each token
|
||||
/// @param totalSupply_ current total LP token supply
|
||||
/// @return amountInUsed actual input amount used (excluding fee)
|
||||
/// @return fee fee amount charged
|
||||
/// @return lpMinted LP tokens that would be minted
|
||||
function swapMintAmounts(
|
||||
uint256 inputTokenIndex,
|
||||
uint256 maxAmountIn,
|
||||
uint256 swapFeePpm,
|
||||
LMSRStabilized.State memory lmsrState,
|
||||
uint256[] memory bases_,
|
||||
uint256 totalSupply_
|
||||
) public pure returns (uint256 amountInUsed, uint256 fee, uint256 lpMinted) {
|
||||
require(inputTokenIndex < bases_.length, "swapMintAmounts: idx");
|
||||
require(maxAmountIn > 0, "swapMintAmounts: input zero");
|
||||
require(lmsrState.nAssets > 0, "swapMintAmounts: uninit pool");
|
||||
|
||||
// Compute fee on gross maxAmountIn to get an initial net estimate
|
||||
uint256 feeGuess = 0;
|
||||
uint256 netUintGuess = maxAmountIn;
|
||||
if (swapFeePpm > 0) {
|
||||
feeGuess = (maxAmountIn * swapFeePpm + 999999) / 1000000; // ceil fee
|
||||
netUintGuess = maxAmountIn - feeGuess;
|
||||
}
|
||||
|
||||
// Convert the net guess to internal (floor)
|
||||
int128 netInternalGuess = _uintToInternalFloorPure(netUintGuess, bases_[inputTokenIndex]);
|
||||
require(netInternalGuess > int128(0), "swapMintAmounts: input too small after fee");
|
||||
|
||||
// Use LMSR view to determine actual internal consumed and size-increase (ΔS) for mint
|
||||
(int128 amountInInternalUsed, int128 sizeIncreaseInternal) =
|
||||
LMSRStabilized.swapAmountsForMint(lmsrState.nAssets, lmsrState.kappa, lmsrState.qInternal,
|
||||
inputTokenIndex, netInternalGuess);
|
||||
|
||||
// amountInInternalUsed may be <= netInternalGuess. Convert to uint (ceil) to determine actual transfer
|
||||
amountInUsed = _internalToUintCeilPure(amountInInternalUsed, bases_[inputTokenIndex]);
|
||||
require(amountInUsed > 0, "swapMintAmounts: input zero after internal conversion");
|
||||
|
||||
// Compute fee on the actual used input (ceiling)
|
||||
fee = 0;
|
||||
if (swapFeePpm > 0) {
|
||||
fee = (amountInUsed * swapFeePpm + 999999) / 1000000; // ceil fee
|
||||
}
|
||||
uint256 totalTransfer = amountInUsed + fee;
|
||||
require(totalTransfer > 0 && totalTransfer <= maxAmountIn, "swapMintAmounts: transfer exceeds max");
|
||||
|
||||
// Compute old and new scaled size metrics to determine LP minted
|
||||
int128 oldTotal = _computeSizeMetricPure(lmsrState.qInternal);
|
||||
require(oldTotal > int128(0), "swapMintAmounts: zero total");
|
||||
uint256 oldScaled = ABDKMath64x64.mulu(oldTotal, LP_SCALE);
|
||||
|
||||
int128 newTotal = oldTotal.add(sizeIncreaseInternal);
|
||||
uint256 newScaled = ABDKMath64x64.mulu(newTotal, LP_SCALE);
|
||||
|
||||
if (totalSupply_ == 0) {
|
||||
// If somehow supply zero (shouldn't happen as lmsr.nAssets>0), mint newScaled
|
||||
lpMinted = newScaled;
|
||||
} else {
|
||||
require(oldScaled > 0, "swapMintAmounts: oldScaled zero");
|
||||
uint256 delta = (newScaled > oldScaled) ? (newScaled - oldScaled) : 0;
|
||||
if (delta > 0) {
|
||||
// floor truncation rounds in favor of pool
|
||||
lpMinted = (totalSupply_ * delta) / oldScaled;
|
||||
} else {
|
||||
lpMinted = 0;
|
||||
}
|
||||
}
|
||||
|
||||
require(lpMinted > 0, "swapMintAmounts: zero LP minted");
|
||||
}
|
||||
|
||||
/// @notice Calculate the amounts for a burn swap operation
|
||||
/// @dev This is a pure view function that computes burn swap amounts from provided state
|
||||
/// @param lpAmount amount of LP tokens to burn
|
||||
/// @param inputTokenIndex index of target asset to receive
|
||||
/// @param swapFeePpm fee in parts-per-million
|
||||
/// @param lmsrState current LMSR state
|
||||
/// @param bases_ scaling bases for each token
|
||||
/// @param totalSupply_ current total LP token supply
|
||||
/// @return amountOut amount of target asset that would be received
|
||||
function burnSwapAmounts(
|
||||
uint256 lpAmount,
|
||||
uint256 inputTokenIndex,
|
||||
uint256 swapFeePpm,
|
||||
LMSRStabilized.State memory lmsrState,
|
||||
uint256[] memory bases_,
|
||||
uint256 totalSupply_
|
||||
) public pure returns (uint256 amountOut) {
|
||||
require(inputTokenIndex < bases_.length, "burnSwapAmounts: idx");
|
||||
require(lpAmount > 0, "burnSwapAmounts: zero lp");
|
||||
require(totalSupply_ > 0, "burnSwapAmounts: empty supply");
|
||||
|
||||
// alpha = lpAmount / supply as Q64.64
|
||||
int128 alpha = ABDKMath64x64.divu(lpAmount, totalSupply_) // fraction of total supply to burn
|
||||
.mul(ABDKMath64x64.divu(1000000-swapFeePpm, 1000000)); // adjusted for fee
|
||||
|
||||
// Use LMSR view to compute single-asset payout and burned size-metric
|
||||
(int128 payoutInternal, ) = LMSRStabilized.swapAmountsForBurn(lmsrState.nAssets, lmsrState.kappa, lmsrState.qInternal,
|
||||
inputTokenIndex, alpha);
|
||||
|
||||
// Convert payoutInternal -> uint (floor) to favor pool
|
||||
amountOut = _internalToUintFloorPure(payoutInternal, bases_[inputTokenIndex]);
|
||||
require(amountOut > 0, "burnSwapAmounts: output zero");
|
||||
}
|
||||
|
||||
/// @notice Burn LP tokens then swap the redeemed proportional basket into a single asset `inputTokenIndex` and send to receiver.
|
||||
/// @dev The function burns LP tokens (authorization via allowance if needed), sends the single-asset payout and updates LMSR state.
|
||||
/// @param payer who burns LP tokens
|
||||
@@ -198,4 +309,40 @@ contract PartyPoolSwapMintImpl is PartyPoolBase {
|
||||
emit Burn(payer, receiver, new uint256[](n), lpAmount);
|
||||
return amountOutUint;
|
||||
}
|
||||
|
||||
/// @notice Pure version of _uintToInternalFloor for use in view functions
|
||||
function _uintToInternalFloorPure(uint256 amount, uint256 base) internal pure returns (int128) {
|
||||
// amount / base as Q64.64, floored
|
||||
return ABDKMath64x64.divu(amount, base);
|
||||
}
|
||||
|
||||
/// @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
|
||||
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
|
||||
}
|
||||
return floored;
|
||||
}
|
||||
|
||||
/// @notice Pure version of _internalToUintFloor for use in view functions
|
||||
function _internalToUintFloorPure(int128 amount, uint256 base) internal pure returns (uint256) {
|
||||
// Convert Q64.64 to uint with floor: floor(amount * base)
|
||||
return ABDKMath64x64.mulu(amount, base);
|
||||
}
|
||||
|
||||
/// @notice Pure version of _computeSizeMetric for use in view functions
|
||||
function _computeSizeMetricPure(int128[] memory qInternal) internal pure returns (int128) {
|
||||
int128 sum = int128(0);
|
||||
for (uint256 i = 0; i < qInternal.length; i++) {
|
||||
sum = sum.add(qInternal[i]);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user