additive fees; burnSwapAmounts fix
This commit is contained in:
@@ -269,7 +269,7 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
||||
/// @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 amountIn actual input amount used (excluding fee)
|
||||
/// @return lpMinted LP tokens that would be minted
|
||||
/// @return inFee fee amount charged
|
||||
function swapMintAmounts(
|
||||
@@ -279,7 +279,7 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
||||
LMSRStabilized.State memory lmsrState,
|
||||
uint256[] memory bases_,
|
||||
uint256 totalSupply_
|
||||
) public pure returns (uint256 amountInUsed, uint256 lpMinted, uint256 inFee) {
|
||||
) public pure returns (uint256 amountIn, uint256 lpMinted, uint256 inFee) {
|
||||
require(inputTokenIndex < bases_.length, "swapMintAmounts: idx");
|
||||
require(maxAmountIn > 0, "swapMintAmounts: input zero");
|
||||
require(lmsrState.qInternal.length > 0, "swapMintAmounts: uninit pool");
|
||||
@@ -302,7 +302,7 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
||||
inputTokenIndex, netInternalGuess);
|
||||
|
||||
// amountInInternalUsed may be <= netInternalGuess. Convert to uint (ceil) to determine actual transfer
|
||||
amountInUsed = _internalToUintCeilPure(amountInInternalUsed, bases_[inputTokenIndex]);
|
||||
uint256 amountInUsed = _internalToUintCeilPure(amountInInternalUsed, bases_[inputTokenIndex]);
|
||||
require(amountInUsed > 0, "swapMintAmounts: input zero after internal conversion");
|
||||
|
||||
// Compute fee on the actual used input (ceiling)
|
||||
@@ -310,8 +310,8 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
||||
if (swapFeePpm > 0) {
|
||||
inFee = (amountInUsed * swapFeePpm + 999999) / 1000000; // ceil fee
|
||||
}
|
||||
uint256 totalTransfer = amountInUsed + inFee;
|
||||
require(totalTransfer > 0 && totalTransfer <= maxAmountIn, "swapMintAmounts: transfer exceeds max");
|
||||
amountIn = amountInUsed + inFee;
|
||||
require(amountIn > 0 && amountIn <= maxAmountIn, "swapMintAmounts: transfer exceeds max");
|
||||
|
||||
// Compute old and new scaled size metrics to determine LP minted
|
||||
int128 oldTotal = _computeSizeMetricPure(lmsrState.qInternal);
|
||||
@@ -347,7 +347,7 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
||||
/// @param maxAmountIn maximum uint token input (inclusive of fee)
|
||||
/// @param deadline optional deadline
|
||||
/// @param swapFeePpm fee in parts-per-million for this pool
|
||||
/// @return amountInUsed actual input used (uint256), lpMinted actual LP minted (uint256), inFee fee taken from the input (uint256)
|
||||
/// @return amountIn actual input used (uint256), lpMinted actual LP minted (uint256), inFee fee taken from the input (uint256)
|
||||
function swapMint(
|
||||
address payer,
|
||||
address receiver,
|
||||
@@ -356,7 +356,7 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
||||
uint256 deadline,
|
||||
uint256 swapFeePpm,
|
||||
uint256 protocolFeePpm
|
||||
) external payable native killable nonReentrant returns (uint256 amountInUsed, uint256 lpMinted, uint256 inFee) {
|
||||
) external payable native killable nonReentrant returns (uint256 amountIn, uint256 lpMinted, uint256 inFee) {
|
||||
uint256 n = _tokens.length;
|
||||
require(inputTokenIndex < n, "swapMint: idx");
|
||||
require(maxAmountIn > 0, "swapMint: input zero");
|
||||
@@ -374,27 +374,27 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
||||
(int128 amountInInternalUsed, int128 sizeIncreaseInternal) = _lmsr.swapAmountsForMint(inputTokenIndex, netInternalGuess);
|
||||
|
||||
// amountInInternalUsed may be <= netInternalGuess. Convert to uint (ceil) to determine actual transfer
|
||||
uint256 amountInUint = _internalToUintCeil(amountInInternalUsed, _bases[inputTokenIndex]);
|
||||
require(amountInUint > 0, "swapMint: input zero after internal conversion");
|
||||
uint256 amountInUsed = _internalToUintCeil(amountInInternalUsed, _bases[inputTokenIndex]);
|
||||
require(amountInUsed > 0, "swapMint: input zero after internal conversion");
|
||||
|
||||
// Compute fee on the actual used input and total transfer amount (ceiling)
|
||||
uint256 feeUintActual = _ceilFee(amountInUint, swapFeePpm);
|
||||
uint256 totalTransfer = amountInUint + feeUintActual;
|
||||
require(totalTransfer > 0 && totalTransfer <= maxAmountIn, "swapMint: transfer exceeds max");
|
||||
inFee = _ceilFee(amountInUsed, swapFeePpm);
|
||||
amountIn = amountInUsed + inFee;
|
||||
require(amountIn > 0 && amountIn <= maxAmountIn, "swapMint: transfer exceeds max");
|
||||
|
||||
// Transfer _tokens from payer (assume standard ERC20 without transfer fees) via helper
|
||||
_receiveTokenFrom(payer, _tokens[inputTokenIndex], totalTransfer);
|
||||
_receiveTokenFrom(payer, _tokens[inputTokenIndex], amountIn);
|
||||
|
||||
// Accrue protocol share (floor) from the fee on the input token
|
||||
uint256 protoShare = 0;
|
||||
if (protocolFeePpm > 0 && feeUintActual > 0) {
|
||||
protoShare = (feeUintActual * protocolFeePpm) / 1_000_000;
|
||||
if (protocolFeePpm > 0 && inFee > 0) {
|
||||
protoShare = (inFee * protocolFeePpm) / 1_000_000;
|
||||
if (protoShare > 0) {
|
||||
_protocolFeesOwed[inputTokenIndex] += protoShare;
|
||||
}
|
||||
}
|
||||
// Update cached effective balance directly: add totalTransfer minus protocol share
|
||||
_cachedUintBalances[inputTokenIndex] += (totalTransfer - protoShare);
|
||||
_cachedUintBalances[inputTokenIndex] += (amountIn - protoShare);
|
||||
|
||||
// Compute old and new scaled size metrics to determine LP minted
|
||||
int128 oldTotal = _computeSizeMetric(_lmsr.qInternal);
|
||||
@@ -403,23 +403,22 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
||||
int128 newTotal = oldTotal.add(sizeIncreaseInternal);
|
||||
uint256 newScaled = ABDKMath64x64.mulu(newTotal, LP_SCALE);
|
||||
|
||||
uint256 actualLpToMint;
|
||||
// Use natural ERC20 function since base contract inherits from ERC20
|
||||
uint256 currentSupply = _totalSupply;
|
||||
if (currentSupply == 0) {
|
||||
// If somehow supply zero (shouldn't happen as _lmsr.nAssets>0), mint newScaled
|
||||
actualLpToMint = newScaled;
|
||||
lpMinted = newScaled;
|
||||
} else {
|
||||
uint256 delta = (newScaled > oldScaled) ? (newScaled - oldScaled) : 0;
|
||||
if (delta > 0) {
|
||||
// floor truncation rounds in favor of pool
|
||||
actualLpToMint = (currentSupply * delta) / oldScaled;
|
||||
lpMinted = (currentSupply * delta) / oldScaled;
|
||||
} else {
|
||||
actualLpToMint = 0;
|
||||
lpMinted = 0;
|
||||
}
|
||||
}
|
||||
|
||||
require(actualLpToMint > 0, "swapMint: zero LP minted");
|
||||
require(lpMinted > 0, "swapMint: zero LP minted");
|
||||
|
||||
// Update LMSR internal state: scale qInternal proportionally by newTotal/oldTotal
|
||||
int128[] memory newQInternal = new int128[](n);
|
||||
@@ -432,14 +431,11 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
||||
_lmsr.updateForProportionalChange(newQInternal);
|
||||
|
||||
// Use natural ERC20 function since base contract inherits from ERC20
|
||||
_mint(receiver, actualLpToMint);
|
||||
_mint(receiver, lpMinted);
|
||||
|
||||
emit IPartyPool.SwapMint(payer, receiver, _tokens[inputTokenIndex],
|
||||
totalTransfer, actualLpToMint, feeUintActual-protoShare, protoShare);
|
||||
amountIn, lpMinted, inFee -protoShare, protoShare);
|
||||
|
||||
amountInUsed = amountInUint;
|
||||
lpMinted = actualLpToMint;
|
||||
inFee = feeUintActual;
|
||||
return (amountInUsed, lpMinted, inFee);
|
||||
}
|
||||
|
||||
@@ -465,23 +461,17 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
||||
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
|
||||
int128 alpha = ABDKMath64x64.divu(lpAmount, totalSupply_);
|
||||
|
||||
// Use LMSR view to compute single-asset payout and burned size-metric
|
||||
(int128 payoutInternal, ) = LMSRStabilized.swapAmountsForBurn(lmsrState.kappa, lmsrState.qInternal,
|
||||
(, int128 payoutInternal) = LMSRStabilized.swapAmountsForBurn(lmsrState.kappa, lmsrState.qInternal,
|
||||
outputTokenIndex, alpha);
|
||||
|
||||
// Convert payoutInternal -> uint (floor) to favor pool
|
||||
amountOut = _internalToUintFloorPure(payoutInternal, bases_[outputTokenIndex]);
|
||||
require(amountOut > 0, "burnSwapAmounts: output zero");
|
||||
|
||||
// Compute gross payout (no swap fee) to derive token-side fee = gross - net
|
||||
int128 alphaGross = ABDKMath64x64.divu(lpAmount, totalSupply_); // gross fraction (no swap fee)
|
||||
(int128 payoutGrossInternal, ) = LMSRStabilized.swapAmountsForBurn(lmsrState.kappa, lmsrState.qInternal,
|
||||
outputTokenIndex, alphaGross);
|
||||
uint256 payoutGrossUint = _internalToUintFloorPure(payoutGrossInternal, bases_[outputTokenIndex]);
|
||||
outFee = (payoutGrossUint > amountOut) ? (payoutGrossUint - amountOut) : 0;
|
||||
uint256 grossAmountOut = _internalToUintFloorPure(payoutInternal, bases_[outputTokenIndex]);
|
||||
(outFee,) = _computeFee(grossAmountOut, swapFeePpm);
|
||||
require(grossAmountOut > outFee, "burnSwapAmounts: output zero");
|
||||
amountOut = grossAmountOut - outFee;
|
||||
}
|
||||
|
||||
/// @notice Burn LP _tokens then swap the redeemed proportional basket into a single asset `outputTokenIndex` and send to receiver.
|
||||
@@ -494,7 +484,8 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
||||
/// @param outputTokenIndex index of target asset to receive
|
||||
/// @param deadline optional deadline
|
||||
/// @param swapFeePpm fee in parts-per-million for this pool (may be used for future fee logic)
|
||||
/// @return amountOutUint uint amount of asset i sent to receiver
|
||||
/// @return amountOut uint amount of asset i sent to receiver
|
||||
/// @return outFee uint amount of asset i kept as an LP and protocol fee
|
||||
function burnSwap(
|
||||
address payer,
|
||||
address receiver,
|
||||
@@ -504,7 +495,7 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
||||
bool unwrap,
|
||||
uint256 swapFeePpm,
|
||||
uint256 protocolFeePpm
|
||||
) external nonReentrant killable returns (uint256 amountOutUint) {
|
||||
) external nonReentrant killable returns (uint256 amountOut, uint256 outFee) {
|
||||
uint256 n = _tokens.length;
|
||||
require(outputTokenIndex < n, "burnSwap: idx");
|
||||
require(lpAmount > 0, "burnSwap: zero lp");
|
||||
@@ -514,35 +505,26 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
||||
require(supply > 0, "burnSwap: empty supply");
|
||||
|
||||
// alpha = lpAmount / supply as Q64.64 (adjusted for fee)
|
||||
int128 alpha = ABDKMath64x64.divu(lpAmount, supply) // fraction of total supply to burn
|
||||
.mul(ABDKMath64x64.divu(1000000-swapFeePpm, 1000000)); // adjusted for fee
|
||||
int128 alpha = ABDKMath64x64.divu(lpAmount, supply); // fraction of total supply to burn
|
||||
|
||||
// Use LMSR view to compute single-asset payout and burned size-metric
|
||||
(int128 payoutInternal, ) = _lmsr.swapAmountsForBurn(outputTokenIndex, alpha);
|
||||
(, int128 payoutInternal) = _lmsr.swapAmountsForBurn(outputTokenIndex, alpha);
|
||||
|
||||
// Convert payoutInternal -> uint (floor) to favor pool
|
||||
amountOutUint = _internalToUintFloor(payoutInternal, _bases[outputTokenIndex]);
|
||||
require(amountOutUint > 0, "burnSwap: output zero");
|
||||
|
||||
// Compute gross payout (no swap fee) so we can determine token-side fee = gross - net
|
||||
int128 alphaGross = ABDKMath64x64.divu(lpAmount, supply); // gross fraction (no swap fee)
|
||||
(int128 payoutGrossInternal, ) = _lmsr.swapAmountsForBurn(outputTokenIndex, alphaGross);
|
||||
uint256 payoutGrossUint = _internalToUintFloor(payoutGrossInternal, _bases[outputTokenIndex]);
|
||||
uint256 feeTokenUint = (payoutGrossUint > amountOutUint) ? (payoutGrossUint - amountOutUint) : 0;
|
||||
uint256 payoutGrossUint = _internalToUintFloorPure(payoutInternal, _bases[outputTokenIndex]);
|
||||
(outFee,) = _computeFee(payoutGrossUint, swapFeePpm);
|
||||
require(payoutGrossUint > outFee, "burnSwapAmounts: output zero");
|
||||
amountOut = payoutGrossUint - outFee;
|
||||
|
||||
// Accrue protocol share (floor) from the token-side fee
|
||||
uint256 protoShare = 0;
|
||||
if (protocolFeePpm > 0 && feeTokenUint > 0) {
|
||||
protoShare = (feeTokenUint * protocolFeePpm) / 1_000_000;
|
||||
if (protocolFeePpm > 0 && outFee > 0) {
|
||||
protoShare = (outFee * protocolFeePpm) / 1_000_000;
|
||||
if (protoShare > 0) {
|
||||
_protocolFeesOwed[outputTokenIndex] += protoShare;
|
||||
}
|
||||
}
|
||||
|
||||
// Transfer the payout to receiver via centralized helper
|
||||
IERC20 outputToken = _tokens[outputTokenIndex];
|
||||
_sendTokenTo(outputToken, receiver, amountOutUint, unwrap);
|
||||
|
||||
// Burn LP _tokens from payer (authorization via allowance)
|
||||
if (msg.sender != payer) {
|
||||
uint256 allowed = _allowances[payer][msg.sender];
|
||||
@@ -550,13 +532,19 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
||||
}
|
||||
_burn(payer, lpAmount);
|
||||
|
||||
// Transfer the payout to receiver via centralized helper
|
||||
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);
|
||||
for (uint256 idx = 0; idx < n; idx++) {
|
||||
uint256 newBal = _cachedUintBalances[idx];
|
||||
if (idx == outputTokenIndex) {
|
||||
// Effective LP balance decreases by net payout and increased protocol owed
|
||||
newBal = newBal - amountOutUint - protoShare;
|
||||
newBal = newBal - amountOut - protoShare;
|
||||
}
|
||||
_cachedUintBalances[idx] = newBal;
|
||||
newQInternal[idx] = _uintToInternalFloor(newBal, _bases[idx]);
|
||||
@@ -573,10 +561,8 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
||||
_lmsr.updateForProportionalChange(newQInternal);
|
||||
}
|
||||
|
||||
emit IPartyPool.BurnSwap(payer, receiver, outputToken, lpAmount, amountOutUint,
|
||||
feeTokenUint-protoShare, protoShare);
|
||||
|
||||
return amountOutUint;
|
||||
emit IPartyPool.BurnSwap(payer, receiver, outputToken, lpAmount, amountOut,
|
||||
outFee-protoShare, protoShare);
|
||||
}
|
||||
|
||||
/// @notice Pure version of _uintToInternalFloor for use in view functions
|
||||
|
||||
Reference in New Issue
Block a user