per-asset fees
This commit is contained in:
@@ -61,8 +61,8 @@ interface IPartyPool is IERC20Metadata, IOwnable {
|
||||
IERC20 indexed tokenOut,
|
||||
uint256 amountIn,
|
||||
uint256 amountOut,
|
||||
uint256 lpFee,
|
||||
uint256 protocolFee
|
||||
uint256 lpFee, // taken from the output token
|
||||
uint256 protocolFee // taken from the output token
|
||||
);
|
||||
|
||||
event Flash(
|
||||
@@ -97,8 +97,11 @@ interface IPartyPool is IERC20Metadata, IOwnable {
|
||||
/// @dev denominators()[i] is the base for tokens[i]. These bases are chosen by deployer and must match token decimals.
|
||||
function denominators() external view returns (uint256[] memory);
|
||||
|
||||
/// @notice Per-swap fee in parts-per-million (ppm). Fee is taken from input amounts before LMSR computations.
|
||||
function swapFeePpm() external view returns (uint256);
|
||||
/// @notice Per-asset swap fees in ppm. Fees are applied on input; for asset-to-asset swaps, the effective pair fee is 1 - (1 - f_i)(1 - f_j).
|
||||
function fees() external view returns (uint256[] memory);
|
||||
|
||||
/// @notice Effective combined fee in ppm for the given asset pair (i as input, j as output).
|
||||
function fee(uint256 i, uint256 j) external view returns (uint256);
|
||||
|
||||
/// @notice Flash-loan fee in parts-per-million (ppm) applied to flash borrow amounts.
|
||||
function flashFeePpm() external view returns (uint256);
|
||||
@@ -163,13 +166,13 @@ interface IPartyPool is IERC20Metadata, IOwnable {
|
||||
/// @param outputTokenIndex index of output token
|
||||
/// @param maxAmountIn maximum gross input allowed (inclusive of fee)
|
||||
/// @param limitPrice maximum acceptable marginal price (pass 0 to ignore)
|
||||
/// @return amountIn gross input amount to transfer (includes fee), amountOut output amount user would receive, fee fee amount taken
|
||||
/// @return amountIn gross input amount to transfer (includes fee), amountOut output amount user would receive, inFee fee taken from input amount
|
||||
function swapAmounts(
|
||||
uint256 inputTokenIndex,
|
||||
uint256 outputTokenIndex,
|
||||
uint256 maxAmountIn,
|
||||
int128 limitPrice
|
||||
) external view returns (uint256 amountIn, uint256 amountOut, uint256 fee);
|
||||
) external view returns (uint256 amountIn, uint256 amountOut, uint256 inFee);
|
||||
|
||||
/// @notice Swap input token inputTokenIndex -> token outputTokenIndex. Payer must approve token inputTokenIndex.
|
||||
/// @dev This function transfers the exact gross input (including fee) from payer and sends the computed output to receiver.
|
||||
@@ -181,7 +184,7 @@ interface IPartyPool is IERC20Metadata, IOwnable {
|
||||
/// @param maxAmountIn maximum amount of token inputTokenIndex (uint256) to transfer in (inclusive of fees)
|
||||
/// @param limitPrice maximum acceptable marginal price (64.64 fixed point). Pass 0 to ignore.
|
||||
/// @param deadline timestamp after which the transaction will revert. Pass 0 to ignore.
|
||||
/// @return amountIn actual input used (uint256), amountOut actual output sent (uint256), fee fee taken from the input (uint256)
|
||||
/// @return amountIn actual input used (uint256), amountOut actual output sent (uint256), inFee fee taken from the input (uint256)
|
||||
function swap(
|
||||
address payer,
|
||||
address receiver,
|
||||
@@ -191,7 +194,7 @@ interface IPartyPool is IERC20Metadata, IOwnable {
|
||||
int128 limitPrice,
|
||||
uint256 deadline,
|
||||
bool unwrap
|
||||
) external payable returns (uint256 amountIn, uint256 amountOut, uint256 fee);
|
||||
) external payable returns (uint256 amountIn, uint256 amountOut, uint256 inFee);
|
||||
|
||||
/// @notice Swap up to the price limit; computes max input to reach limit then performs swap.
|
||||
/// @dev If balances prevent fully reaching the limit, the function caps and returns actuals.
|
||||
@@ -202,7 +205,7 @@ interface IPartyPool is IERC20Metadata, IOwnable {
|
||||
/// @param outputTokenIndex index of output asset
|
||||
/// @param limitPrice target marginal price to reach (must be > 0)
|
||||
/// @param deadline timestamp after which the transaction will revert. Pass 0 to ignore.
|
||||
/// @return amountInUsed actual input used excluding fee (uint256), amountOut actual output sent (uint256), fee fee taken from the input (uint256)
|
||||
/// @return amountInUsed actual input used excluding fee (uint256), amountOut actual output sent (uint256), inFee fee taken from the input (uint256)
|
||||
function swapToLimit(
|
||||
address payer,
|
||||
address receiver,
|
||||
@@ -211,7 +214,7 @@ interface IPartyPool is IERC20Metadata, IOwnable {
|
||||
int128 limitPrice,
|
||||
uint256 deadline,
|
||||
bool unwrap
|
||||
) external payable returns (uint256 amountInUsed, uint256 amountOut, uint256 fee);
|
||||
) external payable returns (uint256 amountInUsed, uint256 amountOut, uint256 inFee);
|
||||
|
||||
/// @notice Single-token mint: deposit a single token, charge swap-LMSR cost, and mint LP.
|
||||
/// @dev swapMint executes as an exact-in planned swap followed by proportional scaling of qInternal.
|
||||
@@ -221,14 +224,14 @@ interface IPartyPool is IERC20Metadata, IOwnable {
|
||||
/// @param inputTokenIndex index of the input token
|
||||
/// @param maxAmountIn maximum uint token input (inclusive of fee)
|
||||
/// @param deadline optional deadline
|
||||
/// @return lpMinted actual LP minted (uint)
|
||||
/// @return amountInUsed actual input used (uint256), lpMinted actual LP minted (uint256), inFee fee taken from the input (uint256)
|
||||
function swapMint(
|
||||
address payer,
|
||||
address receiver,
|
||||
uint256 inputTokenIndex,
|
||||
uint256 maxAmountIn,
|
||||
uint256 deadline
|
||||
) external payable returns (uint256 lpMinted);
|
||||
) external payable returns (uint256 amountInUsed, uint256 lpMinted, uint256 inFee);
|
||||
|
||||
/// @notice Burn LP tokens then swap the redeemed proportional basket into a single asset `outputTokenIndex` and send to receiver.
|
||||
/// @dev The function burns LP tokens (authorization via allowance if needed), sends the single-asset payout and updates LMSR state.
|
||||
|
||||
@@ -34,27 +34,27 @@ interface IPartyPoolViewer {
|
||||
/// @param inputTokenIndex index of input token
|
||||
/// @param outputTokenIndex index of output token
|
||||
/// @param limitPrice target marginal price to reach (must be > 0)
|
||||
/// @return amountIn gross input amount to transfer (includes fee), amountOut output amount user would receive, fee fee amount taken
|
||||
/// @return amountIn gross input amount to transfer (includes fee), amountOut output amount user would receive, inFee fee taken from input amount
|
||||
function swapToLimitAmounts(
|
||||
IPartyPool pool,
|
||||
uint256 inputTokenIndex,
|
||||
uint256 outputTokenIndex,
|
||||
int128 limitPrice
|
||||
) external view returns (uint256 amountIn, uint256 amountOut, uint256 fee);
|
||||
) external view returns (uint256 amountIn, uint256 amountOut, uint256 inFee);
|
||||
|
||||
/// @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)
|
||||
function swapMintAmounts(IPartyPool pool, uint256 inputTokenIndex, uint256 maxAmountIn) external view
|
||||
returns (uint256 amountInUsed, uint256 fee, uint256 lpMinted);
|
||||
returns (uint256 amountInUsed, uint256 lpMinted, uint256 inFee);
|
||||
|
||||
/// @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 outputTokenIndex index of target asset to receive
|
||||
function burnSwapAmounts(IPartyPool pool, uint256 lpAmount, uint256 outputTokenIndex) external view
|
||||
returns (uint256 amountOut);
|
||||
returns (uint256 amountOut, uint256 outFee);
|
||||
|
||||
/// @notice Compute repayment amounts (principal + flash fee) for a proposed flash loan.
|
||||
/// @param loanAmounts array of per-token loan amounts; must match the pool's token ordering.
|
||||
|
||||
@@ -131,8 +131,6 @@ library LMSRStabilized {
|
||||
int128 a,
|
||||
int128 limitPrice
|
||||
) internal pure returns (int128 amountIn, int128 amountOut) {
|
||||
require(i < nAssets && j < nAssets, "LMSR: idx");
|
||||
|
||||
// Initialize amountIn to full amount (will be adjusted if limit price is hit)
|
||||
amountIn = a;
|
||||
|
||||
@@ -140,48 +138,37 @@ library LMSRStabilized {
|
||||
int128 sizeMetric = _computeSizeMetric(qInternal);
|
||||
require(sizeMetric > int128(0), "LMSR: size metric zero");
|
||||
int128 b = kappa.mul(sizeMetric);
|
||||
require(b > int128(0), "LMSR: b<=0");
|
||||
|
||||
// Precompute reciprocal of b to avoid repeated divisions
|
||||
int128 invB = ABDKMath64x64.div(ONE, b);
|
||||
|
||||
// Guard: output asset must have non-zero effective weight to avoid degenerate/div-by-zero-like conditions
|
||||
require(qInternal[j] > int128(0), "LMSR: e_j==0");
|
||||
|
||||
// Compute r0 = exp((q_i - q_j) / b) directly using invB
|
||||
int128 r0 = _exp(qInternal[i].sub(qInternal[j]).mul(invB));
|
||||
require(r0 > int128(0), "LMSR: r0<=0"); // equivalent to e_j > 0 check
|
||||
|
||||
// If a positive limitPrice is given, determine whether the full `a` would
|
||||
// push the marginal price p_i/p_j beyond the limit; if so, truncate `a`.
|
||||
// Marginal price ratio evolves as r(t) = r0 * exp(t/b) (since e_i multiplies by exp(t/b))
|
||||
if (limitPrice > int128(0)) {
|
||||
// r0 must be positive; if r0 == 0 then no risk of exceeding limit by increasing r.
|
||||
require(r0 >= int128(0), "LMSR: r0<0");
|
||||
if (r0 == int128(0)) {
|
||||
// console2.log("r0 == 0 (input asset has zero weight), no limit truncation needed");
|
||||
// If limitPrice <= current price, we revert (caller must choose a limit > current price to allow any fill)
|
||||
if (limitPrice <= r0) {
|
||||
revert("LMSR: limitPrice <= current price");
|
||||
}
|
||||
|
||||
// Compute a_limit directly from ln(limit / r0): a_limit = b * ln(limit / r0)
|
||||
int128 ratioLimitOverR0 = limitPrice.div(r0);
|
||||
require(ratioLimitOverR0 > int128(0), "LMSR: ratio<=0");
|
||||
|
||||
int128 aLimitOverB = _ln(ratioLimitOverR0); // > 0
|
||||
|
||||
// aLimit = b * aLimitOverB
|
||||
int128 aLimit64 = b.mul(aLimitOverB);
|
||||
|
||||
// If computed aLimit is less than the requested a, use the truncated value.
|
||||
if (aLimit64 < a) {
|
||||
amountIn = aLimit64; // Store the truncated input amount
|
||||
a = aLimit64; // Use truncated amount for calculations
|
||||
} else {
|
||||
// If limitPrice <= current price, we revert (caller must choose a limit > current price to allow any fill)
|
||||
if (limitPrice <= r0) {
|
||||
revert("LMSR: limitPrice <= current price");
|
||||
}
|
||||
|
||||
// Compute a_limit directly from ln(limit / r0): a_limit = b * ln(limit / r0)
|
||||
int128 ratioLimitOverR0 = limitPrice.div(r0);
|
||||
require(ratioLimitOverR0 > int128(0), "LMSR: ratio<=0");
|
||||
|
||||
int128 aLimitOverB = _ln(ratioLimitOverR0); // > 0
|
||||
|
||||
// aLimit = b * aLimitOverB
|
||||
int128 aLimit64 = b.mul(aLimitOverB);
|
||||
|
||||
// If computed aLimit is less than the requested a, use the truncated value.
|
||||
if (aLimit64 < a) {
|
||||
amountIn = aLimit64; // Store the truncated input amount
|
||||
a = aLimit64; // Use truncated amount for calculations
|
||||
} else {
|
||||
// console2.log("Not truncating: aLimit64 >= a");
|
||||
}
|
||||
// no truncation needed
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,28 +242,19 @@ library LMSRStabilized {
|
||||
uint256 j,
|
||||
int128 limitPrice
|
||||
) internal pure returns (int128 amountIn, int128 amountOut) {
|
||||
require(i < nAssets && j < nAssets, "LMSR: idx");
|
||||
require(limitPrice > int128(0), "LMSR: limitPrice <= 0");
|
||||
|
||||
// Compute b and ensure positivity before deriving invB
|
||||
int128 sizeMetric = _computeSizeMetric(qInternal);
|
||||
require(sizeMetric > int128(0), "LMSR: size metric zero");
|
||||
int128 b = kappa.mul(sizeMetric);
|
||||
require(b > int128(0), "LMSR: b<=0");
|
||||
|
||||
// Precompute reciprocal of b to avoid repeated divisions
|
||||
int128 invB = ABDKMath64x64.div(ONE, b);
|
||||
|
||||
// Guard: output asset must have non-zero effective weight to avoid degenerate/div-by-zero-like conditions
|
||||
require(qInternal[j] > int128(0), "LMSR: e_j==0");
|
||||
|
||||
// Compute r0 = exp((q_i - q_j) / b) directly using invB
|
||||
int128 r0 = _exp(qInternal[i].sub(qInternal[j]).mul(invB));
|
||||
|
||||
// Mirror swapAmountsForExactInput behavior: treat invalid r0 as an error condition.
|
||||
// Revert if r0 is non-positive (no finite trade under a price limit).
|
||||
require(r0 > int128(0), "LMSR: r0<=0");
|
||||
|
||||
// If current price already exceeds or equals limit, revert the same way swapAmountsForExactInput does.
|
||||
if (r0 >= limitPrice) {
|
||||
revert("LMSR: limitPrice <= current price");
|
||||
|
||||
@@ -81,14 +81,14 @@ contract PartyPlanner is OwnableExternal, IPartyPlanner {
|
||||
protocolFeeAddress = protocolFeeAddress_;
|
||||
}
|
||||
|
||||
/// Main newPool variant: accepts kappa directly (preferred).
|
||||
/// Main newPool variant: accepts kappa directly (preferred) and a per-asset fee vector.
|
||||
function newPool(
|
||||
// Pool constructor args
|
||||
string memory name_,
|
||||
string memory symbol_,
|
||||
IERC20[] memory tokens_,
|
||||
int128 kappa_,
|
||||
uint256 swapFeePpm_,
|
||||
uint256[] memory swapFeesPpm_,
|
||||
uint256 flashFeePpm_,
|
||||
bool stable_,
|
||||
// Initial deposit information
|
||||
@@ -107,6 +107,9 @@ contract PartyPlanner is OwnableExternal, IPartyPlanner {
|
||||
// Validate kappa > 0 (Q64.64)
|
||||
require(kappa_ > int128(0), "Planner: kappa must be > 0");
|
||||
|
||||
// Validate fees vector length matches number of tokens
|
||||
require(swapFeesPpm_.length == tokens_.length, "Planner: fees and tokens length mismatch");
|
||||
|
||||
// Create a new PartyPool instance (kappa-based constructor)
|
||||
IPartyPoolDeployer deployer = stable_ && tokens_.length == 2 ? BALANCED_PAIR_DEPLOYER : NORMAL_POOL_DEPLOYER;
|
||||
pool = deployer.deploy(
|
||||
@@ -115,7 +118,7 @@ contract PartyPlanner is OwnableExternal, IPartyPlanner {
|
||||
symbol_,
|
||||
tokens_,
|
||||
kappa_,
|
||||
swapFeePpm_,
|
||||
swapFeesPpm_,
|
||||
flashFeePpm_,
|
||||
PROTOCOL_FEE_PPM,
|
||||
protocolFeeAddress,
|
||||
@@ -155,6 +158,48 @@ contract PartyPlanner is OwnableExternal, IPartyPlanner {
|
||||
lpAmount = pool.initialMint(receiver, initialLpAmount);
|
||||
}
|
||||
|
||||
/// Convenience overload: legacy single-fee signature — repeat the scalar for every asset and delegate.
|
||||
function newPool(
|
||||
// Pool constructor args (legacy single-fee)
|
||||
string memory name_,
|
||||
string memory symbol_,
|
||||
IERC20[] memory tokens_,
|
||||
int128 kappa_,
|
||||
uint256 swapFeePpm_,
|
||||
uint256 flashFeePpm_,
|
||||
bool stable_,
|
||||
// Initial deposit information
|
||||
address payer,
|
||||
address receiver,
|
||||
uint256[] memory initialDeposits,
|
||||
uint256 initialLpAmount,
|
||||
uint256 deadline
|
||||
) public onlyOwner returns (IPartyPool pool, uint256 lpAmount) {
|
||||
// Build per-asset fee vector by repeating the scalar swapFeePpm_
|
||||
uint256[] memory feesArr = new uint256[](tokens_.length);
|
||||
for (uint256 i = 0; i < tokens_.length; i++) {
|
||||
// We divide by two, because the new per-asset fee semantics charges both the in-asset fee and
|
||||
// out-asset fee. This should be a square-root for exactness.
|
||||
feesArr[i] = swapFeePpm_ / 2;
|
||||
}
|
||||
|
||||
// Delegate to the vector-based newPool variant
|
||||
return newPool(
|
||||
name_,
|
||||
symbol_,
|
||||
tokens_,
|
||||
kappa_,
|
||||
feesArr,
|
||||
flashFeePpm_,
|
||||
stable_,
|
||||
payer,
|
||||
receiver,
|
||||
initialDeposits,
|
||||
initialLpAmount,
|
||||
deadline
|
||||
);
|
||||
}
|
||||
|
||||
// NOTE that the slippage target is only exactly achieved in completely balanced pools where all assets are
|
||||
// priced the same. This target is actually a minimum slippage that the pool imposes on traders, and the actual
|
||||
// slippage cost can be multiples bigger in practice due to pool inventory imbalances.
|
||||
|
||||
@@ -54,9 +54,11 @@ contract PartyPool is PartyPoolBase, OwnableExternal, ERC20External, IPartyPool
|
||||
int128 private immutable KAPPA; // kappa in Q64.64
|
||||
function kappa() external view returns (int128) { return KAPPA; }
|
||||
|
||||
/// @notice Per-swap fee in parts-per-million (ppm). Fee is taken from input amounts before LMSR computations.
|
||||
uint256 private immutable SWAP_FEE_PPM;
|
||||
function swapFeePpm() external view returns (uint256) { return SWAP_FEE_PPM; }
|
||||
/// @notice Per-asset swap fees in ppm.
|
||||
function fees() external view returns (uint256[] memory) { return _fees; }
|
||||
|
||||
/// @notice Effective combined fee in ppm for (i as input, j as output)
|
||||
function fee(uint256 i, uint256 j) external view returns (uint256) { return _pairFeePpm(i,j); }
|
||||
|
||||
/// @notice Flash-loan fee in parts-per-million (ppm) applied to flash borrow amounts.
|
||||
uint256 private immutable FLASH_FEE_PPM;
|
||||
@@ -100,7 +102,7 @@ contract PartyPool is PartyPoolBase, OwnableExternal, ERC20External, IPartyPool
|
||||
/// @param symbol_ LP token symbol
|
||||
/// @param tokens_ token addresses (n)
|
||||
/// @param kappa_ liquidity parameter κ (Q64.64) used to derive b = κ * S(q)
|
||||
/// @param swapFeePpm_ fee in parts-per-million, taken from swap input amounts before LMSR calculations
|
||||
/// @param fees_ per-asset swap fees in ppm (length must equal tokens_.length)
|
||||
/// @param flashFeePpm_ fee in parts-per-million, taken for flash loans
|
||||
/// @param swapImpl_ address of the SwapMint implementation contract
|
||||
/// @param mintImpl_ address of the Mint implementation contract
|
||||
@@ -110,7 +112,7 @@ contract PartyPool is PartyPoolBase, OwnableExternal, ERC20External, IPartyPool
|
||||
string memory symbol_,
|
||||
IERC20[] memory tokens_,
|
||||
int128 kappa_,
|
||||
uint256 swapFeePpm_,
|
||||
uint256[] memory fees_,
|
||||
uint256 flashFeePpm_,
|
||||
uint256 protocolFeePpm_,
|
||||
address protocolFeeAddress_,
|
||||
@@ -126,11 +128,17 @@ contract PartyPool is PartyPoolBase, OwnableExternal, ERC20External, IPartyPool
|
||||
require(tokens_.length > 1, "Pool: need >1 asset");
|
||||
_tokens = tokens_;
|
||||
KAPPA = kappa_;
|
||||
require(swapFeePpm_ < 1_000_000, "Pool: fee >= ppm");
|
||||
SWAP_FEE_PPM = swapFeePpm_;
|
||||
require(flashFeePpm_ < 1_000_000, "Pool: flash fee >= ppm");
|
||||
require(fees_.length == tokens_.length, "Pool: fees length");
|
||||
// validate ppm bounds and assign
|
||||
_fees = new uint256[](fees_.length);
|
||||
for (uint256 i = 0; i < fees_.length; i++) {
|
||||
// Cap all fees at 1%
|
||||
require(fees_[i] < 10_000, "Pool: fee >= 1%");
|
||||
_fees[i] = fees_[i];
|
||||
}
|
||||
require(flashFeePpm_ < 10_000, "Pool: flash fee >= 1%");
|
||||
FLASH_FEE_PPM = flashFeePpm_;
|
||||
require(protocolFeePpm_ < 1_000_000, "Pool: protocol fee >= ppm");
|
||||
require(protocolFeePpm_ < 400_000, "Pool: protocol fee >= 40%");
|
||||
// If the protocolFeePpm_ is set, then also require the fee address to be nonzero
|
||||
require(protocolFeePpm_ == 0 || protocolFeeAddress_ != address(0));
|
||||
PROTOCOL_FEE_PPM = protocolFeePpm_;
|
||||
@@ -168,8 +176,10 @@ contract PartyPool is PartyPoolBase, OwnableExternal, ERC20External, IPartyPool
|
||||
/// @notice If a security problem is found, the vault owner may call this function to permanently disable swap and
|
||||
/// mint functionality, leaving only burns (withdrawals) working.
|
||||
function kill() external onlyOwner {
|
||||
_killed = true;
|
||||
emit Killed();
|
||||
if( !_killed ) {
|
||||
_killed = true;
|
||||
emit Killed();
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------------
|
||||
@@ -232,7 +242,7 @@ contract PartyPool is PartyPoolBase, OwnableExternal, ERC20External, IPartyPool
|
||||
uint256 outputTokenIndex,
|
||||
uint256 maxAmountIn,
|
||||
int128 limitPrice
|
||||
) external view returns (uint256 amountIn, uint256 amountOut, uint256 fee) {
|
||||
) external view returns (uint256 amountIn, uint256 amountOut, uint256 inFee) {
|
||||
(uint256 grossIn, uint256 outUint,,,, uint256 feeUint) = _quoteSwapExactIn(inputTokenIndex, outputTokenIndex, maxAmountIn, limitPrice);
|
||||
return (grossIn, outUint, feeUint);
|
||||
}
|
||||
@@ -247,7 +257,7 @@ contract PartyPool is PartyPoolBase, OwnableExternal, ERC20External, IPartyPool
|
||||
int128 limitPrice,
|
||||
uint256 deadline,
|
||||
bool unwrap
|
||||
) external payable native nonReentrant killable returns (uint256 amountIn, uint256 amountOut, uint256 fee) {
|
||||
) external payable native nonReentrant killable returns (uint256 amountIn, uint256 amountOut, uint256 inFee) {
|
||||
require(deadline == 0 || block.timestamp <= deadline, "swap: deadline exceeded");
|
||||
|
||||
// Compute amounts using the same path as views
|
||||
@@ -303,10 +313,8 @@ contract PartyPool is PartyPoolBase, OwnableExternal, ERC20External, IPartyPool
|
||||
uint256 outputTokenIndex,
|
||||
uint256 maxAmountIn,
|
||||
int128 limitPrice
|
||||
)
|
||||
internal
|
||||
view
|
||||
returns (
|
||||
) internal view
|
||||
returns (
|
||||
uint256 grossIn,
|
||||
uint256 amountOutUint,
|
||||
int128 amountInInternalUsed,
|
||||
@@ -316,7 +324,8 @@ contract PartyPool is PartyPoolBase, OwnableExternal, ERC20External, IPartyPool
|
||||
)
|
||||
{
|
||||
// Estimate max net input (fee on gross rounded up, then subtract)
|
||||
(, uint256 netUintForSwap) = _computeFee(maxAmountIn, SWAP_FEE_PPM);
|
||||
uint256 pairFeePpm = _pairFeePpm(inputTokenIndex, outputTokenIndex);
|
||||
(, uint256 netUintForSwap) = _computeFee(maxAmountIn, pairFeePpm);
|
||||
|
||||
// Convert to internal (floor)
|
||||
int128 deltaInternalI = _uintToInternalFloor(netUintForSwap, _bases[inputTokenIndex]);
|
||||
@@ -332,8 +341,8 @@ contract PartyPool is PartyPoolBase, OwnableExternal, ERC20External, IPartyPool
|
||||
// Compute gross transfer including fee on the used input (ceil)
|
||||
feeUint = 0;
|
||||
grossIn = amountInUintNoFee;
|
||||
if (SWAP_FEE_PPM > 0) {
|
||||
feeUint = _ceilFee(amountInUintNoFee, SWAP_FEE_PPM);
|
||||
if (pairFeePpm > 0) {
|
||||
feeUint = _ceilFee(amountInUintNoFee, pairFeePpm);
|
||||
grossIn += feeUint;
|
||||
}
|
||||
|
||||
@@ -354,7 +363,7 @@ contract PartyPool is PartyPoolBase, OwnableExternal, ERC20External, IPartyPool
|
||||
int128 limitPrice,
|
||||
uint256 deadline,
|
||||
bool unwrap
|
||||
) external payable returns (uint256 amountInUsed, uint256 amountOut, uint256 fee) {
|
||||
) external payable returns (uint256 amountInUsed, uint256 amountOut, uint256 inFee) {
|
||||
bytes memory data = abi.encodeWithSelector(
|
||||
PartyPoolSwapImpl.swapToLimit.selector,
|
||||
payer,
|
||||
@@ -364,7 +373,7 @@ contract PartyPool is PartyPoolBase, OwnableExternal, ERC20External, IPartyPool
|
||||
limitPrice,
|
||||
deadline,
|
||||
unwrap,
|
||||
SWAP_FEE_PPM,
|
||||
_pairFeePpm(inputTokenIndex, outputTokenIndex),
|
||||
PROTOCOL_FEE_PPM
|
||||
);
|
||||
bytes memory result = Address.functionDelegateCall(address(SWAP_IMPL), data);
|
||||
@@ -379,14 +388,14 @@ contract PartyPool is PartyPoolBase, OwnableExternal, ERC20External, IPartyPool
|
||||
/// @param inputTokenIndex index of the input token
|
||||
/// @param maxAmountIn maximum uint token input (inclusive of fee)
|
||||
/// @param deadline optional deadline
|
||||
/// @return lpMinted actual LP minted (uint)
|
||||
/// @return amountInUsed actual input used (uint256), lpMinted actual LP minted (uint256), inFee fee taken from the input (uint256)
|
||||
function swapMint(
|
||||
address payer,
|
||||
address receiver,
|
||||
uint256 inputTokenIndex,
|
||||
uint256 maxAmountIn,
|
||||
uint256 deadline
|
||||
) external payable returns (uint256 lpMinted) {
|
||||
) external payable returns (uint256 amountInUsed, uint256 lpMinted, uint256 inFee) {
|
||||
bytes memory data = abi.encodeWithSelector(
|
||||
PartyPoolMintImpl.swapMint.selector,
|
||||
payer,
|
||||
@@ -394,12 +403,12 @@ contract PartyPool is PartyPoolBase, OwnableExternal, ERC20External, IPartyPool
|
||||
inputTokenIndex,
|
||||
maxAmountIn,
|
||||
deadline,
|
||||
SWAP_FEE_PPM,
|
||||
_assetFeePpm(inputTokenIndex),
|
||||
PROTOCOL_FEE_PPM
|
||||
);
|
||||
|
||||
bytes memory result = Address.functionDelegateCall(address(MINT_IMPL), data);
|
||||
return abi.decode(result, (uint256));
|
||||
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.
|
||||
@@ -426,7 +435,7 @@ contract PartyPool is PartyPoolBase, OwnableExternal, ERC20External, IPartyPool
|
||||
outputTokenIndex,
|
||||
deadline,
|
||||
unwrap,
|
||||
SWAP_FEE_PPM,
|
||||
_assetFeePpm(outputTokenIndex),
|
||||
PROTOCOL_FEE_PPM
|
||||
);
|
||||
|
||||
@@ -435,13 +444,11 @@ contract PartyPool is PartyPoolBase, OwnableExternal, ERC20External, IPartyPool
|
||||
}
|
||||
|
||||
|
||||
bytes32 internal constant FLASH_CALLBACK_SUCCESS = keccak256("ERC3156FlashBorrower.onFlashLoan");
|
||||
|
||||
/**
|
||||
* @dev Loan `amount` _tokens to `receiver`, and takes it back plus a `flashFee` after the callback.
|
||||
* @param receiver The contract receiving the _tokens, needs to implement the `onFlashLoan(address user, uint256 amount, uint256 fee, bytes calldata)` interface.
|
||||
* @dev Loan `amount` tokens to `receiver`, and takes it back plus a `flashFee` after the callback.
|
||||
* @param receiver The contract receiving the tokens, needs to implement the `onFlashLoan(address user, uint256 amount, uint256 fee, bytes calldata)` interface.
|
||||
* @param tokenAddr The loan currency.
|
||||
* @param amount The amount of _tokens lent.
|
||||
* @param amount The amount of tokens lent.
|
||||
* @param data A data parameter to be passed on to the `receiver` for any custom use.
|
||||
*/
|
||||
function flashLoan(
|
||||
@@ -449,37 +456,19 @@ contract PartyPool is PartyPoolBase, OwnableExternal, ERC20External, IPartyPool
|
||||
address tokenAddr,
|
||||
uint256 amount,
|
||||
bytes calldata data
|
||||
) external nonReentrant killable returns (bool)
|
||||
) external returns (bool)
|
||||
{
|
||||
IERC20 token = IERC20(tokenAddr);
|
||||
require(amount <= token.balanceOf(address(this)));
|
||||
uint256 tokenIndex = _tokenAddressToIndexPlusOne[token];
|
||||
require(tokenIndex != 0, 'flash: token not in pool');
|
||||
tokenIndex -= 1;
|
||||
(uint256 fee, ) = _computeFee(amount, FLASH_FEE_PPM);
|
||||
|
||||
// Compute protocol share of flash fee
|
||||
uint256 protoShare = 0;
|
||||
if (PROTOCOL_FEE_PPM > 0 && fee > 0) {
|
||||
protoShare = (fee * PROTOCOL_FEE_PPM) / 1_000_000; // floor
|
||||
if (protoShare > 0) {
|
||||
_protocolFeesOwed[tokenIndex] += protoShare;
|
||||
}
|
||||
}
|
||||
|
||||
_sendTokenTo(token, address(receiver), amount, false);
|
||||
require(receiver.onFlashLoan(msg.sender, address(token), amount, fee, data) == FLASH_CALLBACK_SUCCESS);
|
||||
_receiveTokenFrom(address(receiver), token, amount + fee);
|
||||
|
||||
// Update cached balance for the borrowed token
|
||||
uint256 balAfter = token.balanceOf(address(this));
|
||||
// Inline _recordCachedBalance logic
|
||||
require(balAfter >= _protocolFeesOwed[tokenIndex], "balance < protocol owed");
|
||||
_cachedUintBalances[tokenIndex] = balAfter - _protocolFeesOwed[tokenIndex];
|
||||
|
||||
emit Flash(msg.sender, receiver, token, amount, fee-protoShare, protoShare);
|
||||
|
||||
return true;
|
||||
bytes memory payload = abi.encodeWithSelector(
|
||||
PartyPoolSwapImpl.flashLoan.selector,
|
||||
receiver,
|
||||
tokenAddr,
|
||||
amount,
|
||||
data,
|
||||
FLASH_FEE_PPM,
|
||||
PROTOCOL_FEE_PPM
|
||||
);
|
||||
bytes memory result = Address.functionDelegateCall(address(SWAP_IMPL), payload);
|
||||
return abi.decode(result, (bool));
|
||||
}
|
||||
|
||||
|
||||
@@ -490,7 +479,7 @@ contract PartyPool is PartyPoolBase, OwnableExternal, ERC20External, IPartyPool
|
||||
PartyPoolSwapImpl.collectProtocolFees.selector,
|
||||
protocolFeeAddress
|
||||
);
|
||||
Address.functionDelegateCall(address(MINT_IMPL), data);
|
||||
Address.functionDelegateCall(address(SWAP_IMPL), data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ contract PartyPoolBalancedPair is PartyPool {
|
||||
string memory symbol_,
|
||||
IERC20[] memory tokens_,
|
||||
int128 kappa_,
|
||||
uint256 swapFeePpm_,
|
||||
uint256[] memory fees_,
|
||||
uint256 flashFeePpm_,
|
||||
uint256 protocolFeePpm_, // NEW: protocol share of fees (ppm)
|
||||
address protocolFeeAddress_, // NEW: recipient for collected protocol tokens
|
||||
@@ -24,7 +24,7 @@ contract PartyPoolBalancedPair is PartyPool {
|
||||
PartyPoolSwapImpl swapMintImpl_,
|
||||
PartyPoolMintImpl mintImpl_
|
||||
)
|
||||
PartyPool(owner_, name_, symbol_, tokens_, kappa_, swapFeePpm_, flashFeePpm_, protocolFeePpm_, protocolFeeAddress_, wrapperToken_, swapMintImpl_, mintImpl_)
|
||||
PartyPool(owner_, name_, symbol_, tokens_, kappa_, fees_, flashFeePpm_, protocolFeePpm_, protocolFeeAddress_, wrapperToken_, swapMintImpl_, mintImpl_)
|
||||
{}
|
||||
|
||||
function _swapAmountsForExactInput(uint256 i, uint256 j, int128 a, int128 limitPrice) internal virtual override view
|
||||
|
||||
@@ -24,6 +24,9 @@ abstract contract PartyPoolBase is OwnableInternal, ERC20Internal, ReentrancyGua
|
||||
WRAPPER_TOKEN = wrapper_;
|
||||
}
|
||||
|
||||
/// @notice Per-asset swap fees in ppm. Fees are applied on input for swaps; see helpers for composition rules.
|
||||
uint256[] internal _fees;
|
||||
|
||||
//
|
||||
// Internal state
|
||||
//
|
||||
@@ -78,6 +81,23 @@ abstract contract PartyPoolBase is OwnableInternal, ERC20Internal, ReentrancyGua
|
||||
Conversion & fee helpers (internal)
|
||||
---------------------- */
|
||||
|
||||
// Per-asset fee getters and composition
|
||||
function _assetFeePpm(uint256 i) internal view returns (uint256) {
|
||||
if (_fees.length == 0) return 0;
|
||||
return _fees[i];
|
||||
}
|
||||
|
||||
// Effective pair fee: 1 - (1-fi)(1-fj) in ppm, rounding in favor of the pool, and guarding
|
||||
// overflows by using 1e6 ppm base.
|
||||
// We implement this as: ceil( fi + fj - (fi*fj)/1e6 ) for the real-valued expression.
|
||||
// For integer arithmetic with fi,fj in ppm this is equal to: fi + fj - floor( (fi*fj)/1e6 ).
|
||||
// So we compute prod = fi * fj, prodDiv = prod / 1e6 (floor), and return fi + fj - prodDiv.
|
||||
function _pairFeePpm(uint256 i, uint256 j) internal view returns (uint256) {
|
||||
uint256 fi = _fees[i];
|
||||
uint256 fj = _fees[j];
|
||||
return fi + fj - fi * fj / 1_000_000;
|
||||
}
|
||||
|
||||
// Convert uint token amount -> internal 64.64 (floor). Uses ABDKMath64x64.divu which truncates.
|
||||
function _uintToInternalFloor(uint256 amount, uint256 base) internal pure returns (int128) {
|
||||
// internal = amount / base (as Q64.64)
|
||||
|
||||
@@ -16,7 +16,7 @@ interface IPartyPoolDeployer {
|
||||
string memory symbol_,
|
||||
IERC20[] memory tokens_,
|
||||
int128 kappa_,
|
||||
uint256 swapFeePpm_,
|
||||
uint256[] memory fees_,
|
||||
uint256 flashFeePpm_,
|
||||
uint256 protocolFeePpm_,
|
||||
address protocolFeeAddress_,
|
||||
@@ -33,7 +33,7 @@ contract PartyPoolDeployer is IPartyPoolDeployer {
|
||||
string memory symbol_,
|
||||
IERC20[] memory tokens_,
|
||||
int128 kappa_,
|
||||
uint256 swapFeePpm_,
|
||||
uint256[] memory fees_,
|
||||
uint256 flashFeePpm_,
|
||||
uint256 protocolFeePpm_,
|
||||
address protocolFeeAddress_,
|
||||
@@ -47,7 +47,7 @@ contract PartyPoolDeployer is IPartyPoolDeployer {
|
||||
symbol_,
|
||||
tokens_,
|
||||
kappa_,
|
||||
swapFeePpm_,
|
||||
fees_,
|
||||
flashFeePpm_,
|
||||
protocolFeePpm_,
|
||||
protocolFeeAddress_,
|
||||
@@ -65,7 +65,7 @@ contract PartyPoolBalancedPairDeployer is IPartyPoolDeployer {
|
||||
string memory symbol_,
|
||||
IERC20[] memory tokens_,
|
||||
int128 kappa_,
|
||||
uint256 swapFeePpm_,
|
||||
uint256[] memory fees_,
|
||||
uint256 flashFeePpm_,
|
||||
uint256 protocolFeePpm_,
|
||||
address protocolFeeAddress_,
|
||||
@@ -79,7 +79,7 @@ contract PartyPoolBalancedPairDeployer is IPartyPoolDeployer {
|
||||
symbol_,
|
||||
tokens_,
|
||||
kappa_,
|
||||
swapFeePpm_,
|
||||
fees_,
|
||||
flashFeePpm_,
|
||||
protocolFeePpm_,
|
||||
protocolFeeAddress_,
|
||||
|
||||
@@ -268,8 +268,8 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
||||
/// @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
|
||||
/// @return lpMinted LP tokens that would be minted
|
||||
/// @return inFee fee amount charged
|
||||
function swapMintAmounts(
|
||||
uint256 inputTokenIndex,
|
||||
uint256 maxAmountIn,
|
||||
@@ -277,7 +277,7 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
||||
LMSRStabilized.State memory lmsrState,
|
||||
uint256[] memory bases_,
|
||||
uint256 totalSupply_
|
||||
) public pure returns (uint256 amountInUsed, uint256 fee, uint256 lpMinted) {
|
||||
) public pure returns (uint256 amountInUsed, uint256 lpMinted, uint256 inFee) {
|
||||
require(inputTokenIndex < bases_.length, "swapMintAmounts: idx");
|
||||
require(maxAmountIn > 0, "swapMintAmounts: input zero");
|
||||
require(lmsrState.nAssets > 0, "swapMintAmounts: uninit pool");
|
||||
@@ -304,11 +304,11 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
||||
require(amountInUsed > 0, "swapMintAmounts: input zero after internal conversion");
|
||||
|
||||
// Compute fee on the actual used input (ceiling)
|
||||
fee = 0;
|
||||
inFee = 0;
|
||||
if (swapFeePpm > 0) {
|
||||
fee = (amountInUsed * swapFeePpm + 999999) / 1000000; // ceil fee
|
||||
inFee = (amountInUsed * swapFeePpm + 999999) / 1000000; // ceil fee
|
||||
}
|
||||
uint256 totalTransfer = amountInUsed + fee;
|
||||
uint256 totalTransfer = amountInUsed + inFee;
|
||||
require(totalTransfer > 0 && totalTransfer <= maxAmountIn, "swapMintAmounts: transfer exceeds max");
|
||||
|
||||
// Compute old and new scaled size metrics to determine LP minted
|
||||
@@ -345,7 +345,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 lpMinted actual LP minted (uint)
|
||||
/// @return amountInUsed actual input used (uint256), lpMinted actual LP minted (uint256), inFee fee taken from the input (uint256)
|
||||
function swapMint(
|
||||
address payer,
|
||||
address receiver,
|
||||
@@ -354,7 +354,7 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
||||
uint256 deadline,
|
||||
uint256 swapFeePpm,
|
||||
uint256 protocolFeePpm
|
||||
) external payable native killable nonReentrant returns (uint256 lpMinted) {
|
||||
) external payable native killable nonReentrant returns (uint256 amountInUsed, uint256 lpMinted, uint256 inFee) {
|
||||
uint256 n = _tokens.length;
|
||||
require(inputTokenIndex < n, "swapMint: idx");
|
||||
require(maxAmountIn > 0, "swapMint: input zero");
|
||||
@@ -435,7 +435,10 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
||||
emit IPartyPool.SwapMint(payer, receiver, _tokens[inputTokenIndex],
|
||||
totalTransfer, actualLpToMint, feeUintActual-protoShare, protoShare);
|
||||
|
||||
return actualLpToMint;
|
||||
amountInUsed = amountInUint;
|
||||
lpMinted = actualLpToMint;
|
||||
inFee = feeUintActual;
|
||||
return (amountInUsed, lpMinted, inFee);
|
||||
}
|
||||
|
||||
/// @notice Calculate the amounts for a burn swap operation
|
||||
@@ -454,7 +457,7 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
||||
LMSRStabilized.State memory lmsrState,
|
||||
uint256[] memory bases_,
|
||||
uint256 totalSupply_
|
||||
) public pure returns (uint256 amountOut) {
|
||||
) public pure returns (uint256 amountOut, uint256 outFee) {
|
||||
require(outputTokenIndex < bases_.length, "burnSwapAmounts: idx");
|
||||
require(lpAmount > 0, "burnSwapAmounts: zero lp");
|
||||
require(totalSupply_ > 0, "burnSwapAmounts: empty supply");
|
||||
@@ -470,6 +473,13 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
||||
// 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.nAssets, lmsrState.kappa, lmsrState.qInternal,
|
||||
outputTokenIndex, alphaGross);
|
||||
uint256 payoutGrossUint = _internalToUintFloorPure(payoutGrossInternal, bases_[outputTokenIndex]);
|
||||
outFee = (payoutGrossUint > amountOut) ? (payoutGrossUint - amountOut) : 0;
|
||||
}
|
||||
|
||||
/// @notice Burn LP _tokens then swap the redeemed proportional basket into a single asset `outputTokenIndex` and send to receiver.
|
||||
|
||||
@@ -8,6 +8,7 @@ import {IPartyPool} from "./IPartyPool.sol";
|
||||
import {NativeWrapper} from "./NativeWrapper.sol";
|
||||
import {LMSRStabilized} from "./LMSRStabilized.sol";
|
||||
import {PartyPoolBase} from "./PartyPoolBase.sol";
|
||||
import {IERC3156FlashBorrower} from "../lib/openzeppelin-contracts/contracts/interfaces/IERC3156FlashBorrower.sol";
|
||||
|
||||
/// @title PartyPoolSwapMintImpl - Implementation contract for swapMint and burnSwap functions
|
||||
/// @notice This contract contains the swapMint and burnSwap implementation that will be called via delegatecall
|
||||
@@ -19,6 +20,48 @@ contract PartyPoolSwapImpl is PartyPoolBase {
|
||||
|
||||
constructor(NativeWrapper wrapper_) PartyPoolBase(wrapper_) {}
|
||||
|
||||
bytes32 internal constant FLASH_CALLBACK_SUCCESS = keccak256("ERC3156FlashBorrower.onFlashLoan");
|
||||
|
||||
function flashLoan(
|
||||
IERC3156FlashBorrower receiver,
|
||||
address tokenAddr,
|
||||
uint256 amount,
|
||||
bytes calldata data,
|
||||
uint256 flashFeePpm,
|
||||
uint256 protocolFeePpm
|
||||
) external nonReentrant killable returns (bool) {
|
||||
IERC20 token = IERC20(tokenAddr);
|
||||
require(amount <= token.balanceOf(address(this)), "flash: amount > balance");
|
||||
uint256 tokenIndex = _tokenAddressToIndexPlusOne[token];
|
||||
require(tokenIndex != 0, 'flash: token not in pool');
|
||||
tokenIndex -= 1;
|
||||
(uint256 flashFee, ) = _computeFee(amount, flashFeePpm);
|
||||
|
||||
// Compute protocol share of flash fee
|
||||
uint256 protoShare = 0;
|
||||
if (protocolFeePpm > 0 && flashFee > 0) {
|
||||
protoShare = (flashFee * protocolFeePpm) / 1_000_000; // floor
|
||||
if (protoShare > 0) {
|
||||
_protocolFeesOwed[tokenIndex] += protoShare;
|
||||
}
|
||||
}
|
||||
|
||||
_sendTokenTo(token, address(receiver), amount, false);
|
||||
require(
|
||||
receiver.onFlashLoan(msg.sender, address(token), amount, flashFee, data) == FLASH_CALLBACK_SUCCESS,
|
||||
'flash: callback'
|
||||
);
|
||||
_receiveTokenFrom(address(receiver), token, amount + flashFee);
|
||||
|
||||
// Update cached balance for the borrowed token
|
||||
uint256 balAfter = token.balanceOf(address(this));
|
||||
require(balAfter >= _protocolFeesOwed[tokenIndex], "balance < protocol owed");
|
||||
_cachedUintBalances[tokenIndex] = balAfter - _protocolFeesOwed[tokenIndex];
|
||||
|
||||
emit IPartyPool.Flash(msg.sender, receiver, token, amount, flashFee - protoShare, protoShare);
|
||||
return true;
|
||||
}
|
||||
|
||||
function swapToLimitAmounts(
|
||||
uint256 inputTokenIndex,
|
||||
uint256 outputTokenIndex,
|
||||
@@ -27,7 +70,7 @@ contract PartyPoolSwapImpl is PartyPoolBase {
|
||||
int128 kappa,
|
||||
int128[] memory qInternal,
|
||||
uint256 swapFeePpm
|
||||
) external pure returns (uint256 amountIn, uint256 amountOut, uint256 fee) {
|
||||
) external pure returns (uint256 amountIn, uint256 amountOut, uint256 inFee) {
|
||||
// Compute internal maxima at the price limit
|
||||
(int128 amountInInternal, int128 amountOutInternal) = LMSRStabilized.swapAmountsForPriceLimit(
|
||||
bases.length, kappa, qInternal,
|
||||
@@ -37,11 +80,11 @@ contract PartyPoolSwapImpl is PartyPoolBase {
|
||||
uint256 amountInUintNoFee = _internalToUintCeil(amountInInternal, bases[inputTokenIndex]);
|
||||
require(amountInUintNoFee > 0, "swapToLimit: input zero");
|
||||
|
||||
fee = 0;
|
||||
inFee = 0;
|
||||
amountIn = amountInUintNoFee;
|
||||
if (swapFeePpm > 0) {
|
||||
fee = _ceilFee(amountInUintNoFee, swapFeePpm);
|
||||
amountIn += fee;
|
||||
inFee = _ceilFee(amountInUintNoFee, swapFeePpm);
|
||||
amountIn += inFee;
|
||||
}
|
||||
|
||||
amountOut = _internalToUintFloor(amountOutInternal, bases[outputTokenIndex]);
|
||||
@@ -59,7 +102,7 @@ contract PartyPoolSwapImpl is PartyPoolBase {
|
||||
bool unwrap,
|
||||
uint256 swapFeePpm,
|
||||
uint256 protocolFeePpm
|
||||
) external payable native killable nonReentrant returns (uint256 amountInUsed, uint256 amountOut, uint256 fee) {
|
||||
) external payable native killable nonReentrant returns (uint256 amountInUsed, uint256 amountOut, uint256 inFee) {
|
||||
uint256 n = _tokens.length;
|
||||
require(inputTokenIndex < n && outputTokenIndex < n, "swapToLimit: idx");
|
||||
require(limitPrice > int128(0), "swapToLimit: limit <= 0");
|
||||
|
||||
@@ -91,13 +91,13 @@ contract PartyPoolViewer is PartyPoolHelpers, IPartyPoolViewer {
|
||||
/// @param inputTokenIndex index of input token
|
||||
/// @param outputTokenIndex index of output token
|
||||
/// @param limitPrice target marginal price to reach (must be > 0)
|
||||
/// @return amountIn gross input amount to transfer (includes fee), amountOut output amount user would receive, fee fee amount taken
|
||||
/// @return amountIn gross input amount to transfer (includes fee), amountOut output amount user would receive, inFee fee amount taken
|
||||
function swapToLimitAmounts(
|
||||
IPartyPool pool,
|
||||
uint256 inputTokenIndex,
|
||||
uint256 outputTokenIndex,
|
||||
int128 limitPrice
|
||||
) external view returns (uint256 amountIn, uint256 amountOut, uint256 fee) {
|
||||
) external view returns (uint256 amountIn, uint256 amountOut, uint256 inFee) {
|
||||
LMSRStabilized.State memory lmsr = pool.LMSR();
|
||||
require(inputTokenIndex < lmsr.nAssets && outputTokenIndex < lmsr.nAssets, "swapToLimit: idx");
|
||||
require(limitPrice > int128(0), "swapToLimit: limit <= 0");
|
||||
@@ -105,17 +105,17 @@ contract PartyPoolViewer is PartyPoolHelpers, IPartyPoolViewer {
|
||||
|
||||
return SWAP_IMPL.swapToLimitAmounts(
|
||||
inputTokenIndex, outputTokenIndex, limitPrice,
|
||||
pool.denominators(), pool.kappa(), lmsr.qInternal, pool.swapFeePpm());
|
||||
pool.denominators(), pool.kappa(), lmsr.qInternal, pool.fee(inputTokenIndex, outputTokenIndex));
|
||||
}
|
||||
|
||||
|
||||
function swapMintAmounts(IPartyPool pool, uint256 inputTokenIndex, uint256 maxAmountIn) external view
|
||||
returns (uint256 amountInUsed, uint256 fee, uint256 lpMinted) {
|
||||
returns (uint256 amountInUsed, uint256 lpMinted, uint256 inFee) {
|
||||
LMSRStabilized.State memory lmsr = pool.LMSR();
|
||||
return MINT_IMPL.swapMintAmounts(
|
||||
inputTokenIndex,
|
||||
maxAmountIn,
|
||||
pool.swapFeePpm(),
|
||||
pool.fees()[inputTokenIndex],
|
||||
lmsr,
|
||||
pool.denominators(),
|
||||
pool.totalSupply()
|
||||
@@ -124,12 +124,12 @@ contract PartyPoolViewer is PartyPoolHelpers, IPartyPoolViewer {
|
||||
|
||||
|
||||
function burnSwapAmounts(IPartyPool pool, uint256 lpAmount, uint256 outputTokenIndex) external view
|
||||
returns (uint256 amountOut) {
|
||||
returns (uint256 amountOut, uint256 outFee) {
|
||||
LMSRStabilized.State memory lmsr = pool.LMSR();
|
||||
return MINT_IMPL.burnSwapAmounts(
|
||||
lpAmount,
|
||||
outputTokenIndex,
|
||||
pool.swapFeePpm(),
|
||||
pool.fees()[outputTokenIndex],
|
||||
lmsr,
|
||||
pool.denominators(),
|
||||
pool.totalSupply()
|
||||
|
||||
Reference in New Issue
Block a user