PartyPoolView
This commit is contained in:
@@ -7,6 +7,7 @@ import {PartyPool} from "./PartyPool.sol";
|
|||||||
import {PartyPoolBalancedPair} from "./PartyPoolBalancedPair.sol";
|
import {PartyPoolBalancedPair} from "./PartyPoolBalancedPair.sol";
|
||||||
import {PartyPoolMintImpl} from "./PartyPoolMintImpl.sol";
|
import {PartyPoolMintImpl} from "./PartyPoolMintImpl.sol";
|
||||||
import {PartyPoolSwapImpl} from "./PartyPoolSwapImpl.sol";
|
import {PartyPoolSwapImpl} from "./PartyPoolSwapImpl.sol";
|
||||||
|
import {PartyPoolView} from "./PartyPoolView.sol";
|
||||||
|
|
||||||
library Deploy {
|
library Deploy {
|
||||||
|
|
||||||
@@ -62,4 +63,8 @@ library Deploy {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function newViewer() internal returns (PartyPoolView) {
|
||||||
|
return new PartyPoolView(new PartyPoolSwapImpl(), new PartyPoolMintImpl());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
// SPDX-License-Identifier: UNLICENSED
|
// SPDX-License-Identifier: UNLICENSED
|
||||||
pragma solidity ^0.8.30;
|
pragma solidity ^0.8.30;
|
||||||
|
|
||||||
import {IERC20} from "../lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
|
import "./LMSRStabilized.sol";
|
||||||
import {IERC20Metadata} from "../lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol";
|
import {IERC20Metadata} from "../lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol";
|
||||||
|
import {IERC20} from "../lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
|
||||||
|
|
||||||
/// @title PartyPool - LMSR-backed multi-asset pool with LP ERC20 token
|
/// @title PartyPool - LMSR-backed multi-asset pool with LP ERC20 token
|
||||||
/// @notice A multi-asset liquidity pool backed by the LMSRStabilized pricing model.
|
/// @notice A multi-asset liquidity pool backed by the LMSRStabilized pricing model.
|
||||||
@@ -56,6 +57,8 @@ interface IPartyPool is IERC20Metadata {
|
|||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
function LMSR() external view returns (LMSRStabilized.State memory);
|
||||||
|
|
||||||
/// @notice Token addresses comprising the pool. Effectively immutable after construction.
|
/// @notice Token addresses comprising the pool. Effectively immutable after construction.
|
||||||
/// @dev tokens[i] corresponds to the i-th asset and maps to index i in the internal LMSR arrays.
|
/// @dev tokens[i] corresponds to the i-th asset and maps to index i in the internal LMSR arrays.
|
||||||
function getToken(uint256) external view returns (IERC20); // get single token
|
function getToken(uint256) external view returns (IERC20); // get single token
|
||||||
@@ -103,14 +106,6 @@ interface IPartyPool is IERC20Metadata {
|
|||||||
/// @param lpTokens The number of LP tokens to issue for this mint. If 0, then the number of tokens returned will equal the LMSR internal q total
|
/// @param lpTokens The number of LP tokens to issue for this mint. If 0, then the number of tokens returned will equal the LMSR internal q total
|
||||||
function initialMint(address receiver, uint256 lpTokens) external returns (uint256 lpMinted);
|
function initialMint(address receiver, uint256 lpTokens) external returns (uint256 lpMinted);
|
||||||
|
|
||||||
/// @notice Calculate the proportional deposit amounts required for a given LP token amount
|
|
||||||
/// @dev Returns the minimum token amounts (rounded up) that must be supplied to receive lpTokenAmount
|
|
||||||
/// LP tokens at current pool proportions. If the pool is empty (initial deposit) returns zeros
|
|
||||||
/// because the initial deposit is handled by transferring tokens then calling mint().
|
|
||||||
/// @param lpTokenAmount The amount of LP tokens desired
|
|
||||||
/// @return depositAmounts Array of token amounts to deposit (rounded up)
|
|
||||||
function mintAmounts(uint256 lpTokenAmount) external view returns (uint256[] memory depositAmounts);
|
|
||||||
|
|
||||||
/// @notice Proportional mint (or initial supply if first call).
|
/// @notice Proportional mint (or initial supply if first call).
|
||||||
/// @dev - For initial supply: assumes tokens have already been transferred to the pool prior to calling.
|
/// @dev - For initial supply: assumes tokens have already been transferred to the pool prior to calling.
|
||||||
/// - For subsequent mints: payer must approve the required token amounts before calling.
|
/// - For subsequent mints: payer must approve the required token amounts before calling.
|
||||||
@@ -122,13 +117,6 @@ interface IPartyPool is IERC20Metadata {
|
|||||||
/// @return lpMinted the actual amount of lpToken minted
|
/// @return lpMinted the actual amount of lpToken minted
|
||||||
function mint(address payer, address receiver, uint256 lpTokenAmount, uint256 deadline) external returns (uint256 lpMinted);
|
function mint(address payer, address receiver, uint256 lpTokenAmount, uint256 deadline) external returns (uint256 lpMinted);
|
||||||
|
|
||||||
/// @notice Calculate the proportional withdrawal amounts for a given LP token amount
|
|
||||||
/// @dev Returns the maximum token amounts (rounded down) that will be withdrawn when burning lpTokenAmount.
|
|
||||||
/// If the pool is uninitialized or supply is zero, returns zeros.
|
|
||||||
/// @param lpTokenAmount The amount of LP tokens to burn
|
|
||||||
/// @return withdrawAmounts Array of token amounts to withdraw (rounded down)
|
|
||||||
function burnAmounts(uint256 lpTokenAmount) external view returns (uint256[] memory withdrawAmounts);
|
|
||||||
|
|
||||||
/// @notice Burn LP tokens and withdraw the proportional basket to receiver.
|
/// @notice Burn LP tokens and withdraw the proportional basket to receiver.
|
||||||
/// @dev Payer must own or approve the LP tokens being burned. The function updates LMSR state
|
/// @dev Payer must own or approve the LP tokens being burned. The function updates LMSR state
|
||||||
/// proportionally to reflect the reduced pool size after the withdrawal.
|
/// proportionally to reflect the reduced pool size after the withdrawal.
|
||||||
@@ -175,17 +163,6 @@ interface IPartyPool is IERC20Metadata {
|
|||||||
uint256 deadline
|
uint256 deadline
|
||||||
) external returns (uint256 amountIn, uint256 amountOut, uint256 fee);
|
) external returns (uint256 amountIn, uint256 amountOut, uint256 fee);
|
||||||
|
|
||||||
/// @notice External view to quote swap-to-limit amounts (gross input incl. fee and output), matching swapToLimit() computations
|
|
||||||
/// @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
|
|
||||||
function swapToLimitAmounts(
|
|
||||||
uint256 inputTokenIndex,
|
|
||||||
uint256 outputTokenIndex,
|
|
||||||
int128 limitPrice
|
|
||||||
) external view returns (uint256 amountIn, uint256 amountOut, uint256 fee);
|
|
||||||
|
|
||||||
/// @notice Swap up to the price limit; computes max input to reach limit then performs swap.
|
/// @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.
|
/// @dev If balances prevent fully reaching the limit, the function caps and returns actuals.
|
||||||
/// The payer must transfer the exact gross input computed by the view.
|
/// The payer must transfer the exact gross input computed by the view.
|
||||||
@@ -238,28 +215,6 @@ interface IPartyPool is IERC20Metadata {
|
|||||||
uint256 deadline
|
uint256 deadline
|
||||||
) external returns (uint256 amountOutUint);
|
) external returns (uint256 amountOutUint);
|
||||||
|
|
||||||
/// @notice Marginal price of `base` denominated in `quote` as Q64.64.
|
|
||||||
/// @dev Returns the LMSR marginal price p_quote / p_base in ABDK 64.64 fixed-point format.
|
|
||||||
/// Useful for off-chain quoting; raw 64.64 value is returned (no scaling to token units).
|
|
||||||
/// @param baseTokenIndex index of the base asset (e.g., ETH)
|
|
||||||
/// @param quoteTokenIndex index of the quote asset (e.g., USD)
|
|
||||||
/// @return price Q64.64 value equal to quote per base (p_quote / p_base)
|
|
||||||
function price(uint256 baseTokenIndex, uint256 quoteTokenIndex) external view returns (int128);
|
|
||||||
|
|
||||||
/// @notice Price of one LP token denominated in `quote` as Q64.64.
|
|
||||||
/// @dev Computes LMSR poolPrice (quote per unit internal qTotal) and scales it to LP units:
|
|
||||||
/// returns price_per_LP = poolPrice_quote * (totalSupply() / qTotal) in ABDK 64.64 format.
|
|
||||||
/// The returned value is raw Q64.64 and represents quote units per one LP token unit.
|
|
||||||
/// @param quoteTokenIndex index of the quote asset in which to denominate the LP price
|
|
||||||
/// @return price Q64.64 value equal to quote per LP token unit
|
|
||||||
function poolPrice(uint256 quoteTokenIndex) external view returns (int128);
|
|
||||||
|
|
||||||
/// @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.
|
|
||||||
/// @return repaymentAmounts array where repaymentAmounts[i] = loanAmounts[i] + ceil(loanAmounts[i] * flashFeePpm)
|
|
||||||
function flashRepaymentAmounts(uint256[] memory loanAmounts) external view
|
|
||||||
returns (uint256[] memory repaymentAmounts);
|
|
||||||
|
|
||||||
/// @notice Receive token amounts and require them to be repaid plus a fee inside a callback.
|
/// @notice Receive token amounts and require them to be repaid plus a fee inside a callback.
|
||||||
/// @dev The caller must implement IPartyFlashCallback#partyFlashCallback which receives (amounts, repaymentAmounts, data).
|
/// @dev The caller must implement IPartyFlashCallback#partyFlashCallback which receives (amounts, repaymentAmounts, data).
|
||||||
/// This function verifies that, after the callback returns, the pool's balances have increased by at least the fees
|
/// This function verifies that, after the callback returns, the pool's balances have increased by at least the fees
|
||||||
|
|||||||
@@ -80,6 +80,9 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
|
|||||||
/// @inheritdoc IPartyPool
|
/// @inheritdoc IPartyPool
|
||||||
function denominators() external view returns (uint256[] memory) { return bases; }
|
function denominators() external view returns (uint256[] memory) { return bases; }
|
||||||
|
|
||||||
|
function LMSR() external view returns (LMSRStabilized.State memory) { return lmsr; }
|
||||||
|
|
||||||
|
|
||||||
/// @param name_ LP token name
|
/// @param name_ LP token name
|
||||||
/// @param symbol_ LP token symbol
|
/// @param symbol_ LP token symbol
|
||||||
/// @param tokens_ token addresses (n)
|
/// @param tokens_ token addresses (n)
|
||||||
@@ -87,7 +90,7 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
|
|||||||
/// @param kappa_ liquidity parameter κ (Q64.64) used to derive b = κ * S(q)
|
/// @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 swapFeePpm_ fee in parts-per-million, taken from swap input amounts before LMSR calculations
|
||||||
/// @param flashFeePpm_ fee in parts-per-million, taken for flash loans
|
/// @param flashFeePpm_ fee in parts-per-million, taken for flash loans
|
||||||
/// @param swapMintImpl_ address of the SwapMint implementation contract
|
/// @param swapImpl_ address of the SwapMint implementation contract
|
||||||
/// @param mintImpl_ address of the Mint implementation contract
|
/// @param mintImpl_ address of the Mint implementation contract
|
||||||
constructor(
|
constructor(
|
||||||
string memory name_,
|
string memory name_,
|
||||||
@@ -99,7 +102,7 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
|
|||||||
uint256 flashFeePpm_,
|
uint256 flashFeePpm_,
|
||||||
uint256 protocolFeePpm_, // NEW: protocol share of fees (ppm)
|
uint256 protocolFeePpm_, // NEW: protocol share of fees (ppm)
|
||||||
address protocolFeeAddress_, // NEW: recipient for collected protocol tokens
|
address protocolFeeAddress_, // NEW: recipient for collected protocol tokens
|
||||||
PartyPoolSwapImpl swapMintImpl_,
|
PartyPoolSwapImpl swapImpl_,
|
||||||
PartyPoolMintImpl mintImpl_
|
PartyPoolMintImpl mintImpl_
|
||||||
) ERC20External(name_, symbol_) {
|
) ERC20External(name_, symbol_) {
|
||||||
require(tokens_.length > 1, "Pool: need >1 asset");
|
require(tokens_.length > 1, "Pool: need >1 asset");
|
||||||
@@ -114,7 +117,7 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
|
|||||||
require(protocolFeePpm_ < 1_000_000, "Pool: protocol fee >= ppm");
|
require(protocolFeePpm_ < 1_000_000, "Pool: protocol fee >= ppm");
|
||||||
PROTOCOL_FEE_PPM = protocolFeePpm_;
|
PROTOCOL_FEE_PPM = protocolFeePpm_;
|
||||||
PROTOCOL_FEE_ADDRESS = protocolFeeAddress_;
|
PROTOCOL_FEE_ADDRESS = protocolFeeAddress_;
|
||||||
SWAP_IMPL = swapMintImpl_;
|
SWAP_IMPL = swapImpl_;
|
||||||
MINT_IMPL = mintImpl_;
|
MINT_IMPL = mintImpl_;
|
||||||
|
|
||||||
uint256 n = tokens_.length;
|
uint256 n = tokens_.length;
|
||||||
@@ -134,47 +137,6 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Current marginal prices
|
|
||||||
//
|
|
||||||
|
|
||||||
/// @notice Marginal price of `base` in terms of `quote` (p_quote / p_base) as Q64.64
|
|
||||||
/// @dev Returns the LMSR marginal price directly (raw 64.64) for use by off-chain quoting logic.
|
|
||||||
function price(uint256 baseTokenIndex, uint256 quoteTokenIndex) external view returns (int128) {
|
|
||||||
uint256 n = tokens.length;
|
|
||||||
require(baseTokenIndex < n && quoteTokenIndex < n, "price: idx");
|
|
||||||
require(lmsr.nAssets > 0, "price: uninit");
|
|
||||||
return lmsr.price(baseTokenIndex, quoteTokenIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @notice Price of one LP token denominated in `quote` asset as Q64.64
|
|
||||||
/// @dev Computes LMSR poolPrice (quote per unit qTotal) and scales it by totalSupply() / qTotal
|
|
||||||
/// to return price per LP token unit in quote asset (raw 64.64).
|
|
||||||
function poolPrice(uint256 quoteTokenIndex) external view returns (int128) {
|
|
||||||
uint256 n = tokens.length;
|
|
||||||
require(quoteTokenIndex < n, "poolPrice: idx");
|
|
||||||
require(lmsr.nAssets > 0, "poolPrice: uninit");
|
|
||||||
|
|
||||||
// price per unit of qTotal (Q64.64) from LMSR
|
|
||||||
int128 pricePerQ = lmsr.poolPrice(quoteTokenIndex);
|
|
||||||
|
|
||||||
// total internal q (qTotal) as Q64.64
|
|
||||||
int128 qTotal = _computeSizeMetric(lmsr.qInternal);
|
|
||||||
require(qTotal > int128(0), "poolPrice: qTotal zero");
|
|
||||||
|
|
||||||
// totalSupply as Q64.64
|
|
||||||
uint256 supply = totalSupply();
|
|
||||||
require(supply > 0, "poolPrice: zero supply");
|
|
||||||
int128 supplyQ64 = ABDKMath64x64.fromUInt(supply);
|
|
||||||
|
|
||||||
// factor = totalSupply / qTotal (Q64.64)
|
|
||||||
int128 factor = supplyQ64.div(qTotal);
|
|
||||||
|
|
||||||
// price per LP token = pricePerQ * factor (Q64.64)
|
|
||||||
return pricePerQ.mul(factor);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* ----------------------
|
/* ----------------------
|
||||||
Initialization / Mint / Burn (LP token managed)
|
Initialization / Mint / Burn (LP token managed)
|
||||||
---------------------- */
|
---------------------- */
|
||||||
@@ -192,11 +154,6 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
|
|||||||
return abi.decode(result, (uint256));
|
return abi.decode(result, (uint256));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @inheritdoc IPartyPool
|
|
||||||
function mintAmounts(uint256 lpTokenAmount) public view returns (uint256[] memory depositAmounts) {
|
|
||||||
return MINT_IMPL.mintAmounts(lpTokenAmount, lmsr.nAssets, totalSupply(), cachedUintBalances);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @notice Proportional mint for existing pool.
|
/// @notice Proportional mint for existing pool.
|
||||||
/// @dev This function forwards the call to the mint implementation via delegatecall
|
/// @dev This function forwards the call to the mint implementation via delegatecall
|
||||||
/// @param payer address that provides the input tokens
|
/// @param payer address that provides the input tokens
|
||||||
@@ -216,11 +173,6 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
|
|||||||
return abi.decode(result, (uint256));
|
return abi.decode(result, (uint256));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @inheritdoc IPartyPool
|
|
||||||
function burnAmounts(uint256 lpTokenAmount) external view returns (uint256[] memory withdrawAmounts) {
|
|
||||||
return MINT_IMPL.burnAmounts(lpTokenAmount, lmsr.nAssets, totalSupply(), cachedUintBalances);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @notice Burn LP tokens and withdraw the proportional basket to receiver.
|
/// @notice Burn LP tokens and withdraw the proportional basket to receiver.
|
||||||
/// @dev This function forwards the call to the burn implementation via delegatecall
|
/// @dev This function forwards the call to the burn implementation via delegatecall
|
||||||
/// @param payer address that provides the LP tokens to burn
|
/// @param payer address that provides the LP tokens to burn
|
||||||
@@ -367,22 +319,6 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// @inheritdoc IPartyPool
|
|
||||||
function swapToLimitAmounts(
|
|
||||||
uint256 inputTokenIndex,
|
|
||||||
uint256 outputTokenIndex,
|
|
||||||
int128 limitPrice
|
|
||||||
) external view returns (uint256 amountIn, uint256 amountOut, uint256 fee) {
|
|
||||||
require(inputTokenIndex < tokens.length && outputTokenIndex < tokens.length, "swapToLimit: idx");
|
|
||||||
require(limitPrice > int128(0), "swapToLimit: limit <= 0");
|
|
||||||
require(lmsr.nAssets > 0, "swapToLimit: pool uninitialized");
|
|
||||||
|
|
||||||
return SWAP_IMPL.swapToLimitAmounts(
|
|
||||||
inputTokenIndex, outputTokenIndex, limitPrice,
|
|
||||||
bases, KAPPA, lmsr.qInternal, SWAP_FEE_PPM);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// @inheritdoc IPartyPool
|
/// @inheritdoc IPartyPool
|
||||||
function swapToLimit(
|
function swapToLimit(
|
||||||
address payer,
|
address payer,
|
||||||
@@ -407,29 +343,6 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
|
|||||||
return abi.decode(result, (uint256,uint256,uint256));
|
return abi.decode(result, (uint256,uint256,uint256));
|
||||||
}
|
}
|
||||||
|
|
||||||
function swapMintAmounts(uint256 inputTokenIndex, uint256 maxAmountIn) external view
|
|
||||||
returns (uint256 amountInUsed, uint256 fee, uint256 lpMinted) {
|
|
||||||
return MINT_IMPL.swapMintAmounts(
|
|
||||||
inputTokenIndex,
|
|
||||||
maxAmountIn,
|
|
||||||
SWAP_FEE_PPM,
|
|
||||||
lmsr,
|
|
||||||
bases,
|
|
||||||
totalSupply()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function burnSwapAmounts(uint256 lpAmount, uint256 inputTokenIndex) external view
|
|
||||||
returns (uint256 amountOut) {
|
|
||||||
return MINT_IMPL.burnSwapAmounts(
|
|
||||||
lpAmount,
|
|
||||||
inputTokenIndex,
|
|
||||||
SWAP_FEE_PPM,
|
|
||||||
lmsr,
|
|
||||||
bases,
|
|
||||||
totalSupply()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @notice Single-token mint: deposit a single token, charge swap-LMSR cost, and mint LP.
|
/// @notice Single-token mint: deposit a single token, charge swap-LMSR cost, and mint LP.
|
||||||
/// @dev This function forwards the call to the swapMint implementation via delegatecall
|
/// @dev This function forwards the call to the swapMint implementation via delegatecall
|
||||||
@@ -492,19 +405,6 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// @inheritdoc IPartyPool
|
|
||||||
function flashRepaymentAmounts(uint256[] memory loanAmounts) external view
|
|
||||||
returns (uint256[] memory repaymentAmounts) {
|
|
||||||
repaymentAmounts = new uint256[](tokens.length);
|
|
||||||
for (uint256 i = 0; i < tokens.length; i++) {
|
|
||||||
uint256 amount = loanAmounts[i];
|
|
||||||
if (amount > 0) {
|
|
||||||
repaymentAmounts[i] = amount + _ceilFee(amount, FLASH_FEE_PPM);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// @notice Receive token amounts and require them to be repaid plus a fee inside a callback.
|
/// @notice Receive token amounts and require them to be repaid plus a fee inside a callback.
|
||||||
/// @dev The caller must implement IPartyFlashCallback#partyFlashCallback which receives (amounts, repaymentAmounts, data).
|
/// @dev The caller must implement IPartyFlashCallback#partyFlashCallback which receives (amounts, repaymentAmounts, data).
|
||||||
/// This function verifies that, after the callback returns, the pool's balances have increased by at least the fees
|
/// This function verifies that, after the callback returns, the pool's balances have increased by at least the fees
|
||||||
@@ -604,7 +504,6 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function _swapAmountsForExactInput(uint256 i, uint256 j, int128 a, int128 limitPrice) internal virtual view
|
function _swapAmountsForExactInput(uint256 i, uint256 j, int128 a, int128 limitPrice) internal virtual view
|
||||||
returns (int128 amountIn, int128 amountOut) {
|
returns (int128 amountIn, int128 amountOut) {
|
||||||
return lmsr.swapAmountsForExactInput(i, j, a, limitPrice);
|
return lmsr.swapAmountsForExactInput(i, j, a, limitPrice);
|
||||||
|
|||||||
@@ -6,10 +6,11 @@ import {IERC20} from "../lib/openzeppelin-contracts/contracts/token/ERC20/IERC20
|
|||||||
import {ReentrancyGuard} from "../lib/openzeppelin-contracts/contracts/utils/ReentrancyGuard.sol";
|
import {ReentrancyGuard} from "../lib/openzeppelin-contracts/contracts/utils/ReentrancyGuard.sol";
|
||||||
import {ERC20Internal} from "./ERC20Internal.sol";
|
import {ERC20Internal} from "./ERC20Internal.sol";
|
||||||
import {LMSRStabilized} from "./LMSRStabilized.sol";
|
import {LMSRStabilized} from "./LMSRStabilized.sol";
|
||||||
|
import {PartyPoolHelpers} from "./PartyPoolHelpers.sol";
|
||||||
|
|
||||||
/// @notice Abstract base contract that contains storage and internal helpers only.
|
/// @notice Abstract base contract that contains storage and internal helpers only.
|
||||||
/// No external/public functions or constructor here — derived implementations own immutables and constructors.
|
/// No external/public functions or constructor here — derived implementations own immutables and constructors.
|
||||||
abstract contract PartyPoolBase is ERC20Internal, ReentrancyGuard {
|
abstract contract PartyPoolBase is ERC20Internal, ReentrancyGuard, PartyPoolHelpers {
|
||||||
using ABDKMath64x64 for int128;
|
using ABDKMath64x64 for int128;
|
||||||
using LMSRStabilized for LMSRStabilized.State;
|
using LMSRStabilized for LMSRStabilized.State;
|
||||||
|
|
||||||
@@ -77,46 +78,6 @@ abstract contract PartyPoolBase is ERC20Internal, ReentrancyGuard {
|
|||||||
return floorValue;
|
return floorValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @notice Ceiling fee helper: computes ceil(x * feePpm / 1_000_000)
|
|
||||||
/// @dev Internal helper; public-facing functions use this to ensure fees round up in favor of pool.
|
|
||||||
function _ceilFee(uint256 x, uint256 feePpm) internal pure returns (uint256) {
|
|
||||||
if (feePpm == 0) return 0;
|
|
||||||
// ceil division: (num + denom - 1) / denom
|
|
||||||
return (x * feePpm + 1_000_000 - 1) / 1_000_000;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @notice Compute fee and net amounts for a gross input (fee rounded up to favor the pool).
|
|
||||||
/// @param gross total gross input
|
|
||||||
/// @param feePpm fee in ppm to apply
|
|
||||||
/// @return feeUint fee taken (uint) and netUint remaining for protocol use (uint)
|
|
||||||
function _computeFee(uint256 gross, uint256 feePpm) internal pure returns (uint256 feeUint, uint256 netUint) {
|
|
||||||
if (feePpm == 0) {
|
|
||||||
return (0, gross);
|
|
||||||
}
|
|
||||||
feeUint = _ceilFee(gross, feePpm);
|
|
||||||
netUint = gross - feeUint;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @notice Convenience: return gross = net + fee(net) using ceiling for fee.
|
|
||||||
/// @param netUint net amount
|
|
||||||
/// @param feePpm fee in ppm to apply
|
|
||||||
function _addFee(uint256 netUint, uint256 feePpm) internal pure returns (uint256 gross) {
|
|
||||||
if (feePpm == 0) return netUint;
|
|
||||||
uint256 fee = _ceilFee(netUint, feePpm);
|
|
||||||
return netUint + fee;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @notice Helper to compute size metric (sum of all asset quantities) from internal balances
|
|
||||||
/// @dev Returns the sum of all provided qInternal_ entries as a Q64.64 value.
|
|
||||||
function _computeSizeMetric(int128[] memory qInternal_) internal pure returns (int128) {
|
|
||||||
int128 total = int128(0);
|
|
||||||
for (uint i = 0; i < qInternal_.length; ) {
|
|
||||||
total = total.add(qInternal_[i]);
|
|
||||||
unchecked { i++; }
|
|
||||||
}
|
|
||||||
return total;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Helper to record cached balances as effectiveBalance = onchain - owed. Reverts if owed > onchain.
|
/// @dev Helper to record cached balances as effectiveBalance = onchain - owed. Reverts if owed > onchain.
|
||||||
function _recordCachedBalance(uint256 idx, uint256 onchainBal) internal {
|
function _recordCachedBalance(uint256 idx, uint256 onchainBal) internal {
|
||||||
uint256 owed = protocolFeesOwed[idx];
|
uint256 owed = protocolFeesOwed[idx];
|
||||||
|
|||||||
50
src/PartyPoolHelpers.sol
Normal file
50
src/PartyPoolHelpers.sol
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
// SPDX-License-Identifier: UNLICENSED
|
||||||
|
pragma solidity ^0.8.30;
|
||||||
|
|
||||||
|
import {ABDKMath64x64} from "../lib/abdk-libraries-solidity/ABDKMath64x64.sol";
|
||||||
|
|
||||||
|
|
||||||
|
abstract contract PartyPoolHelpers {
|
||||||
|
using ABDKMath64x64 for int128;
|
||||||
|
|
||||||
|
/// @notice Ceiling fee helper: computes ceil(x * feePpm / 1_000_000)
|
||||||
|
/// @dev Internal helper; public-facing functions use this to ensure fees round up in favor of pool.
|
||||||
|
function _ceilFee(uint256 x, uint256 feePpm) internal pure returns (uint256) {
|
||||||
|
if (feePpm == 0) return 0;
|
||||||
|
// ceil division: (num + denom - 1) / denom
|
||||||
|
return (x * feePpm + 1_000_000 - 1) / 1_000_000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @notice Compute fee and net amounts for a gross input (fee rounded up to favor the pool).
|
||||||
|
/// @param gross total gross input
|
||||||
|
/// @param feePpm fee in ppm to apply
|
||||||
|
/// @return feeUint fee taken (uint) and netUint remaining for protocol use (uint)
|
||||||
|
function _computeFee(uint256 gross, uint256 feePpm) internal pure returns (uint256 feeUint, uint256 netUint) {
|
||||||
|
if (feePpm == 0) {
|
||||||
|
return (0, gross);
|
||||||
|
}
|
||||||
|
feeUint = _ceilFee(gross, feePpm);
|
||||||
|
netUint = gross - feeUint;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @notice Convenience: return gross = net + fee(net) using ceiling for fee.
|
||||||
|
/// @param netUint net amount
|
||||||
|
/// @param feePpm fee in ppm to apply
|
||||||
|
function _addFee(uint256 netUint, uint256 feePpm) internal pure returns (uint256 gross) {
|
||||||
|
if (feePpm == 0) return netUint;
|
||||||
|
uint256 fee = _ceilFee(netUint, feePpm);
|
||||||
|
return netUint + fee;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @notice Helper to compute size metric (sum of all asset quantities) from internal balances
|
||||||
|
/// @dev Returns the sum of all provided qInternal_ entries as a Q64.64 value.
|
||||||
|
function _computeSizeMetric(int128[] memory qInternal_) internal pure returns (int128) {
|
||||||
|
int128 total = int128(0);
|
||||||
|
for (uint i = 0; i < qInternal_.length; ) {
|
||||||
|
total = total.add(qInternal_[i]);
|
||||||
|
unchecked { i++; }
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -17,9 +17,6 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
|||||||
using LMSRStabilized for LMSRStabilized.State;
|
using LMSRStabilized for LMSRStabilized.State;
|
||||||
using SafeERC20 for IERC20;
|
using SafeERC20 for IERC20;
|
||||||
|
|
||||||
// Events that mirror the main contract events
|
|
||||||
event Mint(address indexed payer, address indexed receiver, uint256[] depositAmounts, uint256 lpMinted);
|
|
||||||
event Burn(address indexed payer, address indexed receiver, uint256[] withdrawAmounts, uint256 lpBurned);
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Initialization Mint
|
// Initialization Mint
|
||||||
@@ -57,7 +54,7 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
|||||||
|
|
||||||
require(lpMinted > 0, "initialMint: zero LP amount");
|
require(lpMinted > 0, "initialMint: zero LP amount");
|
||||||
_mint(receiver, lpMinted);
|
_mint(receiver, lpMinted);
|
||||||
emit Mint(address(0), receiver, depositAmounts, lpMinted);
|
emit IPartyPool.Mint(address(0), receiver, depositAmounts, lpMinted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -126,7 +123,7 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
|||||||
require(actualLpToMint >= minAcceptable, "mint: insufficient LP minted");
|
require(actualLpToMint >= minAcceptable, "mint: insufficient LP minted");
|
||||||
|
|
||||||
_mint(receiver, actualLpToMint);
|
_mint(receiver, actualLpToMint);
|
||||||
emit Mint(payer, receiver, depositAmounts, actualLpToMint);
|
emit IPartyPool.Mint(payer, receiver, depositAmounts, actualLpToMint);
|
||||||
return actualLpToMint;
|
return actualLpToMint;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,9 +196,15 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
|||||||
}
|
}
|
||||||
_burn(payer, lpAmount);
|
_burn(payer, lpAmount);
|
||||||
|
|
||||||
emit Burn(payer, receiver, withdrawAmounts, lpAmount);
|
emit IPartyPool.Burn(payer, receiver, withdrawAmounts, lpAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @notice Calculate the proportional deposit amounts required for a given LP token amount
|
||||||
|
/// @dev Returns the minimum token amounts (rounded up) that must be supplied to receive lpTokenAmount
|
||||||
|
/// LP tokens at current pool proportions. If the pool is empty (initial deposit) returns zeros
|
||||||
|
/// because the initial deposit is handled by transferring tokens then calling mint().
|
||||||
|
/// @param lpTokenAmount The amount of LP tokens desired
|
||||||
|
/// @return depositAmounts Array of token amounts to deposit (rounded up)
|
||||||
function mintAmounts(uint256 lpTokenAmount,
|
function mintAmounts(uint256 lpTokenAmount,
|
||||||
uint256 numAssets, uint256 totalSupply, uint256[] memory cachedUintBalances) public pure
|
uint256 numAssets, uint256 totalSupply, uint256[] memory cachedUintBalances) public pure
|
||||||
returns (uint256[] memory depositAmounts) {
|
returns (uint256[] memory depositAmounts) {
|
||||||
|
|||||||
154
src/PartyPoolView.sol
Normal file
154
src/PartyPoolView.sol
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
// SPDX-License-Identifier: UNLICENSED
|
||||||
|
pragma solidity ^0.8.30;
|
||||||
|
|
||||||
|
import {ABDKMath64x64} from "../lib/abdk-libraries-solidity/ABDKMath64x64.sol";
|
||||||
|
import {IPartyPool} from "./IPartyPool.sol";
|
||||||
|
import {LMSRStabilized} from "./LMSRStabilized.sol";
|
||||||
|
import {PartyPoolHelpers} from "./PartyPoolHelpers.sol";
|
||||||
|
import {PartyPoolMintImpl} from "./PartyPoolMintImpl.sol";
|
||||||
|
import {PartyPoolSwapImpl} from "./PartyPoolSwapImpl.sol";
|
||||||
|
|
||||||
|
contract PartyPoolView is PartyPoolHelpers {
|
||||||
|
using ABDKMath64x64 for int128;
|
||||||
|
|
||||||
|
PartyPoolSwapImpl immutable internal SWAP_IMPL;
|
||||||
|
PartyPoolMintImpl immutable internal MINT_IMPL;
|
||||||
|
|
||||||
|
constructor(PartyPoolSwapImpl swapImpl_, PartyPoolMintImpl mintImpl) {
|
||||||
|
SWAP_IMPL = swapImpl_;
|
||||||
|
MINT_IMPL = mintImpl;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Current marginal prices
|
||||||
|
//
|
||||||
|
|
||||||
|
/// @notice Marginal price of `base` denominated in `quote` as Q64.64.
|
||||||
|
/// @dev Returns the LMSR marginal price p_quote / p_base in ABDK 64.64 fixed-point format.
|
||||||
|
/// Useful for off-chain quoting; raw 64.64 value is returned (no scaling to token units).
|
||||||
|
/// @param baseTokenIndex index of the base asset (e.g., ETH)
|
||||||
|
/// @param quoteTokenIndex index of the quote asset (e.g., USD)
|
||||||
|
/// @return price Q64.64 value equal to quote per base (p_quote / p_base)
|
||||||
|
function price(IPartyPool pool, uint256 baseTokenIndex, uint256 quoteTokenIndex) external view returns (int128) {
|
||||||
|
LMSRStabilized.State memory lmsr = pool.LMSR();
|
||||||
|
require(baseTokenIndex < lmsr.nAssets && quoteTokenIndex < lmsr.nAssets, "price: idx");
|
||||||
|
require(lmsr.nAssets > 0, "price: uninit");
|
||||||
|
return LMSRStabilized.price(lmsr.nAssets, pool.kappa(), lmsr.qInternal, baseTokenIndex, quoteTokenIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @notice Price of one LP token denominated in `quote` as Q64.64.
|
||||||
|
/// @dev Computes LMSR poolPrice (quote per unit internal qTotal) and scales it to LP units:
|
||||||
|
/// returns price_per_LP = poolPrice_quote * (totalSupply() / qTotal) in ABDK 64.64 format.
|
||||||
|
/// The returned value is raw Q64.64 and represents quote units per one LP token unit.
|
||||||
|
/// @param quoteTokenIndex index of the quote asset in which to denominate the LP price
|
||||||
|
/// @return price Q64.64 value equal to quote per LP token unit
|
||||||
|
function poolPrice(IPartyPool pool, uint256 quoteTokenIndex) external view returns (int128) {
|
||||||
|
LMSRStabilized.State memory lmsr = pool.LMSR();
|
||||||
|
require(lmsr.nAssets > 0, "poolPrice: uninit");
|
||||||
|
require(quoteTokenIndex < lmsr.nAssets, "poolPrice: idx");
|
||||||
|
|
||||||
|
// price per unit of qTotal (Q64.64) from LMSR
|
||||||
|
int128 pricePerQ = LMSRStabilized.poolPrice(lmsr.nAssets, pool.kappa(), lmsr.qInternal, quoteTokenIndex);
|
||||||
|
|
||||||
|
// total internal q (qTotal) as Q64.64
|
||||||
|
int128 qTotal = LMSRStabilized._computeSizeMetric(lmsr.qInternal);
|
||||||
|
require(qTotal > int128(0), "poolPrice: qTotal zero");
|
||||||
|
|
||||||
|
// totalSupply as Q64.64
|
||||||
|
uint256 supply = pool.totalSupply();
|
||||||
|
require(supply > 0, "poolPrice: zero supply");
|
||||||
|
int128 supplyQ64 = ABDKMath64x64.fromUInt(supply);
|
||||||
|
|
||||||
|
// factor = totalSupply / qTotal (Q64.64)
|
||||||
|
int128 factor = supplyQ64.div(qTotal);
|
||||||
|
|
||||||
|
// price per LP token = pricePerQ * factor (Q64.64)
|
||||||
|
return pricePerQ.mul(factor);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function mintAmounts(IPartyPool pool, uint256 lpTokenAmount) public view returns (uint256[] memory depositAmounts) {
|
||||||
|
LMSRStabilized.State memory lmsr = pool.LMSR();
|
||||||
|
uint256[] memory cachedUintBalances = new uint256[](lmsr.nAssets);
|
||||||
|
for( uint256 i=0; i<lmsr.nAssets; i++ )
|
||||||
|
cachedUintBalances[i] = pool.getToken(i).balanceOf(address(pool));
|
||||||
|
return MINT_IMPL.mintAmounts(lpTokenAmount, lmsr.nAssets, pool.totalSupply(), cachedUintBalances);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function burnAmounts(IPartyPool pool, uint256 lpTokenAmount) external view returns (uint256[] memory withdrawAmounts) {
|
||||||
|
LMSRStabilized.State memory lmsr = pool.LMSR();
|
||||||
|
uint256[] memory cachedUintBalances = new uint256[](lmsr.nAssets);
|
||||||
|
for( uint256 i=0; i<lmsr.nAssets; i++ )
|
||||||
|
cachedUintBalances[i] = pool.getToken(i).balanceOf(address(pool));
|
||||||
|
return MINT_IMPL.burnAmounts(lpTokenAmount, lmsr.nAssets, pool.totalSupply(), cachedUintBalances);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @notice External view to quote swap-to-limit amounts (gross input incl. fee and output), matching swapToLimit() computations
|
||||||
|
/// @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
|
||||||
|
function swapToLimitAmounts(
|
||||||
|
IPartyPool pool,
|
||||||
|
uint256 inputTokenIndex,
|
||||||
|
uint256 outputTokenIndex,
|
||||||
|
int128 limitPrice
|
||||||
|
) external view returns (uint256 amountIn, uint256 amountOut, uint256 fee) {
|
||||||
|
LMSRStabilized.State memory lmsr = pool.LMSR();
|
||||||
|
require(inputTokenIndex < lmsr.nAssets && outputTokenIndex < lmsr.nAssets, "swapToLimit: idx");
|
||||||
|
require(limitPrice > int128(0), "swapToLimit: limit <= 0");
|
||||||
|
require(lmsr.nAssets > 0, "swapToLimit: pool uninitialized");
|
||||||
|
|
||||||
|
return SWAP_IMPL.swapToLimitAmounts(
|
||||||
|
inputTokenIndex, outputTokenIndex, limitPrice,
|
||||||
|
pool.denominators(), pool.kappa(), lmsr.qInternal, pool.swapFeePpm());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function swapMintAmounts(IPartyPool pool, uint256 inputTokenIndex, uint256 maxAmountIn) external view
|
||||||
|
returns (uint256 amountInUsed, uint256 fee, uint256 lpMinted) {
|
||||||
|
LMSRStabilized.State memory lmsr = pool.LMSR();
|
||||||
|
return MINT_IMPL.swapMintAmounts(
|
||||||
|
inputTokenIndex,
|
||||||
|
maxAmountIn,
|
||||||
|
pool.swapFeePpm(),
|
||||||
|
lmsr,
|
||||||
|
pool.denominators(),
|
||||||
|
pool.totalSupply()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function burnSwapAmounts(IPartyPool pool, uint256 lpAmount, uint256 inputTokenIndex) external view
|
||||||
|
returns (uint256 amountOut) {
|
||||||
|
LMSRStabilized.State memory lmsr = pool.LMSR();
|
||||||
|
return MINT_IMPL.burnSwapAmounts(
|
||||||
|
lpAmount,
|
||||||
|
inputTokenIndex,
|
||||||
|
pool.swapFeePpm(),
|
||||||
|
lmsr,
|
||||||
|
pool.denominators(),
|
||||||
|
pool.totalSupply()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @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.
|
||||||
|
/// @return repaymentAmounts array where repaymentAmounts[i] = loanAmounts[i] + ceil(loanAmounts[i] * flashFeePpm)
|
||||||
|
function flashRepaymentAmounts(IPartyPool pool, uint256[] memory loanAmounts) external view
|
||||||
|
returns (uint256[] memory repaymentAmounts) {
|
||||||
|
LMSRStabilized.State memory lmsr = pool.LMSR();
|
||||||
|
repaymentAmounts = new uint256[](lmsr.nAssets);
|
||||||
|
for (uint256 i = 0; i < lmsr.nAssets; i++) {
|
||||||
|
uint256 amount = loanAmounts[i];
|
||||||
|
if (amount > 0) {
|
||||||
|
repaymentAmounts[i] = amount + _ceilFee(amount, pool.flashFeePpm());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@ import "../src/PartyPool.sol";
|
|||||||
import "../src/IPartyFlashCallback.sol";
|
import "../src/IPartyFlashCallback.sol";
|
||||||
import {PartyPlanner} from "../src/PartyPlanner.sol";
|
import {PartyPlanner} from "../src/PartyPlanner.sol";
|
||||||
import {Deploy} from "../src/Deploy.sol";
|
import {Deploy} from "../src/Deploy.sol";
|
||||||
|
import {PartyPoolView} from "../src/PartyPoolView.sol";
|
||||||
|
|
||||||
/// @notice Test contract that implements the flash callback for testing flash loans
|
/// @notice Test contract that implements the flash callback for testing flash loans
|
||||||
contract FlashBorrower is IPartyFlashCallback {
|
contract FlashBorrower is IPartyFlashCallback {
|
||||||
@@ -143,6 +144,7 @@ contract PartyPoolTest is Test {
|
|||||||
PartyPlanner planner;
|
PartyPlanner planner;
|
||||||
PartyPool pool;
|
PartyPool pool;
|
||||||
PartyPool pool10;
|
PartyPool pool10;
|
||||||
|
PartyPoolView viewer;
|
||||||
|
|
||||||
address alice;
|
address alice;
|
||||||
address bob;
|
address bob;
|
||||||
@@ -283,6 +285,8 @@ contract PartyPoolTest is Test {
|
|||||||
token7.mint(bob, INIT_BAL);
|
token7.mint(bob, INIT_BAL);
|
||||||
token8.mint(bob, INIT_BAL);
|
token8.mint(bob, INIT_BAL);
|
||||||
token9.mint(bob, INIT_BAL);
|
token9.mint(bob, INIT_BAL);
|
||||||
|
|
||||||
|
viewer = Deploy.newViewer();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @notice Basic sanity: initial mint should have produced LP tokens for this contract and the pool holds tokens.
|
/// @notice Basic sanity: initial mint should have produced LP tokens for this contract and the pool holds tokens.
|
||||||
@@ -324,7 +328,7 @@ contract PartyPoolTest is Test {
|
|||||||
token2.approve(address(pool), type(uint256).max);
|
token2.approve(address(pool), type(uint256).max);
|
||||||
|
|
||||||
// Inspect the deposit amounts that the pool will require (these are rounded up)
|
// Inspect the deposit amounts that the pool will require (these are rounded up)
|
||||||
uint256[] memory deposits = pool.mintAmounts(1);
|
uint256[] memory deposits = viewer.mintAmounts(pool, 1);
|
||||||
|
|
||||||
// Basic sanity: deposits array length must match token count and not all zero necessarily
|
// Basic sanity: deposits array length must match token count and not all zero necessarily
|
||||||
assertEq(deposits.length, 3);
|
assertEq(deposits.length, 3);
|
||||||
@@ -366,7 +370,7 @@ contract PartyPoolTest is Test {
|
|||||||
uint256 totalLpBefore = pool.totalSupply();
|
uint256 totalLpBefore = pool.totalSupply();
|
||||||
|
|
||||||
// Compute required deposits and perform mint for 1 wei
|
// Compute required deposits and perform mint for 1 wei
|
||||||
uint256[] memory deposits = pool.mintAmounts(1);
|
uint256[] memory deposits = viewer.mintAmounts(pool, 1);
|
||||||
|
|
||||||
// Sum deposits as deposited_value
|
// Sum deposits as deposited_value
|
||||||
uint256 depositedValue = 0;
|
uint256 depositedValue = 0;
|
||||||
@@ -407,7 +411,7 @@ contract PartyPoolTest is Test {
|
|||||||
|
|
||||||
// Request half of LP supply
|
// Request half of LP supply
|
||||||
uint256 want = totalLp / 2;
|
uint256 want = totalLp / 2;
|
||||||
uint256[] memory deposits = pool.mintAmounts(want);
|
uint256[] memory deposits = viewer.mintAmounts(pool, want);
|
||||||
|
|
||||||
// We expect each deposit to be roughly half the pool balance, but due to rounding up it should satisfy:
|
// We expect each deposit to be roughly half the pool balance, but due to rounding up it should satisfy:
|
||||||
// deposits[i] * 2 >= cached balance (i.e., rounding up)
|
// deposits[i] * 2 >= cached balance (i.e., rounding up)
|
||||||
@@ -424,7 +428,7 @@ contract PartyPoolTest is Test {
|
|||||||
assertTrue(totalLp > 0, "precondition: LP > 0");
|
assertTrue(totalLp > 0, "precondition: LP > 0");
|
||||||
|
|
||||||
// Compute amounts required to redeem entire supply (should be current balances)
|
// Compute amounts required to redeem entire supply (should be current balances)
|
||||||
uint256[] memory withdrawAmounts = pool.burnAmounts(totalLp);
|
uint256[] memory withdrawAmounts = viewer.burnAmounts(pool, totalLp);
|
||||||
|
|
||||||
// Sanity: withdrawAmounts should equal pool balances (or very close due to rounding)
|
// Sanity: withdrawAmounts should equal pool balances (or very close due to rounding)
|
||||||
for (uint i = 0; i < withdrawAmounts.length; i++) {
|
for (uint i = 0; i < withdrawAmounts.length; i++) {
|
||||||
@@ -528,7 +532,7 @@ contract PartyPoolTest is Test {
|
|||||||
if (req == 0) req = 1;
|
if (req == 0) req = 1;
|
||||||
|
|
||||||
// Compute expected deposit amounts via view
|
// Compute expected deposit amounts via view
|
||||||
uint256[] memory expected = pool.mintAmounts(req);
|
uint256[] memory expected = viewer.mintAmounts(pool, req);
|
||||||
|
|
||||||
// Ensure alice has tokens and approve pool
|
// Ensure alice has tokens and approve pool
|
||||||
vm.startPrank(alice);
|
vm.startPrank(alice);
|
||||||
@@ -573,7 +577,7 @@ contract PartyPoolTest is Test {
|
|||||||
uint256 req = requests[k];
|
uint256 req = requests[k];
|
||||||
if (req == 0) req = 1;
|
if (req == 0) req = 1;
|
||||||
|
|
||||||
uint256[] memory expected = pool10.mintAmounts(req);
|
uint256[] memory expected = viewer.mintAmounts(pool10, req);
|
||||||
|
|
||||||
// Approve all tokens from alice
|
// Approve all tokens from alice
|
||||||
vm.startPrank(alice);
|
vm.startPrank(alice);
|
||||||
@@ -651,7 +655,7 @@ contract PartyPoolTest is Test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Recompute withdraw amounts via view after any top-up
|
// Recompute withdraw amounts via view after any top-up
|
||||||
uint256[] memory expected = pool.burnAmounts(req);
|
uint256[] memory expected = viewer.burnAmounts(pool, req);
|
||||||
|
|
||||||
// If expected withdraws are all zero (rounding edge), skip this iteration
|
// If expected withdraws are all zero (rounding edge), skip this iteration
|
||||||
if (expected[0] == 0 && expected[1] == 0 && expected[2] == 0) {
|
if (expected[0] == 0 && expected[1] == 0 && expected[2] == 0) {
|
||||||
@@ -708,7 +712,7 @@ contract PartyPoolTest is Test {
|
|||||||
vm.stopPrank();
|
vm.stopPrank();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint256[] memory expected = pool10.burnAmounts(req);
|
uint256[] memory expected = viewer.burnAmounts(pool10, req);
|
||||||
|
|
||||||
// If expected withdraws are all zero (rounding edge), skip this iteration
|
// If expected withdraws are all zero (rounding edge), skip this iteration
|
||||||
bool allZero = true;
|
bool allZero = true;
|
||||||
@@ -1169,7 +1173,7 @@ contract PartyPoolTest is Test {
|
|||||||
|
|
||||||
for (uint256 i = 0; i < testCases.length; i++) {
|
for (uint256 i = 0; i < testCases.length; i++) {
|
||||||
uint256[] memory loanAmounts = testCases[i];
|
uint256[] memory loanAmounts = testCases[i];
|
||||||
uint256[] memory repaymentAmounts = pool.flashRepaymentAmounts(loanAmounts);
|
uint256[] memory repaymentAmounts = viewer.flashRepaymentAmounts(pool, loanAmounts);
|
||||||
|
|
||||||
// Verify each repayment amount is correctly calculated
|
// Verify each repayment amount is correctly calculated
|
||||||
for (uint256 j = 0; j < loanAmounts.length; j++) {
|
for (uint256 j = 0; j < loanAmounts.length; j++) {
|
||||||
@@ -1361,8 +1365,8 @@ contract PartyPoolTest is Test {
|
|||||||
token2.approve(address(poolCustom), type(uint256).max);
|
token2.approve(address(poolCustom), type(uint256).max);
|
||||||
|
|
||||||
// Get required deposit amounts for both pools
|
// Get required deposit amounts for both pools
|
||||||
uint256[] memory depositsDefault = poolDefault.mintAmounts(lpRequestDefault);
|
uint256[] memory depositsDefault = viewer.mintAmounts(poolDefault, lpRequestDefault);
|
||||||
uint256[] memory depositsCustom = poolCustom.mintAmounts(lpRequestCustom);
|
uint256[] memory depositsCustom = viewer.mintAmounts(poolCustom, lpRequestCustom);
|
||||||
|
|
||||||
// Deposits should be identical (same proportion of identical balances)
|
// Deposits should be identical (same proportion of identical balances)
|
||||||
assertEq(depositsDefault[0], depositsCustom[0], "Token0 deposits should be identical");
|
assertEq(depositsDefault[0], depositsCustom[0], "Token0 deposits should be identical");
|
||||||
|
|||||||
Reference in New Issue
Block a user