hm-aftermath-ts-sdk
Version:
Aftermath TypeScript SDK
1,137 lines (1,136 loc) • 62.3 kB
JavaScript
"use strict";
var _a;
Object.defineProperty(exports, "__esModule", { value: true });
exports.CmmmCalculations = void 0;
const utils_1 = require("../../../general/utils");
const fixedUtils_1 = require("../../../general/utils/fixedUtils");
// This file is the typescript version of on-chain calculations. See the .move file for license info.
// These calculations are useful for estimating values on-chain but the JS number format is LESS PRECISE!
// Do not expect these values to be identical to their on-chain counterparts.
// The formula used here differs from that of Curve/Balancer. Our stables allow custom price pegs as opposed to
// the constant 1:1 equal-weight peg. Also our pools do not have an upper coin type limit (practically).
// Here is our construction:
// Start with a pool with balances b1,...,bn > 0. Call the tuple B = (b1,...,bn).
// Take weights w1,...,wn with 0 < wi < 1 and w1 + ... + wn = 1.
// Let X stand for the tuple (x1,...,xn) in Rn.
// For normalization we need the tuple T = (h,h,...,h) for some h > 0, solved for later.
// The invariant is defined as the value of this h.
// -- TODO: generalize this reference point T to lie on a chosen ray like (w1*h, w2*h, ..., wn*h).
// -- This would allow setting the swap price to be centered at a chosen balance distribution instead
// -- of the 1:1:...:1 diagonal balance distribution currently in use.
// Define the sum function S: Rn -> R as S(X) = w1*x1 + ... + wn*xn.
// Define the product function P: Rn -> R as P(X) = x1^w1 * ... * xn^wn.
// Note P(T) = S(T) = h.
// We want the sum to vanish on the coordinate hyperplanes too so instead use L where
// L(X) = [2P(X) / (P(X) + P(T))] * S(X)
// Then 0 <= L(X) < 2S(X) and L(T) = h.
// The constant price surface is defined by the equation L(X) = L(B) and the product curve by
// P(X) = P(B). Equivilantly by L(X) - L(B) = 0, P(X) - P(B) = 0.
// Take a flatness parameter A, 0 <= A <= 1. Then (1-A) is the dual parameter:
// 0 <= (1-A) <= 1 and A + (1-A) = 1. Take the linear combination of the defining functions
// C(X) = A * L(X) + (1-A) * P(X). The stable curve is defined as the solution to C(X) = C(B).
// Moreover we can solve for T from the equation C(T) = C(B), making all the following equal:
// C(B) = L(T) = S(T) = P(T) = h.
// The defining equation C(X) = C(B) can be rewritten in a computationally simpler form as
// P(X) * (2A * S(X) + (1-A) * P(X)) = h * (A * P(X) + h).
// To see these functions/equations in action check out https://www.desmos.com/calculator/eu5mfckuk9
class CmmmCalculations {
}
exports.CmmmCalculations = CmmmCalculations;
_a = CmmmCalculations;
CmmmCalculations.minWeight = 0.01;
// Having a minimum normalized weight imposes a limit on the maximum number of tokens;
// i.e., the largest possible pool is one where all tokens have exactly the minimum weight.
CmmmCalculations.maxWeightedTokens = 100;
// Pool limits that arise from limitations in the fixed point power function (and the imposed 1:100 maximum weight
// ratio).
// Swap limits: amounts swapped may not be larger than this percentage of total balance.
CmmmCalculations.maxInRatio = 0.3;
CmmmCalculations.maxOutRatio = 0.3;
// Invariant shrink limit: non-proportional exits cannot cause the invariant to decrease by less than this ratio.
CmmmCalculations.minInvariantRatio = 0.7;
// Invariant growth limit: non-proportional joins cannot cause the invariant to increase by more than this ratio.
CmmmCalculations.maxInvariantRatio = 3;
CmmmCalculations.maxNewtonAttempts = 255;
CmmmCalculations.convergenceBound = 1e-9;
CmmmCalculations.tolerance = 1e-13;
CmmmCalculations.validityTolerance = 0.000001;
// Invariant is used to govern pool behavior. Swaps are operations which change the pool balances without changing
// the invariant (ignoring fees) and investments change the invariant without changing the distribution of balances.
// Invariant and pool lp are almost in 1:1 correspondence -- e.g. burning lp in a withdraw proportionally lowers the pool invariant.
// The difference is as swap fees are absorbed they increase the invariant without incrasing total lp, increasing lp worth.
// Every pool operation either explicitly or implicity calls this function.
CmmmCalculations.calcInvariant = (pool) => {
let flatness = fixedUtils_1.FixedUtils.directCast(pool.flatness);
// The value for h which we want is the one for which the balances vector B lies on the curve through T.
// That is, C(T) = C(B). This turns out to be a quadratic equation which can be solved with
// h = [sqrt[P(B) * (P(B) * (A*A + 4*(1-A)) + 8*A*S(B))] - A*P(B)] / 2.
let sum = 0;
let prod = 0;
let balance;
let weight;
for (let coin of Object.values(pool.coins)) {
balance = fixedUtils_1.FixedUtils.directCast(coin.normalizedBalance);
weight = fixedUtils_1.FixedUtils.directCast(coin.weight);
sum += weight * balance;
prod += weight * Math.log(balance);
}
prod = Math.exp(prod);
return _a.calcInvariantQuadratic(prod, sum, flatness);
};
// The invariant for stables comes from a quadratic equation coming from the reference point T = (h,h,...,h).
// h = [sqrt[p * (p * (A*A + 4*(1-A)) + 8*A*s)] - A*p] / 2.
CmmmCalculations.calcInvariantQuadratic = (prod, sum, flatness) => (Math.sqrt(prod *
(prod * (flatness * flatness + (1 - flatness) * 4) +
flatness * sum * 8)) -
flatness * prod) /
2;
// This function is used for 1d optimization. It computes the full invariant components and their
// portions which omit contribution from the balance in the `index` coordinate.
// It returns (prod, sum, p0, s0, h) where:
// prod = b1^w1 * ... * bn^wn
// sum = w1*b1 + ... + wn*bn
// p0 = b1^w1 * ... * [bi^w1] * ... * bn^wn (remove bi from prod)
// s0 = w1*b1 + ... + [wi*bi] + ... + wn*bn (remove bi from sum)
// h is the invariant
CmmmCalculations.calcInvariantComponents = (pool, index) => {
let flatness = fixedUtils_1.FixedUtils.directCast(pool.flatness);
let prod = 0;
let sum = 0;
let p0 = 0;
let s0 = 0;
let balance;
let weight;
let p;
let s;
for (let [coinType, coin] of Object.entries(pool.coins)) {
balance = fixedUtils_1.FixedUtils.directCast(coin.normalizedBalance);
weight = fixedUtils_1.FixedUtils.directCast(coin.weight);
p = weight * Math.log(balance);
s = weight * balance;
prod = prod + p;
sum = sum + s;
if (coinType != index) {
p0 = p0 + p;
s0 = s0 + s;
}
}
prod = Math.exp(prod);
p0 = Math.exp(p0);
return [
prod,
sum,
p0,
s0,
CmmmCalculations.calcInvariantQuadratic(prod, sum, flatness),
];
};
// spot price is given in units of Bin / Bout
CmmmCalculations.calcSpotPrice = (pool, coinTypeIn, coinTypeOut) => CmmmCalculations.calcSpotPriceWithFees(pool, coinTypeIn, coinTypeOut, true);
// spot price is given in units of Bin / Bout
CmmmCalculations.calcSpotPriceWithFees = (pool, coinTypeIn, coinTypeOut, ignoreFees) => {
var _b, _c;
let a = fixedUtils_1.FixedUtils.directCast(pool.flatness);
let part1 = CmmmCalculations.calcSpotPriceBody(pool);
let coinIn = pool.coins[coinTypeIn];
let coinOut = pool.coins[coinTypeOut];
let balanceIn = fixedUtils_1.FixedUtils.directCast(coinIn.normalizedBalance);
let balanceOut = fixedUtils_1.FixedUtils.directCast(coinOut.normalizedBalance);
let weightIn = fixedUtils_1.FixedUtils.directCast(coinIn.weight);
let weightOut = fixedUtils_1.FixedUtils.directCast(coinOut.weight);
let swapFeeIn = ignoreFees
? 0
: fixedUtils_1.FixedUtils.directCast(coinIn.tradeFeeIn);
let swapFeeOut = ignoreFees
? 0
: fixedUtils_1.FixedUtils.directCast(coinIn.tradeFeeOut);
let sbi = weightOut * balanceIn;
// this is the only place where fee values are used
let sbo = (1 -
(ignoreFees
? 0
: utils_1.Casting.bpsToPercentage((_c = (_b = pool.daoFeePoolObject) === null || _b === void 0 ? void 0 : _b.feeBps) !== null && _c !== void 0 ? _c : BigInt(0)))) *
(1 - swapFeeIn) *
(1 - swapFeeOut) *
weightIn *
balanceOut;
return ((sbi * (part1 + 2 * a * balanceOut)) /
(sbo * (part1 + 2 * a * balanceIn)));
};
// The spot price formula contains a factor of C0^2 / P(B0) + (1-A)P(B0), this returns that
CmmmCalculations.calcSpotPriceBody = (pool) => {
// The spot price formula comes from the partial derivatives of Cf, specifically -(dCf / dxOut) / (dCf / dxIn)
let a = fixedUtils_1.FixedUtils.directCast(pool.flatness);
let ac = 1 - a;
let prod = 0;
let sum = 0;
let balance;
let weight;
// The spot price formula requires knowing the value of the invariant. We need the prod and sum parts
// also later on so no need to compute them twice by calling calcInvariant, just evaluate here.
for (let coin of Object.values(pool.coins)) {
balance = fixedUtils_1.FixedUtils.directCast(coin.normalizedBalance);
weight = fixedUtils_1.FixedUtils.directCast(coin.weight);
prod += weight * Math.log(balance);
sum += weight * balance;
}
prod = Math.exp(prod);
let invarnt = CmmmCalculations.calcInvariantQuadratic(prod, sum, a);
return (invarnt * invarnt) / prod + ac * prod;
};
// 1d optimized swap function for finding out given in. Returns the amount out.
CmmmCalculations.calcOutGivenIn = (pool, coinTypeIn, coinTypeOut, amountIn) => {
if (coinTypeIn === coinTypeOut)
throw Error("in and out must be different coins");
let coinIn = pool.coins[coinTypeIn];
let coinOut = pool.coins[coinTypeOut];
let swapFeeIn = fixedUtils_1.FixedUtils.directCast(coinIn.tradeFeeIn);
let swapFeeOut = fixedUtils_1.FixedUtils.directCast(coinOut.tradeFeeOut);
if (swapFeeIn >= 1 || swapFeeOut >= 1) {
// this swap is disabled
return BigInt(0);
}
let flatness = fixedUtils_1.FixedUtils.directCast(pool.flatness);
let oldIn = fixedUtils_1.FixedUtils.directCast(coinIn.normalizedBalance);
let oldOut = fixedUtils_1.FixedUtils.directCast(coinOut.normalizedBalance);
let wIn = fixedUtils_1.FixedUtils.directCast(coinIn.weight);
let wOut = fixedUtils_1.FixedUtils.directCast(coinOut.weight);
let [prod, , p0, s0, h] = CmmmCalculations.calcInvariantComponents(pool, coinTypeOut);
let feedAmountIn = (1 - swapFeeIn) *
fixedUtils_1.FixedUtils.castAndNormalize(coinIn.decimalsScalar, amountIn);
let newIn = oldIn + feedAmountIn;
let prodRatio = Math.pow(newIn / oldIn, wIn);
let newP0 = p0 * prodRatio;
// the initial estimate (xi) is from if there were only the product part of the curve
let xi = Math.pow(prod / newP0, 1 / wOut);
let newS0 = s0 + wIn * feedAmountIn;
let tokenAmountOut = CmmmCalculations.getTokenBalanceGivenInvariantAndAllOtherBalances(flatness, wOut, h, xi, // initial estimate -- default can be (P(X) / p0)^n
newP0, // P(B) / xi^(1/n) (everything but the missing part)
newS0 // S(B) - xi / n (everything but the missing part)
);
let amountOut = fixedUtils_1.FixedUtils.uncastAndUnnormalize(coinOut.decimalsScalar, (oldOut - tokenAmountOut) * (1 - swapFeeOut));
if (!CmmmCalculations.checkValid1dSwap(pool, coinTypeIn, coinTypeOut, amountIn, amountOut))
throw Error("invalid 1d swap");
return amountOut;
};
// 1d optimized swap function for finding in given out. Returns the amount in.
CmmmCalculations.calcInGivenOut = (pool, coinTypeIn, coinTypeOut, amountOut) => {
if (coinTypeIn === coinTypeOut)
throw Error("in and out must be different coins");
let coinIn = pool.coins[coinTypeIn];
let coinOut = pool.coins[coinTypeOut];
let swapFeeIn = fixedUtils_1.FixedUtils.directCast(coinIn.tradeFeeIn);
let swapFeeOut = fixedUtils_1.FixedUtils.directCast(coinOut.tradeFeeOut);
if (swapFeeIn >= 1 || swapFeeOut >= 1) {
// this swap is disabled
if (amountOut === BigInt(0))
return BigInt(0);
throw Error("this swap is disabled");
}
let flatness = fixedUtils_1.FixedUtils.directCast(pool.flatness);
let oldIn = fixedUtils_1.FixedUtils.directCast(coinIn.normalizedBalance);
let oldOut = fixedUtils_1.FixedUtils.directCast(coinOut.normalizedBalance);
let wOut = fixedUtils_1.FixedUtils.directCast(coinOut.weight);
let wIn = fixedUtils_1.FixedUtils.directCast(coinIn.weight);
let [prod, , p0, s0, h] = CmmmCalculations.calcInvariantComponents(pool, coinTypeIn);
let feedAmountOut = fixedUtils_1.FixedUtils.castAndNormalize(coinIn.decimalsScalar, amountOut) /
(1 - swapFeeOut);
let newOut = oldOut - feedAmountOut;
let prodRatio = Math.pow(newOut / oldOut, wOut);
let newP0 = p0 * prodRatio;
// the initial estimate (xi) is from if there were only the product part of the curve
let xi = Math.pow(prod / newP0, 1 / wIn);
let newS0 = s0 - wOut * feedAmountOut;
let tokenAmountIn = CmmmCalculations.getTokenBalanceGivenInvariantAndAllOtherBalances(flatness, wIn, h, xi, // initial estimate -- default can be (P(X) / p0)^n
newP0, // P(B) / xi^(1/n) (everything but the missing part)
newS0 // S(B) - xi / n (everything but the missing part)
);
let amountIn = fixedUtils_1.FixedUtils.uncastAndUnnormalize(coinOut.decimalsScalar, (tokenAmountIn - oldIn) / (1 - swapFeeIn));
if (!CmmmCalculations.checkValid1dSwap(pool, coinTypeIn, coinTypeOut, amountIn, amountOut))
throw Error("invalid 1d swap");
return amountIn;
};
// For computing swap amounts. Given the current balances (and any other parameters) and an amounts in vector,
// and a expected amounts out vector, determine the value of t > 0 such that t*expected_amounts_out
// is a valid swap from balances corresponding to adding amounts_in to the pool. The correct value of t is the one for which
// calc_swap_invariant(balances, ...parameters, amounts_in, t*expected_amounts_out) === calc_invariant_full(balances, ...parameters).
CmmmCalculations.calcSwapFixedIn = (pool, amountsIn, amountsOutDirection) => {
let coins = pool.coins;
let invariant = CmmmCalculations.calcInvariant(pool);
let a = fixedUtils_1.FixedUtils.directCast(pool.flatness);
let ac = 1 - a;
let t = 1; // assume that the expected amounts out are close to the true amounts out
// this allows faster convergence if the caller chooses expected_amounts_out well
let prevT = t;
let balance;
let weight;
let amountIn;
let amountOut;
let feeIn;
let feeOut;
let prod;
let prod1;
let sum;
let sum1;
let part1;
let part2;
let part3;
let part4;
let skip;
let drainT = Number.POSITIVE_INFINITY;
let shifter = 1;
// make sure no disabled coin type is expected
for (let [coinType, coin] of Object.entries(coins)) {
amountOut = fixedUtils_1.FixedUtils.castAndNormalize(coin.decimalsScalar, amountsOutDirection[coinType] || BigInt(0));
feeOut = fixedUtils_1.FixedUtils.complement(fixedUtils_1.FixedUtils.directCast(coin.tradeFeeOut));
if (amountOut > 0) {
if (feeOut === 0) {
throw Error("this trade is disabled");
}
else {
// pool is drained when b + Ain * (1 - Sin) - t * Aout / (1 - Sout) = 0, or t = (b + Ain * (1 - Sin)) * (1 - So) / Aout
t =
((fixedUtils_1.FixedUtils.directCast(coin.normalizedBalance) +
fixedUtils_1.FixedUtils.castAndNormalize(coin.decimalsScalar, amountsIn[coinType] || BigInt(0)) *
fixedUtils_1.FixedUtils.complement(fixedUtils_1.FixedUtils.directCast(coin.tradeFeeIn))) /
amountOut) *
feeOut;
drainT = Math.min(drainT, t);
}
}
}
// drain_t is the maximum t can possibly be. It will be 0 if expected amounts out is way too high.
if (drainT === 0)
return BigInt(0);
while (shifter >= drainT)
shifter /= 2;
t = 1;
for (let i = 0; i < CmmmCalculations.maxNewtonAttempts; ++i) {
prod = 0;
prod1 = 0;
sum = 0;
sum1 = 0;
skip = false;
for (let [coinType, coin] of Object.entries(coins)) {
balance = fixedUtils_1.FixedUtils.directCast(coin.normalizedBalance);
weight = fixedUtils_1.FixedUtils.directCast(coin.weight);
amountIn = fixedUtils_1.FixedUtils.castAndNormalize(coin.decimalsScalar, amountsIn[coinType] || BigInt(0));
amountOut = fixedUtils_1.FixedUtils.castAndNormalize(coin.decimalsScalar, amountsOutDirection[coinType] || BigInt(0));
feeIn = fixedUtils_1.FixedUtils.complement(fixedUtils_1.FixedUtils.directCast(coin.tradeFeeIn));
feeOut = fixedUtils_1.FixedUtils.complement(fixedUtils_1.FixedUtils.directCast(coin.tradeFeeOut));
// pseudoin
part1 = feeIn * amountIn;
// pseudoout
part2 = (t * amountOut) / feeOut;
// pseudobalance
if (part2 >= balance + part1 + 1) {
skip = true;
break;
}
part3 = balance + part1 - part2;
// for derivatives: weight * expected_amounts_out / fee_out
part4 = (weight * amountOut) / feeOut;
prod += weight * Math.log(part3);
prod1 += part4 / part3;
sum += weight * part3;
sum1 += part4;
}
prod = Math.exp(prod);
part1 = a * sum;
part2 = ac * prod;
part3 = part1 + part2;
part4 = a * invariant * prod1;
t =
(a * (sum + 2 * t * sum1) +
part3 +
2 * prod1 * t * part3 -
(t * part4 + invariant * (a + invariant / prod))) /
(2 * (prod1 * part3 + a * sum1) - part4);
if (utils_1.Helpers.closeEnough(t, prevT, CmmmCalculations.convergenceBound)) {
if (!CmmmCalculations.checkValidSwap(pool, amountsIn, 1, amountsOutDirection, t))
throw Error("invalid swap");
return fixedUtils_1.FixedUtils.directUncast(t);
}
prevT = t;
}
throw Error("Newton diverged");
};
// Swaps but fixed amounts out. Given the pool's current state and a guaranteed out vector, and a expected in vector,
// scale expected_amounts_in by t > 0 so that this swap is valid and return the correct value for t
CmmmCalculations.calcSwapFixedOut = (pool, amountsInDirection, amountsOut) => {
let coins = pool.coins;
let invariant = CmmmCalculations.calcInvariant(pool);
let a = fixedUtils_1.FixedUtils.directCast(pool.flatness);
let ac = 1 - a;
let t = 1; // assume that the expected amounts out are close to the true amounts out
// this allows faster convergence if the caller chooses expected_amounts_out well
let prevT = 0;
let balance;
let weight;
let amountIn;
let amountOut;
let feeIn;
let feeOut;
let prod;
let prod1;
let sum;
let sum1;
let part1;
let part2;
let part3;
let part4;
// make sure no disabled coin type is expected
for (let [coinType, coin] of Object.entries(coins)) {
if (coin.tradeFeeOut >= fixedUtils_1.FixedUtils.fixedOneB &&
(amountsOut[coinType] || BigInt(0)) > BigInt(0))
throw Error("this trade is disabled");
}
for (let i = 0; i < CmmmCalculations.maxNewtonAttempts; ++i) {
prod = 0;
prod1 = 0;
sum = 0;
sum1 = 0;
for (let [coinType, coin] of Object.entries(coins)) {
balance = fixedUtils_1.FixedUtils.directCast(coin.normalizedBalance);
weight = fixedUtils_1.FixedUtils.directCast(coin.weight);
amountIn = fixedUtils_1.FixedUtils.castAndNormalize(coin.decimalsScalar, amountsInDirection[coinType] || BigInt(0));
amountOut = fixedUtils_1.FixedUtils.castAndNormalize(coin.decimalsScalar, amountsOut[coinType] || BigInt(0));
feeIn = 1 - fixedUtils_1.FixedUtils.directCast(coin.tradeFeeIn);
feeOut = 1 - fixedUtils_1.FixedUtils.directCast(coin.tradeFeeOut);
// pseudoin expected
part1 = feeIn * amountIn;
// pseudoout
part2 = amountOut === 0 ? 0 : amountOut / feeOut;
// pseudobalance
part3 = balance + t * part1 - part2;
// for derivatives: weight * fee_in * expected_amounts_in
part4 = weight * part1;
prod += weight * Math.log(part3);
prod1 += part4 / part3;
sum += weight * part3;
sum1 += part4;
}
prod = Math.exp(prod);
part1 = 2 * a * sum;
part2 = ac * prod;
part3 = part1 + part2;
part4 =
(part3 + part2) * prod1 + 2 * a * sum1 - a * invariant * prod1;
t =
(t * part4 + invariant * (a + invariant / prod) - part3) /
part4;
if (utils_1.Helpers.closeEnough(t, prevT, CmmmCalculations.convergenceBound)) {
if (!CmmmCalculations.checkValidSwap(pool, amountsInDirection, 1, amountsOut, t))
throw Error("invalid swap");
return fixedUtils_1.FixedUtils.directUncast(t);
}
prevT = t;
}
throw Error("Newton diverged");
};
// Return the expected lp ratio for this deposit
CmmmCalculations.calcDepositFixedAmounts = (pool, amountsIn) => {
let invariant = CmmmCalculations.calcInvariant(pool);
let coins = pool.coins;
let a = fixedUtils_1.FixedUtils.directCast(pool.flatness);
let ac = 1 - a;
let balance;
let weight;
let amount;
let prod = 0;
let sum = 0;
let r = CmmmCalculations.calcDepositFixedAmountsInitialEstimate(pool, amountsIn);
let prevR = r;
let fees = {};
for (let [coinType, coin] of Object.entries(coins)) {
balance = fixedUtils_1.FixedUtils.directCast(coin.normalizedBalance);
amount = fixedUtils_1.FixedUtils.castAndNormalize(coin.decimalsScalar, amountsIn[coinType] || BigInt(0));
fees[coinType] =
r * (balance + amount) >= balance
? 1 - fixedUtils_1.FixedUtils.directCast(coin.tradeFeeIn)
: 1 / (1 - fixedUtils_1.FixedUtils.directCast(coin.tradeFeeOut));
}
let i = 0;
let prod1;
let sum1;
let fee;
let part1;
let part2;
let part3;
let part4;
while (i < CmmmCalculations.maxNewtonAttempts) {
prod = 0;
prod1 = 0;
sum = 0;
sum1 = 0;
for (let [coinType, coin] of Object.entries(coins)) {
balance = fixedUtils_1.FixedUtils.directCast(coin.normalizedBalance);
weight = fixedUtils_1.FixedUtils.directCast(coin.weight);
amount = fixedUtils_1.FixedUtils.castAndNormalize(coin.decimalsScalar, amountsIn[coinType] || BigInt(0));
fee = fees[coinType];
part1 = balance + amount;
part2 = fee * r * part1 + balance - fee * balance;
part3 = weight * fee * part1;
prod += weight * Math.log(part2);
prod1 += part3 / part2;
sum += weight * part2;
sum1 += part3;
}
prod = Math.exp(prod);
part3 = a * invariant * prod1;
part4 = 2 * prod1 * (a * sum + ac * prod) + 2 * a * sum1;
r =
(r * part4 +
invariant * (1 + invariant / prod) -
(r * part3 + 2 * a * sum + ac * (prod + invariant))) /
(part4 - part3);
if (utils_1.Helpers.closeEnough(r, prevR, CmmmCalculations.convergenceBound)) {
let scalar = fixedUtils_1.FixedUtils.directUncast(r);
if (!CmmmCalculations.checkValidDeposit(pool, amountsIn, scalar))
throw Error("invalid deposit");
return scalar;
}
prevR = r;
i += 1;
}
throw Error("Newton diverged");
};
CmmmCalculations.calcDepositFixedAmountsInitialEstimate = (pool, amountsIn) => {
let invariant = CmmmCalculations.calcInvariant(pool);
let coins = pool.coins;
let a = fixedUtils_1.FixedUtils.directCast(pool.flatness);
let ac = 1 - a;
let balance;
let amount;
let weight;
let r;
let rMin = 0;
let cfMin = 0;
let prod = 0;
let sum = 0;
let part1;
// start cf_max as corresponding to r = 1
for (let [coinType, coin] of Object.entries(coins)) {
balance = fixedUtils_1.FixedUtils.directCast(coin.normalizedBalance);
amount = fixedUtils_1.FixedUtils.castAndNormalize(coin.decimalsScalar, amountsIn[coinType] || BigInt(0));
weight = fixedUtils_1.FixedUtils.directCast(coin.weight);
// this is all in so use fees in
part1 =
balance + (1 - fixedUtils_1.FixedUtils.directCast(coin.tradeFeeIn)) * amount;
prod += weight * Math.log(part1);
sum += weight * part1;
// r_min portion of the loop
r =
(fixedUtils_1.FixedUtils.directCast(coin.tradeFeeOut) * balance) /
(balance + amount);
rMin = Math.max(r, rMin);
}
prod = Math.exp(prod);
let cfMax = (2 * a * prod * sum) / (prod + invariant) + ac * prod;
let rMax = 1;
let cf;
for (let [coinType, coin] of Object.entries(coins)) {
balance = fixedUtils_1.FixedUtils.directCast(coin.normalizedBalance);
weight = fixedUtils_1.FixedUtils.directCast(coin.weight);
amount = fixedUtils_1.FixedUtils.castAndNormalize(coin.decimalsScalar, amountsIn[coinType] || BigInt(0));
r = balance / (balance + amount);
if (r <= rMin)
continue;
prod = 0;
sum = 0;
for (let [coinType2, coin2] of Object.entries(coins)) {
balance = fixedUtils_1.FixedUtils.directCast(coin2.normalizedBalance);
weight = fixedUtils_1.FixedUtils.directCast(coin2.weight);
amount = fixedUtils_1.FixedUtils.castAndNormalize(coin2.decimalsScalar, amountsIn[coinType2] || BigInt(0));
part1 = r * (balance + amount);
if (part1 >= balance) {
// r * (B0 + Din) >= B0 so use fees in
part1 =
balance +
(1 - fixedUtils_1.FixedUtils.directCast(coin2.tradeFeeIn)) *
(part1 - balance);
}
else {
// r * (B0 + Din) < B0 so use fees out
part1 =
balance -
(balance - part1) /
fixedUtils_1.FixedUtils.complement(fixedUtils_1.FixedUtils.directCast(coin.tradeFeeOut));
}
prod += weight * Math.log(part1);
sum += weight * part1;
}
prod = Math.exp(prod);
cf = (2 * a * prod * sum) / (prod + invariant) + ac * prod;
if (cf <= invariant) {
// is a lower bound, check min
if (cf >= cfMin) {
rMin = r;
cfMin = cf;
}
}
if (cf >= invariant) {
// is an upper bound, check max
if (cf <= cfMax) {
rMax = r;
cfMax = cf;
}
}
}
r =
cfMin === cfMax
? rMin
: (rMin * cfMax + (rMax - rMin) * invariant - rMax * cfMin) /
(cfMax - cfMin);
return r;
};
// Return the expected amounts out for this withdrawal
CmmmCalculations.calcWithdrawFlpAmountsOut = (pool, amountsOutDirection, lpRatio) => {
let invariant = CmmmCalculations.calcInvariant(pool);
let coins = pool.coins;
let lpr = lpRatio;
let lpc = 1 - lpr;
let scaledInvariant = invariant * lpr;
let a = fixedUtils_1.FixedUtils.directCast(pool.flatness);
let ac = 1 - a;
let i;
let prevR = 0;
let balance;
let weight;
let amountOut;
let fee;
let prod;
let prod1;
let sum;
let sum1;
let part1;
let part2;
let part3;
let part4;
let skip;
let shrinker = 1;
let [r, rDrain] = CmmmCalculations.calcWithdrawFlpAmountsOutInitialEstimate(pool, amountsOutDirection, lpRatio);
while (shrinker >= rDrain)
shrinker /= 2;
let fees = {};
for (let [coinType, coin] of Object.entries(coins)) {
balance = fixedUtils_1.FixedUtils.directCast(coin.normalizedBalance);
amountOut = fixedUtils_1.FixedUtils.castAndNormalize(coin.decimalsScalar, amountsOutDirection[coinType] || BigInt(0));
fees[coinType] =
balance * lpc >= r * amountOut
? 1 - fixedUtils_1.FixedUtils.directCast(coin.tradeFeeIn)
: 1 / (1 - fixedUtils_1.FixedUtils.directCast(coin.tradeFeeOut));
}
i = 0;
while (i < CmmmCalculations.maxNewtonAttempts) {
prod = 0;
prod1 = 0;
sum = 0;
sum1 = 0;
skip = false;
for (let [coinType, coin] of Object.entries(coins)) {
balance = fixedUtils_1.FixedUtils.directCast(coin.normalizedBalance);
weight = fixedUtils_1.FixedUtils.directCast(coin.weight);
amountOut = fixedUtils_1.FixedUtils.castAndNormalize(coin.decimalsScalar, amountsOutDirection[coinType] || BigInt(0));
fee = fees[coinType];
part1 = balance * (lpr + lpc * fee);
part2 = fee * r * amountOut;
if (part2 + 1 >= part1) {
// Overshot and drained pool. Set t to be closer to t_max and try again.
skip = true;
break;
}
else {
part1 -= part2;
}
part2 = weight * fee * amountOut;
prod += weight * Math.log(part1);
prod1 += part2 / part1;
sum += weight * part1;
sum1 += part2;
}
if (skip) {
r = rDrain - shrinker / Math.pow(2, i);
i += 1;
continue;
}
prod = Math.exp(prod);
part1 = prod / scaledInvariant;
part2 = 2 * a * sum;
part3 = ac * (prod * part1 + 2 * prod + scaledInvariant) + part2;
part4 = part3 * prod1 + 2 * a * (part1 + 1) * sum1;
r =
(r * part4 +
part3 +
part1 * part2 -
prod -
scaledInvariant * (2 + scaledInvariant / prod)) /
part4;
if (utils_1.Helpers.closeEnough(r, prevR, CmmmCalculations.convergenceBound)) {
let returner = {};
for (let coinType of Object.keys(coins)) {
returner[coinType] = fixedUtils_1.FixedUtils.directUncast(r *
fixedUtils_1.FixedUtils.directCast(amountsOutDirection[coinType] || BigInt(0)));
}
if (!CmmmCalculations.checkValidWithdraw(pool, returner, lpRatio))
throw Error("invalid withdraw");
return returner;
}
prevR = r;
i += 1;
}
throw Error("Newton diverged");
};
CmmmCalculations.calcWithdrawFlpAmountsOutInitialEstimate = (pool, amountsOutDirection, lpRatio) => {
let invariant = CmmmCalculations.calcInvariant(pool);
let coins = pool.coins;
let lpr = lpRatio;
let lpc = 1 - lpr;
let scaledInvariant = invariant * lpr;
let a = fixedUtils_1.FixedUtils.directCast(pool.flatness);
let ac = 1 - a;
let keepT;
let tDrain;
let t;
let cf;
let tMin;
let cfMin;
let tMax;
let cfMax;
let balance;
let weight;
let amountOut;
let fee;
let prod;
let sum;
let part1;
let part2;
let part3;
// the biggest cfMax can possibly be is f(0) which is this:
tMax = 0;
prod = 0;
sum = 0;
for (let coin of Object.values(coins)) {
balance = fixedUtils_1.FixedUtils.directCast(coin.normalizedBalance);
weight = fixedUtils_1.FixedUtils.directCast(coin.weight);
fee = fixedUtils_1.FixedUtils.directCast(coin.tradeFeeIn);
part1 = balance * (1 + lpr * fee - fee);
prod += weight * Math.log(part1);
sum += weight * part1;
}
prod = Math.exp(prod);
cfMax = (2 * a * prod * sum) / (prod + scaledInvariant) + ac * prod;
// the smallest cfMin can be is 0 which occurs when the pool is drained
cfMin = 0;
tMin = Number.POSITIVE_INFINITY;
for (let [coinType, coin] of Object.entries(coins)) {
amountOut = fixedUtils_1.FixedUtils.castAndNormalize(coin.decimalsScalar, amountsOutDirection[coinType] || BigInt(0));
if (amountOut === 0)
continue;
t =
(fixedUtils_1.FixedUtils.directCast(coin.normalizedBalance) *
fixedUtils_1.FixedUtils.complement(fixedUtils_1.FixedUtils.directCast(coin.tradeFeeOut) * lpRatio)) /
amountOut;
if (t < tMin)
tMin = t;
}
tDrain = tMin;
// remaining test points are the CF discontinuities: where B0 - t*D = R*B0
for (let [coinTypeT, coinT] of Object.entries(coins)) {
amountOut = fixedUtils_1.FixedUtils.castAndNormalize(coinT.decimalsScalar, amountsOutDirection[coinTypeT] || BigInt(0));
if (amountOut === 0)
continue;
balance = fixedUtils_1.FixedUtils.directCast(coinT.normalizedBalance);
t = (balance * lpc) / amountOut;
prod = 0;
sum = 0;
keepT = true;
for (let [coinType, coin] of Object.entries(coins)) {
balance = fixedUtils_1.FixedUtils.directCast(coin.normalizedBalance);
weight = fixedUtils_1.FixedUtils.directCast(coin.weight);
amountOut = fixedUtils_1.FixedUtils.castAndNormalize(coin.decimalsScalar, amountsOutDirection[coinType] || BigInt(0));
part1 = t * amountOut;
if (part1 >= balance) {
// this t is too large to be a bound because B0 - t*D overdraws the pool
keepT = false;
break;
}
part1 = balance - part1;
part2 = lpr * balance;
part3 =
part1 >= part2
? part2 +
fixedUtils_1.FixedUtils.complement(fixedUtils_1.FixedUtils.directCast(coin.tradeFeeIn)) *
(part1 - part2)
: part2 -
(part2 - part1) /
fixedUtils_1.FixedUtils.complement(fixedUtils_1.FixedUtils.directCast(coin.tradeFeeOut));
prod += weight * Math.log(part3);
sum += weight * part3;
}
if (keepT) {
prod = Math.exp(prod);
cf =
(2 * a * prod * sum) / (prod + scaledInvariant) + ac * prod;
if (cf >= scaledInvariant) {
// upper bound, check against cfMax
if (cf <= cfMax) {
tMax = t;
cfMax = cf;
}
}
if (cf <= scaledInvariant) {
// lower bound, check against cfMin
if (cf >= cfMin) {
tMin = t;
cfMin = cf;
}
}
}
}
// initial estimate is the linear interpolation between discontinuity bounds
t =
cfMax === cfMin
? tMin
: (tMin * cfMax +
tMax * scaledInvariant -
tMax * cfMin -
tMin * scaledInvariant) /
(cfMax - cfMin);
return [t, tDrain];
};
// Dusty direct all-coin deposit, returns the number s >= 0 so that amounts_in = s*B0 + dust.
// When performing an all-coin deposit, call this function to get t then split amounts_in into s*B0 + dust.
// At least one coordinate of dust will be 0. Send the s*B0 balances into the pool and mint s*total_lp.
// The caller keeps the dust.
CmmmCalculations.calcAllCoinDeposit = (pool, amountsIn) => {
let coins = pool.coins;
let balance;
let amountIn;
let s;
let sMin = Number.POSITIVE_INFINITY;
for (let [coinType, coin] of Object.entries(coins)) {
balance = fixedUtils_1.FixedUtils.directCast(coin.normalizedBalance);
amountIn = fixedUtils_1.FixedUtils.castAndNormalize(coin.decimalsScalar, amountsIn[coinType] || BigInt(0));
s = amountIn / balance;
if (s < sMin)
sMin = s;
}
let returner = {};
for (let coinType of Object.keys(coins))
returner[coinType] = utils_1.Helpers.blendedOperations.mulNBB(sMin, amountsIn[coinType] || BigInt(0));
return returner;
};
// Dusty direct all-coin withdraw, returns the number s >= 0 so that amounts_out + dust = s*B0.
// The normal all-coin withdraw (take this exact amount of lp and give however much balances out)
// should be done directly without this function -- just burn the lp and give the user
// lp/total_lp * balance_i in each coordinate. This function is for finding how much lp it takes to
// ensure that at least amounts_out comes out.
CmmmCalculations.calcAllCoinWithdraw = (pool, amountsOut) => {
let coins = pool.coins;
let balance;
let amountOut;
let s;
let sMax = 0;
for (let [coinType, coin] of Object.entries(coins)) {
balance = fixedUtils_1.FixedUtils.directCast(coin.normalizedBalance);
amountOut = fixedUtils_1.FixedUtils.castAndNormalize(coin.decimalsScalar, amountsOut[coinType] || BigInt(0));
s = amountOut / balance;
if (s > sMax)
sMax = s;
}
let returner = {};
for (let coinType of Object.keys(coins))
returner[coinType] = utils_1.Helpers.blendedOperations.mulNBB(sMax, amountsOut[coinType] || BigInt(0));
return returner;
};
// This function calculates the balance of a given token (index) given all the other balances (combined in p0, s0)
// and the invariant along with an initial estimate. It is useful for 1d optimization.
CmmmCalculations.getTokenBalanceGivenInvariantAndAllOtherBalances = (flatness, w, h, xi, // initial estimate -- default can be (P(X) / p0)^n
p0, // P(B) / xi^(1/n) (everything but the missing part)
s0 // S(B) - xi / n (everything but the missing part)
) => {
if (isNaN(xi))
throw new Error("initial estimate is not a number");
// Standard Newton method used here
// ---------------- setting constants ----------------
// c1 = 2*A*w*w
// c2 = 2*(1-A)*w*p0
// c3 = A*(2*w*s0+t)
// c4 = t*t/p0
// c5 = (1-A)*p0
// c6 = A*(2*s0+w*t)
// c7 = 2*A*w*(w+1)
// c8 = 2*(1-A)*p0
// c9 = 2*A*w*s0
// c10= A*w*t
let ac = 1 - flatness;
let aw = flatness * w;
let acw = ac * w;
let as0 = flatness * s0;
let ah = flatness * h;
let c1 = 2 * aw * w;
let c2 = 2 * acw * p0;
let c3 = 2 * w * as0 + ah;
let c4 = (h * h) / p0;
let c5 = ac * p0;
let c6 = 2 * as0 + w * ah;
let c7 = 2 * aw * (w + 1);
let c8 = 2 * acw * p0;
let c9 = 2 * aw * s0;
let c10 = aw * h;
// ---------------- iterating ----------------
//x = (
// x * (
// (
// x^w * (
// c1 * x + c2 * x^w + c3
// ) + c4
// ) - x^w * (
// c5 * x^w + c6
// )
// )
//) / (
// x^w * (
// (
// c7 * x + c8 * x^w + c9
// ) - c10
// )
//)
let x = xi;
let xw; // x^w
let topPos;
let topNeg;
let bottomPos;
//let bottomNeg;
let prevX = x;
let i = 0;
while (i < CmmmCalculations.maxNewtonAttempts) {
xw = Math.pow(x, w);
topPos = x * (xw * (c1 * x + c2 * xw + c3) + c4);
topNeg = x * (xw * (c5 * xw + c6));
bottomPos = c7 * x + c8 * xw + c9;
//bottomNeg = c10;
// If x jumps too much (bad initial estimate) then g(x) might overshoot into a negative number.
// This only happens if x is supposed to be small. In this case, replace x with a small number and try again.
// Once x is close enough to the true value g(x) won't overshoot anymore and this test will be skipped from then on.
if (topPos < topNeg || bottomPos < c10) {
x = 1 / Math.pow(2, i);
i = i + 1;
continue;
}
x = (topPos - topNeg) / (xw * (bottomPos - c10));
// using relative error here (easier to pass) because js numbers are less precise
if (utils_1.Helpers.closeEnough(x, prevX, CmmmCalculations.convergenceBound)) {
return x;
}
prevX = x;
i = i + 1;
}
throw Error("Newton diverged");
};
// Compute the invariant before swap and pseudoinvariant (invariant considering fees)
// after the swap and see if they are the same up to a tolerance.
// It also checks that this balance does not drain the pool i.e. the final balance is at least 1.
// The scalars are here to avoid unnecessary vector creation. In most calls one scalar will be 10^18 (1).
CmmmCalculations.checkValidSwap = (pool, amountsIn, amountsInScalar, amountsOut, amountsOutScalar) => {
let coins = pool.coins;
let flatness = fixedUtils_1.FixedUtils.directCast(pool.flatness);
// balance = balances[i]
let balance;
// pseudobalance = balance + feedAmountIn - feedAmountOut
let pseudobalance;
// postbalance = balance + amountIn - amountOut
let postbalance;
let weight;
let amountIn;
let amountOut;
let feedAmountIn;
let feedAmountOut;
let preprod = 0;
let presum = 0;
let pseudoprod = 0;
let pseudosum = 0;
let postprod = 0;
let postsum = 0;
for (let [coinType, coin] of Object.entries(coins)) {
balance = fixedUtils_1.FixedUtils.directCast(coin.normalizedBalance);
weight = fixedUtils_1.FixedUtils.directCast(coin.weight);
amountIn =
fixedUtils_1.FixedUtils.castAndNormalize(coin.decimalsScalar, amountsIn[coinType] || BigInt(0)) * amountsInScalar;
amountOut =
fixedUtils_1.FixedUtils.castAndNormalize(coin.decimalsScalar, amountsOut[coinType] || BigInt(0)) * amountsOutScalar;
if (amountIn > 0 && amountOut > 0)
return false;
feedAmountIn =
amountIn * (1 - fixedUtils_1.FixedUtils.directCast(coin.tradeFeeIn));
feedAmountOut =
amountOut === 0
? 0
: amountOut / (1 - fixedUtils_1.FixedUtils.directCast(coin.tradeFeeOut));
postbalance = balance + amountIn;
if (amountOut > postbalance + 1)
return false;
postbalance -= -amountOut;
pseudobalance = balance + feedAmountIn;
if (feedAmountOut > pseudobalance + 1)
return false;
pseudobalance -= -feedAmountOut;
preprod += weight * Math.log(balance);
presum += weight * balance;
postprod += weight * Math.log(postbalance);
postsum += weight * postbalance;
pseudoprod += weight * Math.log(pseudobalance);
pseudosum += weight * pseudobalance;
}
preprod = Math.exp(preprod);
postprod = Math.exp(postprod);
pseudoprod = Math.exp(pseudoprod);
let preinvariant = CmmmCalculations.calcInvariantQuadratic(preprod, presum, flatness);
let postinvariant = CmmmCalculations.calcInvariantQuadratic(postprod, postsum, flatness);
let pseudoinvariant = CmmmCalculations.calcInvariantQuadratic(pseudoprod, pseudosum, flatness);
return (postinvariant * (1 + CmmmCalculations.tolerance) >= preinvariant &&
(utils_1.Helpers.veryCloseInt(preinvariant, pseudoinvariant, fixedUtils_1.FixedUtils.fixedOneN) ||
utils_1.Helpers.closeEnough(preinvariant, pseudoinvariant, CmmmCalculations.validityTolerance)));
};
// Compute the invariant before swap and pseudoinvariant (invariant considering fees)
// after the swap and see if they are the same up to a tolerance.
// It also checks that this balance does not drain the pool i.e. the final balance is at least 1.
CmmmCalculations.checkValid1dSwap = (pool, coinTypeIn, coinTypeOut, amountInB, amountOutB) => {
if (coinTypeIn === coinTypeOut)
return false;
let coins = pool.coins;
let coinIn = coins[coinTypeIn];
let coinOut = coins[coinTypeOut];
let flatness = fixedUtils_1.FixedUtils.directCast(pool.flatness);
// balance = balances[i]
let balance;
// pseudobalance = balance + feed amount in - feed amount out
let pseudobalance;
// postbalance = balance + amount in - amount out
let postbalance;
let weight;
let amountIn = fixedUtils_1.FixedUtils.castAndNormalize(coinIn.decimalsScalar, amountInB);
let amountOut = fixedUtils_1.FixedUtils.castAndNormalize(coinOut.decimalsScalar, amountOutB);
let feedAmountIn = amountIn * (1 - fixedUtils_1.FixedUtils.directCast(coinIn.tradeFeeIn));
let feedAmountOut = amountOut === 0
? 0
: amountOut / (1 - fixedUtils_1.FixedUtils.directCast(coinOut.tradeFeeOut));
let preprod = 0;
let presum = 0;
let pseudoprod = 0;
let pseudosum = 0;
let postprod = 0;
let postsum = 0;
let p;
let s;
for (let [coinType, coin] of Object.entries(coins)) {
balance = fixedUtils_1.FixedUtils.directCast(coin.normalizedBalance);
weight = fixedUtils_1.FixedUtils.directCast(coin.weight);
p = weight * Math.log(balance);
s = weight * balance;
preprod += p;
presum += s;
if (coinType === coinTypeIn) {
pseudobalance = balance + feedAmountIn;
postbalance = balance + amountIn;
pseudoprod += weight * Math.log(pseudobalance);
pseudosum += weight * pseudobalance;
postprod += weight * Math.log(postbalance);
postsum += weight * postbalance;
}
else {
if (coinType === coinTypeOut) {
if (feedAmountOut > balance + 1 || amountOut > balance + 1)
return false;
pseudobalance = balance - feedAmountOut;
postbalance = balance - amountOut;
pseudoprod += weight * Math.log(pseudobalance);
pseudosum += weight * pseudobalance;
postprod += weight * Math.log(postbalance);
postsum += weight * postbalance;
}
else {
pseudoprod += p;
pseudosum += s;
postprod += p;
postsum += s;
}
}
}
preprod = Math.exp(preprod);
postprod = Math.exp(postprod);
pseudoprod = Math.exp(pseudoprod);
let preinvariant = CmmmCalculations.calcInvariantQuadratic(preprod, presum, flatness);
let postinvariant = CmmmCalculations.calcInvariantQuadratic(postprod, postsum, flatness);
let pseudoinvariant = CmmmCalculations.calcInvariantQuadratic(pseudoprod, pseudosum, flatness);
return (postinvariant * (1 + CmmmCalculations.tolerance) >= preinvariant &&
(utils_1.Helpers.veryCloseInt(preinvariant, pseudoinvariant, fixedUtils_1.FixedUtils.fixedOneN) ||
utils_1.Helpers.closeEnough(preinvariant, pseudoinvariant, CmmmCalculations.validityTolerance)));
};
// A fixed amount investment is a swap followed by an all coin investment. This function checks that the
// intermediate swap is allowed and corresponds to the claimed lp ratio.
CmmmCalculations.checkValidDeposit = (pool, amountsIn, lpRatioRaw) => {
// The supposed swap is from B0 to R*(B0 + Din)
// This test is check_valid_swap for those data
let coins = pool.coins;
let lpRatio = fixedUtils_1.FixedUtils.directCast(lpRatioRaw);
if (lpRatio > 1)
return false;
let flatness = fixedUtils_1.FixedUtils.directCast(pool.flatness);
// balance = balances[i]
let balance;
let weight;
// amount = amountsIn[i]
let amount;
// postbalance = lpRatio * (balance + amount)
let postbalance;
// pseudobalance = fee(postbalance - balance) + balance
let pseudobalance;
// diff = postbalance - balance
let diff;
// pseudodiff = fee(diff)
let pseudodiff;
let preprod = 0;
let presum = 0;
let pseudoprod = 0;
let pseudosum = 0;
let postprod = 0;
let postsum = 0;
for (let [coinType, coin] of Object.entries(coins)) {
balance = fixedUtils_1.FixedUtils.directCast(coin.normalizedBalance);
weight = fixedUtils_1.FixedUtils.directCast(coin.weight);
amount = fixedUtils_1.FixedUtils.castAndNormalize(coin.decimalsScalar, amountsIn[coinType] || BigInt(0));
postbalance = lpRatio * (balance + amount);
if (postbalance >= balance) {
// use fee in
diff = postbalance - balance;
pseudodiff =
diff * (1 - fixedUtils_1.FixedUtils.directCast(coin.tradeFeeIn));
pseudobalance = balance + pseudodiff;
}
else {
// use fee out
diff = balance - postbalance;
pseudodiff =
diff === 0
? 0
: diff / (1 - fixedUtils_1.FixedUtils.directCast(coin.tradeFeeOut));
if (pseudodiff >= balance + 1)
return false;
pseudobalance = balance - pseudodiff;
}
preprod += weight * Math.log(balance);
presum += weight * balance;
postprod += weight * Math.log(postbalance);
postsum += weight * postbalance;
pseudoprod += weight * Math.log(pseudobalance);
pseudosum += weight * pseudobalance;
}
preprod = Math.exp(preprod);
postprod = Math.exp(postprod);
pseudoprod = Math.exp(pseudoprod);
let preinvariant = CmmmCalculations.calcInvariantQuadratic(preprod, presum, flatness);
let postinvariant = CmmmCalculations.calcInvariantQuadratic(postprod, postsum, flatness);
let pseudoinvariant = CmmmCalculations.calcInvariantQuadratic(pseudoprod, pseudosum, flatness);
return (postinvariant * (1 + CmmmCalculations.tolerance) >= preinvariant &&