marginal price views

This commit is contained in:
tim
2025-09-20 16:18:41 -04:00
parent 10d432070d
commit cd663105f4
3 changed files with 97 additions and 0 deletions

View File

@@ -224,6 +224,22 @@ interface IPartyPool is IERC20Metadata {
uint256 deadline
) 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)

View File

@@ -778,6 +778,51 @@ library LMSRStabilized {
return e_i_acc.div(Z);
}
/// @notice Marginal price of `base` in terms of `quote` (p_quote / p_base) as Q64.64
/// @dev Returns exp((q_quote - q_base) / b). Indices must be valid and b > 0.
function price(State storage s, uint256 baseTokenIndex, uint256 quoteTokenIndex) internal view returns (int128) {
require(baseTokenIndex < s.nAssets && quoteTokenIndex < s.nAssets, "LMSR: idx");
int128 b = _computeB(s);
require(b > int128(0), "LMSR: b<=0");
// Use reciprocal of b to avoid repeated divisions
int128 invB = ABDKMath64x64.div(ONE, b);
// Marginal price p_quote / p_base = exp((q_quote - q_base) / b)
return _exp(s.qInternal[quoteTokenIndex].sub(s.qInternal[baseTokenIndex]).mul(invB));
}
/// @notice Price of one unit of the LP size-metric (S = sum q_i) denominated in `quote` asset (Q64.64)
/// @dev Computes: poolPrice_quote = (1 / S) * sum_j q_j * exp((q_j - q_quote) / b)
function poolPrice(State storage s, uint256 quoteTokenIndex) internal view returns (int128) {
require(quoteTokenIndex < s.nAssets, "LMSR: idx");
// Compute b and ensure positivity
int128 b = _computeB(s);
require(b > int128(0), "LMSR: b<=0");
// Compute total size metric S = sum q_i
int128 S = _computeSizeMetric(s.qInternal);
require(S > int128(0), "LMSR: size zero");
// Precompute reciprocal of b
int128 invB = ABDKMath64x64.div(ONE, b);
// Accumulate weighted exponentials: sum_j q_j * exp((q_j - q_quote) / b)
int128 acc = int128(0);
uint256 n = s.nAssets;
for (uint256 j = 0; j < n; ) {
// factor = exp((q_j - q_quote) / b)
int128 factor = _exp(s.qInternal[j].sub(s.qInternal[quoteTokenIndex]).mul(invB));
// term = q_j * factor
int128 term = s.qInternal[j].mul(factor);
acc = acc.add(term);
unchecked { j++; }
}
// pool price in units of quote = (1 / S) * acc
return acc.div(S);
}
/* --------------------
Slippage -> b computation & resize-triggered rescale
-------------------- */

View File

@@ -927,6 +927,42 @@ contract PartyPool is IPartyPool, ERC20, ReentrancyGuard {
return floorValue;
}
/// @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);
}
/// @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_) private pure returns (int128) {