From dc2e186331e3589a7480d399cbc8bca2daa1f4b1 Mon Sep 17 00:00:00 2001 From: tim Date: Tue, 4 Nov 2025 16:58:16 -0400 Subject: [PATCH] additive fees; burnSwapAmounts fix --- doc/launch_list.md | 24 +++++++++ research/fees.py | 15 +++--- src/IPartyPool.sol | 5 +- src/LMSRStabilized.sol | 19 +++---- src/PartyPool.sol | 13 ++--- src/PartyPoolBase.sol | 5 +- src/PartyPoolMintImpl.sol | 110 +++++++++++++++++--------------------- test/LMSRStabilized.t.sol | 4 +- test/NativeTest.t.sol | 6 +-- test/PartyPool.t.sol | 2 +- 10 files changed, 103 insertions(+), 100 deletions(-) create mode 100644 doc/launch_list.md diff --git a/doc/launch_list.md b/doc/launch_list.md new file mode 100644 index 0000000..94c4c33 --- /dev/null +++ b/doc/launch_list.md @@ -0,0 +1,24 @@ +| Symbol | Address | +|-----------------|----------------------------------------------| +| **Stablecoins** | | +| USDT | `0xdAC17F958D2ee523a2206206994597C13D831ec7` | +| USDC | `0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48` | +| USDe | `0x4c9EDD5852cd905f086C759E8383e09bff1E68B3` | +| DAI | `0x6B175474E89094C44Da98b954EedeAC495271d0F` | +| **Chain Coins** | | +| WETH | `0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2` | +| WBTC | `0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599` | +| WSOL | `0xd1D82d3Ab815E0B47e38EC2d666c5b8AA05Ae501` | +| BNB | `0xB8c77482e45F1F44dE1745F52C74426C631bDD52` | +| TRX | `0x50327c6c5a14DCaDE707ABad2E27eB517df87AB5` | +| POL | `0x455e53CBB86018Ac2B8092FdCd39d8444aFFC3F6` | +| DOT | `0x21c2c96Dbfa137E23946143c71AC8330F9B44001` | +| NEAR | `0x85F17Cf997934a597031b2E18a9aB6ebD4B9f6a4` | +| **Others** | | +| WBETH | `0xa2E3356610840701BDf5611a53974510Ae27E2e1` | +| ARB | `0xB50721BCf8d664c30412Cfbc6cf7a15145234ad1` | +| UNI | `0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984` | +| LINK | `0x514910771AF9Ca656af840dff83E8264EcF986CA` | +| AAVE | `0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9` | +| PEPE | `0x6982508145454Ce325dDbE47a25d4ec3d2311933` | +| SHIB | `0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE` | diff --git a/research/fees.py b/research/fees.py index 56d83da..fd85ed9 100644 --- a/research/fees.py +++ b/research/fees.py @@ -117,20 +117,19 @@ if __name__ == "__main__": if alerts: log.warning("Some fee configuration may produce large values (bps): %s", alerts) - print("\nPair fees [per-asset fee -> total]:") + # Per-asset diagnostic: show per-asset fee (interpreted vs USDC) + for n, w in weights.items(): + f_token = per_asset_fee(w) + print(f"{n:>12} {bps(f_token):>12}") + print() + for (name_a, w_a), (name_b, w_b) in combinations(weights.items(), 2): f_eff_ab, f_i_ab, f_j_ab = fee_for_pair(w_a, w_b) print( - f"{name_a}/{name_b}: per-asset_in={bps(f_i_ab)}, per-asset_out={bps(f_j_ab)}, total={bps(f_eff_ab)}" + f"{name_a+'-'+name_b:>12} {bps(f_eff_ab):>12}" ) - # Per-asset diagnostic: show per-asset fee (interpreted vs USDC) - print("\nPer-asset diagnostics (fees interpreted vs USDC):") - for n, w in weights.items(): - f_token = per_asset_fee(w) - print(f"{n}: target_vs_USDC={w:.6f} bps, per_asset_fee={bps(f_token)}") - # -- Solidity (ABDK Q64.64) output -- # Print derived per-asset fees and cap as Solidity int128 Q64.64 constants that can be pasted # into a Solidity file using ABDK's 64.64 fixed-point representation (int128 constants). diff --git a/src/IPartyPool.sol b/src/IPartyPool.sol index f3ec118..068dad2 100644 --- a/src/IPartyPool.sol +++ b/src/IPartyPool.sol @@ -240,7 +240,8 @@ interface IPartyPool is IERC20Metadata, IOwnable { /// @param lpAmount amount of LP tokens to burn /// @param outputTokenIndex index of target asset to receive /// @param deadline optional deadline - /// @return amountOutUint uint amount of asset outputTokenIndex sent to receiver + /// @return amountOut uint amount of asset outputTokenIndex sent to receiver + /// @return outFee uint amount of output asset kept by the LP's and protocol as a fee function burnSwap( address payer, address receiver, @@ -248,7 +249,7 @@ interface IPartyPool is IERC20Metadata, IOwnable { uint256 outputTokenIndex, uint256 deadline, bool unwrap - ) external returns (uint256 amountOutUint); + ) external returns (uint256 amountOut, uint256 outFee); /// @dev Initiate a flash loan. /// @param receiver The receiver of the tokens in the loan, and the receiver of the callback. diff --git a/src/LMSRStabilized.sol b/src/LMSRStabilized.sol index bbe24db..5f86540 100644 --- a/src/LMSRStabilized.sol +++ b/src/LMSRStabilized.sol @@ -519,7 +519,7 @@ library LMSRStabilized { State storage s, uint256 i, int128 alpha - ) internal view returns (int128 amountOut, int128 amountIn) { + ) internal view returns (int128 amountIn, int128 amountOut) { return swapAmountsForBurn(s.kappa, s.qInternal, i, alpha); } @@ -536,14 +536,14 @@ library LMSRStabilized { /// @param qInternal Cached internal balances in 64.64 fixed-point format /// @param i Index of output asset /// @param alpha Proportional share to burn (0 < alpha <= 1) - /// @return amountOut Amount of asset i received (in 64.64 fixed-point) /// @return amountIn LP size-metric redeemed (alpha * S) + /// @return amountOut Amount of asset i received (in 64.64 fixed-point) function swapAmountsForBurn( int128 kappa, int128[] memory qInternal, uint256 i, int128 alpha - ) internal pure returns (int128 amountOut, int128 amountIn) { + ) internal pure returns (int128 amountIn, int128 amountOut) { require(alpha > int128(0) && alpha <= ONE, "LMSR: alpha"); int128 sizeMetric = _computeSizeMetric(qInternal); @@ -566,10 +566,10 @@ library LMSRStabilized { } // Start totalOut with direct portion of asset i redeemed - int128 totalOut = alpha.mul(qInternal[i]); + amountOut = alpha.mul(qInternal[i]); // Track whether any non-zero contribution was produced - bool anyNonZero = (totalOut > int128(0)); + bool anyNonZero = (amountOut > int128(0)); // For each asset j != i, swap the withdrawn a_j := alpha * q_j into i for (uint256 j = 0; j < n; ) { @@ -621,7 +621,7 @@ library LMSRStabilized { // Update q_local: pool receives amountInUsed on asset j, and loses qLocal[i] qLocal[j] = qLocal[j].add(amountInUsed); // subtract capped output from qLocal[i] (becomes zero) - totalOut = totalOut.add(qLocal[i]); + amountOut = amountOut.add(qLocal[i]); qLocal[i] = int128(0); anyNonZero = true; unchecked { j++; } @@ -632,7 +632,7 @@ library LMSRStabilized { // Update q_local accordingly: pool receives aj on j, and loses y on i qLocal[j] = qLocal[j].add(aj); qLocal[i] = qLocal[i].sub(y); - totalOut = totalOut.add(y); + amountOut = amountOut.add(y); anyNonZero = true; } } @@ -640,12 +640,9 @@ library LMSRStabilized { } // If no asset contributed (totalOut == 0) treat as no-trade and revert - if (!anyNonZero || totalOut <= int128(0)) { + if (!anyNonZero || amountOut <= int128(0)) { revert("LMSR: zero output"); } - - amountOut = totalOut; - return (amountOut, amountIn); } diff --git a/src/PartyPool.sol b/src/PartyPool.sol index fc2e0b1..0a4da2b 100644 --- a/src/PartyPool.sol +++ b/src/PartyPool.sol @@ -408,14 +408,7 @@ contract PartyPool is PartyPoolBase, OwnableExternal, ERC20External, IPartyPool return abi.decode(result, (uint256, uint256, uint256)); } - /// @notice Burn LP _tokens then swap the redeemed proportional basket into a single asset `inputTokenIndex` and send to receiver. - /// @dev This function forwards the call to the burnSwap implementation via delegatecall - /// @param payer who burns LP _tokens - /// @param receiver who receives the single asset - /// @param lpAmount amount of LP _tokens to burn - /// @param outputTokenIndex index of target asset to receive - /// @param deadline optional deadline - /// @return amountOutUint uint amount of asset i sent to receiver + /// @inheritdoc IPartyPool function burnSwap( address payer, address receiver, @@ -423,7 +416,7 @@ contract PartyPool is PartyPoolBase, OwnableExternal, ERC20External, IPartyPool uint256 outputTokenIndex, uint256 deadline, bool unwrap - ) external returns (uint256 amountOutUint) { + ) external returns (uint256 amountOut, uint256 outFee) { bytes memory data = abi.encodeWithSelector( PartyPoolMintImpl.burnSwap.selector, payer, @@ -437,7 +430,7 @@ contract PartyPool is PartyPoolBase, OwnableExternal, ERC20External, IPartyPool ); bytes memory result = Address.functionDelegateCall(address(MINT_IMPL), data); - return abi.decode(result, (uint256)); + return abi.decode(result, (uint256,uint256 )); } diff --git a/src/PartyPoolBase.sol b/src/PartyPoolBase.sol index 8e8ef3f..c629fa0 100644 --- a/src/PartyPoolBase.sol +++ b/src/PartyPoolBase.sol @@ -96,7 +96,10 @@ abstract contract PartyPoolBase is OwnableInternal, ERC20Internal, ReentrancyGua function _pairFeePpmView(uint256 i, uint256 j) internal view returns (uint256) { uint256 fi = _fees[i]; uint256 fj = _fees[j]; - return fi + fj - fi * fj / 1_000_000; + // multiplicative combination, while mathematically correct, is more confusing to users + // return fi + fj - fi * fj / 1_000_000; + // additive fees are easy to understand and very very close to the multiplicative combination. + return fi + fj; } function _pairFeePpm(uint256 i, uint256 j) internal returns (uint256 fee) { diff --git a/src/PartyPoolMintImpl.sol b/src/PartyPoolMintImpl.sol index 701d768..11b5620 100644 --- a/src/PartyPoolMintImpl.sol +++ b/src/PartyPoolMintImpl.sol @@ -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 diff --git a/test/LMSRStabilized.t.sol b/test/LMSRStabilized.t.sol index cc30ede..51526e8 100644 --- a/test/LMSRStabilized.t.sol +++ b/test/LMSRStabilized.t.sol @@ -900,7 +900,7 @@ contract LMSRStabilizedTest is Test { int128 alpha = ABDKMath64x64.divu(1, 100); // 1% int128 S = _computeSizeMetric(s.qInternal); - (int128 payout, int128 burned) = s.swapAmountsForBurn(0, alpha); + (int128 burned, int128 payout) = s.swapAmountsForBurn(0, alpha); // burned should equal alpha * S assertEq(burned, alpha.mul(S), "burned size-metric mismatch"); @@ -921,7 +921,7 @@ contract LMSRStabilizedTest is Test { _updateCachedQInternal(mockQInternal); int128 alpha = ABDKMath64x64.divu(1, 100); // 1% - (int128 payout, int128 burned) = s.swapAmountsForBurn(0, alpha); + (int128 burned, int128 payout) = s.swapAmountsForBurn(0, alpha); // Should still burn the size metric int128 S = _computeSizeMetric(mockQInternal); diff --git a/test/NativeTest.t.sol b/test/NativeTest.t.sol index c9e6110..a323e38 100644 --- a/test/NativeTest.t.sol +++ b/test/NativeTest.t.sol @@ -484,7 +484,7 @@ contract NativeTest is Test { uint256 thisEthBefore = address(this).balance; // Burn LP and receive all proceeds as native currency (WETH unwrapped) - uint256 payout = pool.burnSwap( + (uint256 payout, ) = pool.burnSwap( address(this), // payer (holds LP) address(this), // receiver lpToBurn, // lpAmount @@ -506,7 +506,7 @@ contract NativeTest is Test { uint256 bobEthBefore = bob.balance; // Burn LP and send native currency to bob - uint256 payout = pool.burnSwap( + (uint256 payout, ) = pool.burnSwap( address(this), // payer bob, // receiver lpToBurn, @@ -555,7 +555,7 @@ contract NativeTest is Test { // 4. Burn LP to native currency uint256 lpToBurn = lpMinted / 2; - uint256 payout = pool.burnSwap(alice, alice, lpToBurn, 2, 0, true); + (uint256 payout, ) = pool.burnSwap(alice, alice, lpToBurn, 2, 0, true); assertTrue(payout > 0, "Should receive payout in native"); // Alice should have some ETH back (maybe more or less depending on slippage) diff --git a/test/PartyPool.t.sol b/test/PartyPool.t.sol index 61102d6..ee88055 100644 --- a/test/PartyPool.t.sol +++ b/test/PartyPool.t.sol @@ -784,7 +784,7 @@ contract PartyPoolTest is Test { uint256 bobBefore = token0.balanceOf(bob); // Call burnSwap where this contract is the payer (it holds initial LP from setUp) - uint256 payout = pool.burnSwap(address(this), bob, lpToBurn, target, 0, false); + (uint256 payout, ) = pool.burnSwap(address(this), bob, lpToBurn, target, 0, false); // Payout must be > 0 assertTrue(payout > 0, "burnSwap should produce a payout");