LMSRStabilized pure refactor; swapMintAmounts
This commit is contained in:
@@ -64,8 +64,15 @@ library LMSRStabilized {
|
||||
|
||||
/// @notice Cost C(q) = b * (M + ln(Z))
|
||||
function cost(State storage s) internal view returns (int128) {
|
||||
int128 b = _computeB(s);
|
||||
(int128 M, int128 Z) = _computeMAndZ(b, s.qInternal);
|
||||
return cost(s.kappa, s.qInternal);
|
||||
}
|
||||
|
||||
/// @notice Pure version: Cost C(q) = b * (M + ln(Z))
|
||||
function cost(int128 kappa, int128[] memory qInternal) internal pure returns (int128) {
|
||||
int128 sizeMetric = _computeSizeMetric(qInternal);
|
||||
require(sizeMetric > int128(0), "LMSR: size metric zero");
|
||||
int128 b = kappa.mul(sizeMetric);
|
||||
(int128 M, int128 Z) = _computeMAndZ(b, qInternal);
|
||||
int128 lnZ = _ln(Z);
|
||||
int128 inner = M.add(lnZ);
|
||||
int128 c = b.mul(inner);
|
||||
@@ -102,23 +109,58 @@ library LMSRStabilized {
|
||||
int128 a,
|
||||
int128 limitPrice
|
||||
) internal view returns (int128 amountIn, int128 amountOut) {
|
||||
require(i < s.nAssets && j < s.nAssets, "LMSR: idx");
|
||||
return swapAmountsForExactInput(s.nAssets, s.kappa, s.qInternal, i, j, a, limitPrice);
|
||||
}
|
||||
|
||||
/// @notice Pure version: Closed-form asset-i -> asset-j amountOut in 64.64 fixed-point format (fee-free kernel)
|
||||
/// Uses the closed-form two-asset LMSR formula (no fees in kernel):
|
||||
/// y = b * ln(1 + r0 * (1 - exp(-a / b)))
|
||||
/// where r0 = e_i / e_j.
|
||||
///
|
||||
/// This variant accepts an additional `limitPrice` (64.64) which represents the
|
||||
/// maximum acceptable marginal price (p_i / p_j). If the marginal price would
|
||||
/// exceed `limitPrice` before the requested `a` is fully consumed, the input
|
||||
/// `a` is truncated to the value that makes the marginal price equal `limitPrice`.
|
||||
///
|
||||
/// NOTE: Kernel is fee-free; fees should be handled by the wrapper/token layer.
|
||||
///
|
||||
/// @param nAssets Number of assets in the pool
|
||||
/// @param kappa Liquidity parameter κ (64.64 fixed point)
|
||||
/// @param qInternal Cached internal balances in 64.64 fixed-point format
|
||||
/// @param i Index of input asset
|
||||
/// @param j Index of output asset
|
||||
/// @param a Amount of input asset (in int128 format, 64.64 fixed-point)
|
||||
/// @param limitPrice Maximum acceptable price ratio (64.64). If <= current price, this call reverts.
|
||||
/// @return amountIn Actual amount of input asset used (may be less than `a` if limited by price)
|
||||
/// @return amountOut Amount of output asset j in 64.64 fixed-point format
|
||||
function swapAmountsForExactInput(
|
||||
uint256 nAssets,
|
||||
int128 kappa,
|
||||
int128[] memory qInternal,
|
||||
uint256 i,
|
||||
uint256 j,
|
||||
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;
|
||||
|
||||
// Compute b and ensure positivity before deriving invB
|
||||
int128 b = _computeB(s);
|
||||
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(s.qInternal[j] > int128(0), "LMSR: e_j==0");
|
||||
require(qInternal[j] > int128(0), "LMSR: e_j==0");
|
||||
|
||||
// Compute r0 = exp((q_i - q_j) / b) directly using invB
|
||||
int128 r0 = _exp(s.qInternal[i].sub(s.qInternal[j]).mul(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
|
||||
@@ -203,7 +245,7 @@ library LMSRStabilized {
|
||||
// If inner <= 0 then cap output to the current balance q_j (cannot withdraw more than q_j)
|
||||
if (inner <= int128(0)) {
|
||||
console2.log("WARNING: inner <= 0, capping output to balance q_j");
|
||||
int128 qj64 = s.qInternal[j];
|
||||
int128 qj64 = qInternal[j];
|
||||
console2.log("Capped output (64.64):");
|
||||
console2.logInt(qj64);
|
||||
return (amountIn, qj64);
|
||||
@@ -249,21 +291,48 @@ library LMSRStabilized {
|
||||
uint256 j,
|
||||
int128 limitPrice
|
||||
) internal view returns (int128 amountIn, int128 amountOut) {
|
||||
require(i < s.nAssets && j < s.nAssets, "LMSR: idx");
|
||||
return swapAmountsForPriceLimit(s.nAssets, s.kappa, s.qInternal, i, j, limitPrice);
|
||||
}
|
||||
|
||||
/// @notice Pure version: Maximum input/output pair possible when swapping from asset i to asset j
|
||||
/// given a maximum acceptable price ratio (p_i/p_j).
|
||||
/// Returns the input amount that would drive the marginal price to the limit (amountIn)
|
||||
/// and the corresponding output amount (amountOut). If the output would exceed the
|
||||
/// j-balance, amountOut is capped and amountIn is solved for the capped output.
|
||||
///
|
||||
/// @param nAssets Number of assets in the pool
|
||||
/// @param kappa Liquidity parameter κ (64.64 fixed point)
|
||||
/// @param qInternal Cached internal balances in 64.64 fixed-point format
|
||||
/// @param i Index of input asset
|
||||
/// @param j Index of output asset
|
||||
/// @param limitPrice Maximum acceptable price ratio (64.64)
|
||||
/// @return amountIn Maximum input amount in 64.64 fixed-point format that reaches the price limit
|
||||
/// @return amountOut Corresponding maximum output amount in 64.64 fixed-point format
|
||||
function swapAmountsForPriceLimit(
|
||||
uint256 nAssets,
|
||||
int128 kappa,
|
||||
int128[] memory qInternal,
|
||||
uint256 i,
|
||||
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 b = _computeB(s);
|
||||
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(s.qInternal[j] > int128(0), "LMSR: e_j==0");
|
||||
require(qInternal[j] > int128(0), "LMSR: e_j==0");
|
||||
|
||||
// Compute r0 = exp((q_i - q_j) / b) directly using invB
|
||||
int128 r0 = _exp(s.qInternal[i].sub(s.qInternal[j]).mul(invB));
|
||||
int128 r0 = _exp(qInternal[i].sub(qInternal[j]).mul(invB));
|
||||
|
||||
console2.log("\n=== Max Input/Output Calculation ===");
|
||||
console2.log("Limit price (64x64):");
|
||||
@@ -316,7 +385,7 @@ library LMSRStabilized {
|
||||
console2.logInt(maxOutput);
|
||||
|
||||
// Current balance of asset j (in 64.64)
|
||||
int128 qj64 = s.qInternal[j];
|
||||
int128 qj64 = qInternal[j];
|
||||
console2.log("Current j balance (64.64):");
|
||||
console2.logInt(qj64);
|
||||
|
||||
@@ -366,20 +435,45 @@ library LMSRStabilized {
|
||||
uint256 i,
|
||||
int128 a
|
||||
) internal view returns (int128 amountIn, int128 amountOut) {
|
||||
require(i < s.nAssets, "LMSR: idx");
|
||||
return swapAmountsForMint(s.nAssets, s.kappa, s.qInternal, i, a);
|
||||
}
|
||||
|
||||
/// @notice Pure version: Compute LP-size increase when minting from a single-token input using bisection only.
|
||||
/// @dev Solve for α >= 0 such that:
|
||||
/// a = α*q_i + sum_{j != i} x_j(α)
|
||||
/// where x_j(α) is the input to swap i->j that yields y_j = α*q_j and
|
||||
/// x_j = b * ln( r0_j / (r0_j + 1 - exp(y_j / b)) ), r0_j = exp((q_i - q_j)/b).
|
||||
/// Bisection is used (no Newton) to keep implementation compact and gas-friendly.
|
||||
/// @param nAssets Number of assets in the pool
|
||||
/// @param kappa Liquidity parameter κ (64.64 fixed point)
|
||||
/// @param qInternal Cached internal balances in 64.64 fixed-point format
|
||||
/// @param i Index of input asset
|
||||
/// @param a Amount of input asset (in int128 format, 64.64 fixed-point)
|
||||
/// @return amountIn Actual amount of input consumed
|
||||
/// @return amountOut LP size-metric increase (alpha * S)
|
||||
function swapAmountsForMint(
|
||||
uint256 nAssets,
|
||||
int128 kappa,
|
||||
int128[] memory qInternal,
|
||||
uint256 i,
|
||||
int128 a
|
||||
) internal pure returns (int128 amountIn, int128 amountOut) {
|
||||
require(i < nAssets, "LMSR: idx");
|
||||
require(a > int128(0), "LMSR: amount <= 0");
|
||||
|
||||
int128 b = _computeB(s);
|
||||
int128 sizeMetric = _computeSizeMetric(qInternal);
|
||||
require(sizeMetric > int128(0), "LMSR: size metric zero");
|
||||
int128 b = kappa.mul(sizeMetric);
|
||||
require(b > int128(0), "LMSR: b<=0");
|
||||
int128 invB = ABDKMath64x64.div(ONE, b);
|
||||
int128 S = _computeSizeMetric(s.qInternal);
|
||||
int128 S = sizeMetric;
|
||||
|
||||
uint256 n = s.nAssets;
|
||||
uint256 n = nAssets;
|
||||
|
||||
// Precompute r0_j = exp((q_i - q_j) / b) for all j to avoid recomputing during search.
|
||||
int128[] memory r0 = new int128[](n);
|
||||
for (uint256 j = 0; j < n; ) {
|
||||
r0[j] = _exp(s.qInternal[i].sub(s.qInternal[j]).mul(invB));
|
||||
r0[j] = _exp(qInternal[i].sub(qInternal[j]).mul(invB));
|
||||
unchecked { j++; }
|
||||
}
|
||||
|
||||
@@ -415,7 +509,7 @@ library LMSRStabilized {
|
||||
// loop j != i
|
||||
for (uint256 j = 0; j < n; ) {
|
||||
if (j != i) {
|
||||
int128 yj = alpha.mul(s.qInternal[j]); // target output y_j = alpha * q_j
|
||||
int128 yj = alpha.mul(qInternal[j]); // target output y_j = alpha * q_j
|
||||
if (yj > int128(0)) {
|
||||
int128 expArg = yj.mul(invB);
|
||||
// Guard exp arg
|
||||
@@ -433,7 +527,7 @@ library LMSRStabilized {
|
||||
unchecked { j++; }
|
||||
}
|
||||
|
||||
int128 aReq = fail ? int128(type(int128).max) : alpha.mul(s.qInternal[i]).add(sumX);
|
||||
int128 aReq = fail ? int128(type(int128).max) : alpha.mul(qInternal[i]).add(sumX);
|
||||
|
||||
if (aReq >= a || high >= alphaCap) {
|
||||
break;
|
||||
@@ -455,7 +549,7 @@ library LMSRStabilized {
|
||||
|
||||
for (uint256 j = 0; j < n; ) {
|
||||
if (j != i) {
|
||||
int128 yj = alpha.mul(s.qInternal[j]);
|
||||
int128 yj = alpha.mul(qInternal[j]);
|
||||
if (yj > int128(0)) {
|
||||
int128 expArg = yj.mul(invB);
|
||||
if (expArg > EXP_LIMIT) { fail = true; break; }
|
||||
@@ -472,7 +566,7 @@ library LMSRStabilized {
|
||||
unchecked { j++; }
|
||||
}
|
||||
|
||||
int128 aReq = fail ? int128(type(int128).max) : alpha.mul(s.qInternal[i]).add(sumX);
|
||||
int128 aReq = fail ? int128(type(int128).max) : alpha.mul(qInternal[i]).add(sumX);
|
||||
|
||||
if (aReq > a) {
|
||||
// mid requires more input than provided -> decrease alpha
|
||||
@@ -502,7 +596,7 @@ library LMSRStabilized {
|
||||
bool failFinal = false;
|
||||
for (uint256 j = 0; j < n; ) {
|
||||
if (j != i) {
|
||||
int128 yj = alphaFinal.mul(s.qInternal[j]);
|
||||
int128 yj = alphaFinal.mul(qInternal[j]);
|
||||
if (yj > int128(0)) {
|
||||
int128 expArg = yj.mul(invB);
|
||||
if (expArg > EXP_LIMIT) { failFinal = true; break; }
|
||||
@@ -524,7 +618,7 @@ library LMSRStabilized {
|
||||
return (int128(0), int128(0));
|
||||
}
|
||||
|
||||
int128 aRequired = alphaFinal.mul(s.qInternal[i]).add(sumXFinal);
|
||||
int128 aRequired = alphaFinal.mul(qInternal[i]).add(sumXFinal);
|
||||
|
||||
// amountIn is actual consumed input (may be <= provided a)
|
||||
amountIn = aRequired;
|
||||
@@ -553,28 +647,56 @@ library LMSRStabilized {
|
||||
uint256 i,
|
||||
int128 alpha
|
||||
) internal view returns (int128 amountOut, int128 amountIn) {
|
||||
require(i < s.nAssets, "LMSR: idx");
|
||||
return swapAmountsForBurn(s.nAssets, s.kappa, s.qInternal, i, alpha);
|
||||
}
|
||||
|
||||
/// @notice Pure version: Compute single-asset payout when burning a proportional share alpha of the pool.
|
||||
/// @dev Simulate q_after = (1 - alpha) * q, return the amount of asset `i` the burner
|
||||
/// would receive after swapping each other asset's withdrawn portion into `i`.
|
||||
/// For each j != i:
|
||||
/// - wrapper holds a_j = alpha * q_j
|
||||
/// - swap j->i with closed-form exact-input formula using the current q_local
|
||||
/// - cap output to q_local[i] when necessary (solve inverse for input used)
|
||||
/// Treat any per-asset rhs<=0 as "this asset contributes zero" (do not revert).
|
||||
/// Revert only if the final single-asset payout is zero.
|
||||
/// @param nAssets Number of assets in the pool
|
||||
/// @param kappa Liquidity parameter κ (64.64 fixed point)
|
||||
/// @param qInternal Cached internal balances in 64.64 fixed-point format
|
||||
/// @param i Index of output asset
|
||||
/// @param alpha Proportional share to burn (0 < alpha <= 1)
|
||||
/// @return amountOut Amount of asset i received (in 64.64 fixed-point)
|
||||
/// @return amountIn LP size-metric redeemed (alpha * S)
|
||||
function swapAmountsForBurn(
|
||||
uint256 nAssets,
|
||||
int128 kappa,
|
||||
int128[] memory qInternal,
|
||||
uint256 i,
|
||||
int128 alpha
|
||||
) internal pure returns (int128 amountOut, int128 amountIn) {
|
||||
require(i < nAssets, "LMSR: idx");
|
||||
require(alpha > int128(0) && alpha <= ONE, "LMSR: alpha");
|
||||
|
||||
int128 b = _computeB(s);
|
||||
int128 sizeMetric = _computeSizeMetric(qInternal);
|
||||
require(sizeMetric > int128(0), "LMSR: size metric zero");
|
||||
int128 b = kappa.mul(sizeMetric);
|
||||
require(b > int128(0), "LMSR: b<=0");
|
||||
int128 invB = ABDKMath64x64.div(ONE, b);
|
||||
|
||||
uint256 n = s.nAssets;
|
||||
uint256 n = nAssets;
|
||||
|
||||
// Size metric and burned size (amountIn returned)
|
||||
int128 S = _computeSizeMetric(s.qInternal);
|
||||
int128 S = sizeMetric;
|
||||
amountIn = alpha.mul(S); // total size-metric redeemed
|
||||
|
||||
// Build q_local := q_after_burn = (1 - alpha) * q
|
||||
int128[] memory qLocal = new int128[](n);
|
||||
for (uint256 j = 0; j < n; ) {
|
||||
qLocal[j] = s.qInternal[j].mul(ONE.sub(alpha));
|
||||
qLocal[j] = qInternal[j].mul(ONE.sub(alpha));
|
||||
unchecked { j++; }
|
||||
}
|
||||
|
||||
// Start totalOut with direct portion of asset i redeemed
|
||||
int128 totalOut = alpha.mul(s.qInternal[i]);
|
||||
int128 totalOut = alpha.mul(qInternal[i]);
|
||||
|
||||
// Track whether any non-zero contribution was produced
|
||||
bool anyNonZero = (totalOut > int128(0));
|
||||
@@ -582,7 +704,7 @@ library LMSRStabilized {
|
||||
// For each asset j != i, swap the withdrawn a_j := alpha * q_j into i
|
||||
for (uint256 j = 0; j < n; ) {
|
||||
if (j != i) {
|
||||
int128 aj = alpha.mul(s.qInternal[j]); // wrapper-held withdrawn amount of j
|
||||
int128 aj = alpha.mul(qInternal[j]); // wrapper-held withdrawn amount of j
|
||||
if (aj > int128(0)) {
|
||||
// expArg = aj / b
|
||||
int128 expArg = aj.mul(invB);
|
||||
@@ -722,15 +844,26 @@ library LMSRStabilized {
|
||||
|
||||
/// @notice Price-share of asset i: exp(z_i) / Z (64.64)
|
||||
function priceShare(State storage s, uint256 i) internal view returns (int128) {
|
||||
int128 b = _computeB(s);
|
||||
uint len = s.qInternal.length;
|
||||
return priceShare(s.kappa, s.qInternal, i);
|
||||
}
|
||||
|
||||
/// @notice Pure version: Price-share of asset i: exp(z_i) / Z (64.64)
|
||||
/// @param kappa Liquidity parameter κ (64.64 fixed point)
|
||||
/// @param qInternal Cached internal balances in 64.64 fixed-point format
|
||||
/// @param i Index of asset
|
||||
/// @return Price share in 64.64 fixed-point format
|
||||
function priceShare(int128 kappa, int128[] memory qInternal, uint256 i) internal pure returns (int128) {
|
||||
int128 sizeMetric = _computeSizeMetric(qInternal);
|
||||
require(sizeMetric > int128(0), "LMSR: size metric zero");
|
||||
int128 b = kappa.mul(sizeMetric);
|
||||
uint len = qInternal.length;
|
||||
require(len > 0, "LMSR: no assets");
|
||||
|
||||
// Precompute reciprocal of b and perform a single pass that tracks M, Z, and e_i
|
||||
int128 invB = ABDKMath64x64.div(ONE, b);
|
||||
|
||||
// Initialize from the first element
|
||||
int128 M = s.qInternal[0].mul(invB);
|
||||
int128 M = qInternal[0].mul(invB);
|
||||
int128 Z = ONE; // exp(0)
|
||||
int128 e_i_acc;
|
||||
bool setEi;
|
||||
@@ -741,7 +874,7 @@ library LMSRStabilized {
|
||||
}
|
||||
|
||||
for (uint idx = 1; idx < len; ) {
|
||||
int128 yi = s.qInternal[idx].mul(invB);
|
||||
int128 yi = qInternal[idx].mul(invB);
|
||||
if (yi <= M) {
|
||||
// Add contribution under current center
|
||||
int128 term = _exp(yi.sub(M));
|
||||
@@ -770,7 +903,7 @@ library LMSRStabilized {
|
||||
if (!setEi) {
|
||||
// Only possible when len == 1 and i != 0, guarded by caller invariants typically
|
||||
// Fallback: compute directly (kept for completeness)
|
||||
int128 yi = s.qInternal[i].mul(invB);
|
||||
int128 yi = qInternal[i].mul(invB);
|
||||
e_i_acc = _exp(yi.sub(M));
|
||||
}
|
||||
|
||||
@@ -780,27 +913,54 @@ library LMSRStabilized {
|
||||
/// @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);
|
||||
return price(s.nAssets, s.kappa, s.qInternal, baseTokenIndex, quoteTokenIndex);
|
||||
}
|
||||
|
||||
/// @notice Pure version: 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.
|
||||
/// @param nAssets Number of assets in the pool
|
||||
/// @param kappa Liquidity parameter κ (64.64 fixed point)
|
||||
/// @param qInternal Cached internal balances in 64.64 fixed-point format
|
||||
/// @param baseTokenIndex Index of base token
|
||||
/// @param quoteTokenIndex Index of quote token
|
||||
/// @return Price in 64.64 fixed-point format
|
||||
function price(uint256 nAssets, int128 kappa, int128[] memory qInternal, uint256 baseTokenIndex, uint256 quoteTokenIndex) internal pure returns (int128) {
|
||||
require(baseTokenIndex < nAssets && quoteTokenIndex < nAssets, "LMSR: idx");
|
||||
int128 sizeMetric = _computeSizeMetric(qInternal);
|
||||
require(sizeMetric > int128(0), "LMSR: size metric zero");
|
||||
int128 b = kappa.mul(sizeMetric);
|
||||
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));
|
||||
return _exp(qInternal[quoteTokenIndex].sub(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");
|
||||
return poolPrice(s.nAssets, s.kappa, s.qInternal, quoteTokenIndex);
|
||||
}
|
||||
|
||||
/// @notice Pure version: 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)
|
||||
/// @param nAssets Number of assets in the pool
|
||||
/// @param kappa Liquidity parameter κ (64.64 fixed point)
|
||||
/// @param qInternal Cached internal balances in 64.64 fixed-point format
|
||||
/// @param quoteTokenIndex Index of quote token
|
||||
/// @return Pool price in 64.64 fixed-point format
|
||||
function poolPrice(uint256 nAssets, int128 kappa, int128[] memory qInternal, uint256 quoteTokenIndex) internal pure returns (int128) {
|
||||
require(quoteTokenIndex < nAssets, "LMSR: idx");
|
||||
// Compute b and ensure positivity
|
||||
int128 b = _computeB(s);
|
||||
int128 sizeMetric = _computeSizeMetric(qInternal);
|
||||
require(sizeMetric > int128(0), "LMSR: size metric zero");
|
||||
int128 b = kappa.mul(sizeMetric);
|
||||
require(b > int128(0), "LMSR: b<=0");
|
||||
|
||||
// Compute total size metric S = sum q_i
|
||||
int128 S = _computeSizeMetric(s.qInternal);
|
||||
int128 S = sizeMetric;
|
||||
require(S > int128(0), "LMSR: size zero");
|
||||
|
||||
// Precompute reciprocal of b
|
||||
@@ -808,12 +968,12 @@ library LMSRStabilized {
|
||||
|
||||
// Accumulate weighted exponentials: sum_j q_j * exp((q_j - q_quote) / b)
|
||||
int128 acc = int128(0);
|
||||
uint256 n = s.nAssets;
|
||||
uint256 n = 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));
|
||||
int128 factor = _exp(qInternal[j].sub(qInternal[quoteTokenIndex]).mul(invB));
|
||||
// term = q_j * factor
|
||||
int128 term = s.qInternal[j].mul(factor);
|
||||
int128 term = qInternal[j].mul(factor);
|
||||
acc = acc.add(term);
|
||||
unchecked { j++; }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user