aftermath-ts-sdk 0.0.136 → 0.0.138

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/dist/general/utils/fixed.d.ts +15 -0
  2. package/dist/general/utils/fixed.d.ts.map +1 -0
  3. package/dist/general/utils/fixed.js +15 -0
  4. package/dist/general/utils/helpers.d.ts +9 -0
  5. package/dist/general/utils/helpers.d.ts.map +1 -1
  6. package/dist/general/utils/helpers.js +9 -0
  7. package/dist/packages/pools/api/poolsApiCasting.d.ts.map +1 -1
  8. package/dist/packages/pools/api/poolsApiCasting.js +7 -7
  9. package/dist/packages/pools/api/poolsApiHelpers.js +1 -1
  10. package/dist/packages/pools/utils/cmmmCalculations.d.ts +22 -8
  11. package/dist/packages/pools/utils/cmmmCalculations.d.ts.map +1 -1
  12. package/dist/packages/pools/utils/cmmmCalculations.js +1009 -261
  13. package/dist/packages/router/utils/synchronous/routerGraph.d.ts.map +1 -1
  14. package/dist/packages/router/utils/synchronous/routerGraph.js +1 -13
  15. package/dist/packages/router/utils/synchronous/routerPools/aftermathRouterPool.d.ts.map +1 -1
  16. package/dist/packages/router/utils/synchronous/routerPools/aftermathRouterPool.js +26 -8
  17. package/dist/packages/staking/api/stakingApi.d.ts +4 -1
  18. package/dist/packages/staking/api/stakingApi.d.ts.map +1 -1
  19. package/dist/packages/staking/api/stakingApi.js +3 -0
  20. package/dist/packages/staking/api/stakingApiCasting.d.ts.map +1 -1
  21. package/dist/packages/staking/api/stakingApiCasting.js +0 -1
  22. package/dist/packages/staking/api/stakingApiCastingTypes.d.ts +0 -1
  23. package/dist/packages/staking/api/stakingApiCastingTypes.d.ts.map +1 -1
  24. package/dist/packages/staking/api/stakingApiHelpers.d.ts +2 -0
  25. package/dist/packages/staking/api/stakingApiHelpers.d.ts.map +1 -1
  26. package/dist/packages/staking/api/stakingApiHelpers.js +12 -0
  27. package/dist/packages/staking/staking.d.ts +2 -1
  28. package/dist/packages/staking/staking.d.ts.map +1 -1
  29. package/dist/packages/staking/staking.js +5 -0
  30. package/dist/packages/staking/stakingTypes.d.ts +3 -2
  31. package/dist/packages/staking/stakingTypes.d.ts.map +1 -1
  32. package/package.json +1 -1
@@ -2,11 +2,38 @@
2
2
  var _a;
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.CmmmCalculations = void 0;
5
- const coin_1 = require("../../coin/coin");
6
5
  const utils_1 = require("../../../general/utils");
6
+ const fixed_1 = require("../../../general/utils/fixed");
7
7
  // This file is the typescript version of on-chain calculations. See the .move file for license info.
8
8
  // These calculations are useful for estimating values on-chain but the JS number format is LESS PRECISE!
9
9
  // Do not expect these values to be identical to their on-chain counterparts.
10
+ // The formula used here differs from that of Curve/Balancer. Our stables allow custom price pegs as opposed to
11
+ // the constant 1:1 equal-weight peg. Also our pools do not have an upper coin type limit (practically).
12
+ // Here is our construction:
13
+ // Start with a pool with balances b1,...,bn > 0. Call the tuple B = (b1,...,bn).
14
+ // Take weights w1,...,wn with 0 < wi < 1 and w1 + ... + wn = 1.
15
+ // Let X stand for the tuple (x1,...,xn) in Rn.
16
+ // For normalization we need the tuple T = (h,h,...,h) for some h > 0, solved for later.
17
+ // The invariant is defined as the value of this h.
18
+ // -- TODO: generalize this reference point T to lie on a chosen ray like (w1*h, w2*h, ..., wn*h).
19
+ // -- This would allow setting the swap price to be centered at a chosen balance distribution instead
20
+ // -- of the 1:1:...:1 diagonal balance distribution currently in use.
21
+ // Define the sum function S: Rn -> R as S(X) = w1*x1 + ... + wn*xn.
22
+ // Define the product function P: Rn -> R as P(X) = x1^w1 * ... * xn^wn.
23
+ // Note P(T) = S(T) = h.
24
+ // We want the sum to vanish on the coordinate hyperplanes too so instead use L where
25
+ // L(X) = [2P(X) / (P(X) + P(T))] * S(X)
26
+ // Then 0 <= L(X) < 2S(X) and L(T) = h.
27
+ // The constant price surface is defined by the equation L(X) = L(B) and the product curve by
28
+ // P(X) = P(B). Equivilantly by L(X) - L(B) = 0, P(X) - P(B) = 0.
29
+ // Take a flatness parameter A, 0 <= A <= 1. Then (1-A) is the dual parameter:
30
+ // 0 <= (1-A) <= 1 and A + (1-A) = 1. Take the linear combination of the defining functions
31
+ // C(X) = A * L(X) + (1-A) * P(X). The stable curve is defined as the solution to C(X) = C(B).
32
+ // Moreover we can solve for T from the equation C(T) = C(B), making all the following equal:
33
+ // C(B) = L(T) = S(T) = P(T) = h.
34
+ // The defining equation C(X) = C(B) can be rewritten in a computationally simpler form as
35
+ // P(X) * (2A * S(X) + (1-A) * P(X)) = h * (A * P(X) + h).
36
+ // To see these functions/equations in action check out https://www.desmos.com/calculator/eu5mfckuk9
10
37
  class CmmmCalculations {
11
38
  }
12
39
  exports.CmmmCalculations = CmmmCalculations;
@@ -26,21 +53,15 @@ CmmmCalculations.minInvariantRatio = 0.7;
26
53
  CmmmCalculations.maxInvariantRatio = 3;
27
54
  CmmmCalculations.maxNewtonAttempts = 255;
28
55
  CmmmCalculations.convergenceBound = 1e-9;
29
- CmmmCalculations.withdrawConvergenceBound = 0.000001;
30
- // pools assume coins are stored in raw integer format
31
- // every other fixed point nubmer is in 18 point format
32
- // these terms come from their on chain equivalents where direct cast means (x: u64 as u256)
33
- CmmmCalculations.convertFromInt = (n) => Number(n);
34
- CmmmCalculations.convertToInt = (n) => BigInt(Math.floor(n));
35
- CmmmCalculations.directCast = (n) => coin_1.Coin.balanceWithDecimals(n, 18);
36
- CmmmCalculations.directUncast = (n) => coin_1.Coin.normalizeBalance(n, 18);
56
+ CmmmCalculations.tolerance = 1e-13;
57
+ CmmmCalculations.validityTolerance = 0.000001;
37
58
  // Invariant is used to govern pool behavior. Swaps are operations which change the pool balances without changing
38
59
  // the invariant (ignoring fees) and investments change the invariant without changing the distribution of balances.
39
60
  // Invariant and pool lp are almost in 1:1 correspondence -- e.g. burning lp in a withdraw proportionally lowers the pool invariant.
40
61
  // The difference is as swap fees are absorbed they increase the invariant without incrasing total lp, increasing lp worth.
41
62
  // Every pool operation either explicitly or implicity calls this function.
42
63
  CmmmCalculations.calcInvariant = (pool) => {
43
- let flatness = CmmmCalculations.directCast(pool.flatness);
64
+ let flatness = fixed_1.Fixed.directCast(pool.flatness);
44
65
  // The value for h which we want is the one for which the balances vector B lies on the curve through T.
45
66
  // That is, C(T) = C(B). This turns out to be a quadratic equation which can be solved with
46
67
  // h = [sqrt[P(B) * (P(B) * (A*A + 4*(1-A)) + 8*A*S(B))] - A*P(B)] / 2.
@@ -49,8 +70,8 @@ CmmmCalculations.calcInvariant = (pool) => {
49
70
  let balance;
50
71
  let weight;
51
72
  for (let coin of Object.values(pool.coins)) {
52
- balance = CmmmCalculations.convertFromInt(coin.balance);
53
- weight = CmmmCalculations.directCast(coin.weight);
73
+ balance = fixed_1.Fixed.convertFromInt(coin.balance);
74
+ weight = fixed_1.Fixed.directCast(coin.weight);
54
75
  sum += weight * balance;
55
76
  prod += weight * Math.log(balance);
56
77
  }
@@ -73,7 +94,7 @@ CmmmCalculations.calcInvariantQuadratic = (prod, sum, flatness) => (Math.sqrt(pr
73
94
  // s0 = w1*b1 + ... + [wi*bi] + ... + wn*bn (remove bi from sum)
74
95
  // h is the invariant
75
96
  CmmmCalculations.calcInvariantComponents = (pool, index) => {
76
- let flatness = CmmmCalculations.directCast(pool.flatness);
97
+ let flatness = fixed_1.Fixed.directCast(pool.flatness);
77
98
  let prod = 0;
78
99
  let sum = 0;
79
100
  let p0 = 0;
@@ -83,8 +104,8 @@ CmmmCalculations.calcInvariantComponents = (pool, index) => {
83
104
  let p;
84
105
  let s;
85
106
  for (let [coinType, coin] of Object.entries(pool.coins)) {
86
- balance = CmmmCalculations.convertFromInt(coin.balance);
87
- weight = CmmmCalculations.directCast(coin.weight);
107
+ balance = fixed_1.Fixed.convertFromInt(coin.balance);
108
+ weight = fixed_1.Fixed.directCast(coin.weight);
88
109
  p = weight * Math.log(balance);
89
110
  s = weight * balance;
90
111
  prod = prod + p;
@@ -104,106 +125,20 @@ CmmmCalculations.calcInvariantComponents = (pool, index) => {
104
125
  CmmmCalculations.calcInvariantQuadratic(prod, sum, flatness),
105
126
  ];
106
127
  };
107
- // This function calculates the balance of a given token (index) given all the other balances (combined in p0, s0)
108
- // and the invariant along with an initial estimate. It is useful for 1d optimization.
109
- CmmmCalculations.getTokenBalanceGivenInvariantAndAllOtherBalances = (flatness, w, h, xi, // initial estimate -- default can be (P(X) / p0)^n
110
- p0, // P(B) / xi^(1/n) (everything but the missing part)
111
- s0 // S(B) - xi / n (everything but the missing part)
112
- ) => {
113
- // Standard Newton method used here
114
- // ---------------- setting constants ----------------
115
- // c1 = 2*A*w*w
116
- // c2 = 2*(1-A)*w*p0
117
- // c3 = A*(2*w*s0+t)
118
- // c4 = t*t/p0
119
- // c5 = (1-A)*p0
120
- // c6 = A*(2*s0+w*t)
121
- // c7 = 2*A*w*(w+1)
122
- // c8 = 2*(1-A)*p0
123
- // c9 = 2*A*w*s0
124
- // c10= A*w*t
125
- let ac = 1 - flatness;
126
- let aw = flatness * w;
127
- let acw = ac * w;
128
- let as0 = flatness * s0;
129
- let ah = flatness * h;
130
- let c1 = 2 * aw * w;
131
- let c2 = 2 * acw * p0;
132
- let c3 = 2 * w * as0 + ah;
133
- let c4 = (h * h) / p0;
134
- let c5 = ac * p0;
135
- let c6 = 2 * as0 + w * ah;
136
- let c7 = 2 * aw * (w + 1);
137
- let c8 = 2 * acw * p0;
138
- let c9 = 2 * aw * s0;
139
- let c10 = aw * h;
140
- // ---------------- iterating ----------------
141
- //x = (
142
- // x * (
143
- // (
144
- // x^w * (
145
- // c1 * x + c2 * x^w + c3
146
- // ) + c4
147
- // ) - x^w * (
148
- // c5 * x^w + c6
149
- // )
150
- // )
151
- //) / (
152
- // x^w * (
153
- // (
154
- // c7 * x + c8 * x^w + c9
155
- // ) - c10
156
- // )
157
- //)
158
- let x = xi;
159
- let xw; // x^w
160
- let topPos;
161
- let topNeg;
162
- let bottomPos;
163
- //let bottomNeg;
164
- let prevX = x;
165
- let i = 0;
166
- while (i < CmmmCalculations.maxNewtonAttempts) {
167
- xw = Math.pow(x, w);
168
- topPos = x * (xw * (c1 * x + c2 * xw + c3) + c4);
169
- topNeg = x * (xw * (c5 * xw + c6));
170
- bottomPos = c7 * x + c8 * xw + c9;
171
- //bottomNeg = c10;
172
- // If x jumps too much (bad initial estimate) then g(x) might overshoot into a negative number.
173
- // This only happens if x is supposed to be small. In this case, replace x with a small number and try again.
174
- // Once x is close enough to the true value g(x) won't overshoot anymore and this test will be skipped from then on.
175
- if (topPos < topNeg || bottomPos < c10) {
176
- x = 1 / Math.pow(2, i);
177
- i = i + 1;
178
- continue;
179
- }
180
- x = (topPos - topNeg) / (xw * (bottomPos - c10));
181
- // using relative error here (easier to pass) because js numbers are less precise
182
- if (utils_1.Helpers.closeEnough(x, prevX, CmmmCalculations.convergenceBound)) {
183
- return x;
184
- }
185
- prevX = x;
186
- i = i + 1;
187
- }
188
- throw Error("Newton diverged");
189
- };
128
+ // spot price is given in units of Bin / Bout
190
129
  CmmmCalculations.calcSpotPrice = (pool, coinTypeIn, coinTypeOut) => CmmmCalculations.calcSpotPriceWithFees(pool, coinTypeIn, coinTypeOut, true);
191
130
  // spot price is given in units of Bin / Bout
192
131
  CmmmCalculations.calcSpotPriceWithFees = (pool, coinTypeIn, coinTypeOut, ignoreFees) => {
193
- let a = CmmmCalculations.directCast(pool.flatness);
132
+ let a = fixed_1.Fixed.directCast(pool.flatness);
194
133
  let part1 = CmmmCalculations.calcSpotPriceBody(pool);
195
134
  let coinIn = pool.coins[coinTypeIn];
196
135
  let coinOut = pool.coins[coinTypeOut];
197
- let balanceIn = CmmmCalculations.convertFromInt(coinIn.balance);
198
- let balanceOut = CmmmCalculations.convertFromInt(coinOut.balance);
199
- let weightIn = CmmmCalculations.directCast(coinIn.weight);
200
- let weightOut = CmmmCalculations.directCast(coinOut.weight);
201
- let swapFeeIn = ignoreFees
202
- ? 0
203
- : CmmmCalculations.directCast(coinIn.tradeFeeIn);
204
- let swapFeeOut = ignoreFees
205
- ? 0
206
- : CmmmCalculations.directCast(coinIn.tradeFeeOut);
136
+ let balanceIn = fixed_1.Fixed.convertFromInt(coinIn.balance);
137
+ let balanceOut = fixed_1.Fixed.convertFromInt(coinOut.balance);
138
+ let weightIn = fixed_1.Fixed.directCast(coinIn.weight);
139
+ let weightOut = fixed_1.Fixed.directCast(coinOut.weight);
140
+ let swapFeeIn = ignoreFees ? 0 : fixed_1.Fixed.directCast(coinIn.tradeFeeIn);
141
+ let swapFeeOut = ignoreFees ? 0 : fixed_1.Fixed.directCast(coinIn.tradeFeeOut);
207
142
  let sbi = weightOut * balanceIn;
208
143
  // this is the only place where fee values are used
209
144
  let sbo = (1 - swapFeeIn) * (1 - swapFeeOut) * weightIn * balanceOut;
@@ -213,7 +148,7 @@ CmmmCalculations.calcSpotPriceWithFees = (pool, coinTypeIn, coinTypeOut, ignoreF
213
148
  // The spot price formula contains a factor of C0^2 / P(B0) + (1-A)P(B0), this returns that
214
149
  CmmmCalculations.calcSpotPriceBody = (pool) => {
215
150
  // The spot price formula comes from the partial derivatives of Cf, specifically -(dCf / dxOut) / (dCf / dxIn)
216
- let a = CmmmCalculations.directCast(pool.flatness);
151
+ let a = fixed_1.Fixed.directCast(pool.flatness);
217
152
  let ac = 1 - a;
218
153
  let prod = 0;
219
154
  let sum = 0;
@@ -222,8 +157,8 @@ CmmmCalculations.calcSpotPriceBody = (pool) => {
222
157
  // The spot price formula requires knowing the value of the invariant. We need the prod and sum parts
223
158
  // also later on so no need to compute them twice by calling calcInvariant, just evaluate here.
224
159
  for (let coin of Object.values(pool.coins)) {
225
- balance = CmmmCalculations.convertFromInt(coin.balance);
226
- weight = CmmmCalculations.directCast(coin.weight);
160
+ balance = fixed_1.Fixed.convertFromInt(coin.balance);
161
+ weight = fixed_1.Fixed.directCast(coin.weight);
227
162
  prod += weight * Math.log(balance);
228
163
  sum += weight * balance;
229
164
  }
@@ -237,31 +172,33 @@ CmmmCalculations.calcOutGivenIn = (pool, coinTypeIn, coinTypeOut, amountIn) => {
237
172
  throw Error("in and out must be different coins");
238
173
  let coinIn = pool.coins[coinTypeIn];
239
174
  let coinOut = pool.coins[coinTypeOut];
240
- let swapFeeIn = CmmmCalculations.directCast(coinIn.tradeFeeIn);
241
- let swapFeeOut = CmmmCalculations.directCast(coinOut.tradeFeeOut);
175
+ let swapFeeIn = fixed_1.Fixed.directCast(coinIn.tradeFeeIn);
176
+ let swapFeeOut = fixed_1.Fixed.directCast(coinOut.tradeFeeOut);
242
177
  if (swapFeeIn >= 1 || swapFeeOut >= 1) {
243
178
  // this swap is disabled
244
179
  return BigInt(0);
245
180
  }
246
- let flatness = CmmmCalculations.directCast(pool.flatness);
247
- let oldIn = CmmmCalculations.convertFromInt(coinIn.balance);
248
- let oldOut = CmmmCalculations.convertFromInt(coinOut.balance);
249
- let wIn = CmmmCalculations.directCast(coinIn.weight);
181
+ let flatness = fixed_1.Fixed.directCast(pool.flatness);
182
+ let oldIn = fixed_1.Fixed.convertFromInt(coinIn.balance);
183
+ let oldOut = fixed_1.Fixed.convertFromInt(coinOut.balance);
184
+ let wIn = fixed_1.Fixed.directCast(coinIn.weight);
250
185
  let [prod, _sum, p0, s0, h] = CmmmCalculations.calcInvariantComponents(pool, coinTypeOut);
251
- let feedAmountIn = (1 - swapFeeIn) * CmmmCalculations.convertFromInt(amountIn);
186
+ let feedAmountIn = (1 - swapFeeIn) * fixed_1.Fixed.convertFromInt(amountIn);
252
187
  let newIn = oldIn + feedAmountIn;
253
188
  let prodRatio = Math.pow(newIn / oldIn, wIn);
254
189
  let newP0 = p0 * prodRatio;
255
190
  // the initial estimate (xi) is from if there were only the product part of the curve
256
191
  let xi = Math.pow(prod / newP0, 1 / wIn);
257
192
  let newS0 = s0 + wIn * feedAmountIn;
258
- let wOut = CmmmCalculations.directCast(coinOut.weight);
193
+ let wOut = fixed_1.Fixed.directCast(coinOut.weight);
259
194
  let tokenAmountOut = CmmmCalculations.getTokenBalanceGivenInvariantAndAllOtherBalances(flatness, wOut, h, xi, // initial estimate -- default can be (P(X) / p0)^n
260
195
  newP0, // P(B) / xi^(1/n) (everything but the missing part)
261
196
  newS0 // S(B) - xi / n (everything but the missing part)
262
197
  );
263
- let amountOut = (oldOut - tokenAmountOut) * (1 - swapFeeOut);
264
- return CmmmCalculations.convertToInt(amountOut);
198
+ let amountOut = fixed_1.Fixed.convertToInt((oldOut - tokenAmountOut) * (1 - swapFeeOut));
199
+ if (!CmmmCalculations.checkValid1dSwap(pool, coinTypeIn, coinTypeOut, amountIn, amountOut))
200
+ throw Error("invalid 1d swap");
201
+ return amountOut;
265
202
  };
266
203
  // 1d optimized swap function for finding in given out. Returns the amount in.
267
204
  CmmmCalculations.calcInGivenOut = (pool, coinTypeIn, coinTypeOut, amountOut) => {
@@ -269,51 +206,293 @@ CmmmCalculations.calcInGivenOut = (pool, coinTypeIn, coinTypeOut, amountOut) =>
269
206
  throw Error("in and out must be different coins");
270
207
  let coinIn = pool.coins[coinTypeIn];
271
208
  let coinOut = pool.coins[coinTypeOut];
272
- let swapFeeIn = CmmmCalculations.directCast(coinIn.tradeFeeIn);
273
- let swapFeeOut = CmmmCalculations.directCast(coinOut.tradeFeeOut);
209
+ let swapFeeIn = fixed_1.Fixed.directCast(coinIn.tradeFeeIn);
210
+ let swapFeeOut = fixed_1.Fixed.directCast(coinOut.tradeFeeOut);
274
211
  if (swapFeeIn >= 1 || swapFeeOut >= 1) {
275
212
  // this swap is disabled
276
213
  if (amountOut == BigInt(0))
277
214
  return BigInt(0);
278
215
  throw Error("this swap is disabled");
279
216
  }
280
- let flatness = CmmmCalculations.directCast(pool.flatness);
281
- let oldIn = CmmmCalculations.convertFromInt(coinIn.balance);
282
- let oldOut = CmmmCalculations.convertFromInt(coinOut.balance);
283
- let wOut = CmmmCalculations.directCast(coinOut.weight);
217
+ let flatness = fixed_1.Fixed.directCast(pool.flatness);
218
+ let oldIn = fixed_1.Fixed.convertFromInt(coinIn.balance);
219
+ let oldOut = fixed_1.Fixed.convertFromInt(coinOut.balance);
220
+ let wOut = fixed_1.Fixed.directCast(coinOut.weight);
284
221
  let [prod, _sum, p0, s0, h] = CmmmCalculations.calcInvariantComponents(pool, coinTypeIn);
285
- let feedAmountOut = CmmmCalculations.convertFromInt(amountOut) / (1 - swapFeeOut);
222
+ let feedAmountOut = fixed_1.Fixed.convertFromInt(amountOut) / (1 - swapFeeOut);
286
223
  let newOut = oldOut - feedAmountOut;
287
224
  let prodRatio = Math.pow(newOut / oldOut, wOut);
288
225
  let newP0 = p0 * prodRatio;
289
226
  // the initial estimate (xi) is from if there were only the product part of the curve
290
227
  let xi = Math.pow(prod / newP0, 1 / wOut);
291
228
  let newS0 = s0 - wOut * feedAmountOut;
292
- let wIn = CmmmCalculations.directCast(coinIn.weight);
229
+ let wIn = fixed_1.Fixed.directCast(coinIn.weight);
293
230
  let tokenAmountIn = CmmmCalculations.getTokenBalanceGivenInvariantAndAllOtherBalances(flatness, wIn, h, xi, // initial estimate -- default can be (P(X) / p0)^n
294
231
  newP0, // P(B) / xi^(1/n) (everything but the missing part)
295
232
  newS0 // S(B) - xi / n (everything but the missing part)
296
233
  );
297
- let amountIn = (tokenAmountIn - oldIn) / (1 - swapFeeIn);
298
- return CmmmCalculations.convertToInt(amountIn);
234
+ let amountIn = fixed_1.Fixed.convertToInt((tokenAmountIn - oldIn) / (1 - swapFeeIn));
235
+ if (!CmmmCalculations.checkValid1dSwap(pool, coinTypeIn, coinTypeOut, amountIn, amountOut))
236
+ throw Error("invalid 1d swap");
237
+ return amountIn;
238
+ };
239
+ // For computing swap amounts. Given the current balances (and any other parameters) and an amounts in vector,
240
+ // and a expected amounts out vector, determine the value of t > 0 such that t*expected_amounts_out
241
+ // is a valid swap from balances corresponding to adding amounts_in to the pool. The correct value of t is the one for which
242
+ // calc_swap_invariant(balances, ...parameters, amounts_in, t*expected_amounts_out) == calc_invariant_full(balances, ...parameters).
243
+ CmmmCalculations.calcSwapFixedIn = (pool, amountsIn, amountsOutDirection) => {
244
+ let coins = pool.coins;
245
+ let invariant = CmmmCalculations.calcInvariant(pool);
246
+ let a = fixed_1.Fixed.directCast(pool.flatness);
247
+ let ac = 1 - a;
248
+ let t = 1; // assume that the expected amounts out are close to the true amounts out
249
+ // this allows faster convergence if the caller chooses expected_amounts_out well
250
+ let prevT = t;
251
+ let balance;
252
+ let weight;
253
+ let amountIn;
254
+ let amountOut;
255
+ let feeIn;
256
+ let feeOut;
257
+ let prod;
258
+ let prod1;
259
+ let sum;
260
+ let sum1;
261
+ let part1;
262
+ let part2;
263
+ let part3;
264
+ let part4;
265
+ let skip;
266
+ let drainT = Number.POSITIVE_INFINITY;
267
+ let shifter = 1;
268
+ // make sure no disabled coin type is expected
269
+ for (let [coinType, coin] of Object.entries(coins)) {
270
+ amountOut = fixed_1.Fixed.convertFromInt(amountsOutDirection[coinType] || BigInt(0));
271
+ feeOut = fixed_1.Fixed.complement(fixed_1.Fixed.directCast(coin.tradeFeeOut));
272
+ if (amountOut > 0) {
273
+ if (feeOut == 0) {
274
+ throw Error("this trade is disabled");
275
+ }
276
+ else {
277
+ // pool is drained when b + Ain * (1 - Sin) - t * Aout / (1 - Sout) = 0, or t = (b + Ain * (1 - Sin)) * (1 - So) / Aout
278
+ t =
279
+ ((fixed_1.Fixed.convertFromInt(coin.balance) +
280
+ fixed_1.Fixed.convertFromInt(amountsIn[coinType] || BigInt(0)) *
281
+ fixed_1.Fixed.complement(fixed_1.Fixed.directCast(coin.tradeFeeIn))) /
282
+ amountOut) *
283
+ feeOut;
284
+ drainT = Math.min(drainT, t);
285
+ }
286
+ }
287
+ }
288
+ // drain_t is the maximum t can possibly be. It will be 0 if expected amounts out is way too high.
289
+ if (drainT == 0)
290
+ return BigInt(0);
291
+ while (shifter >= drainT)
292
+ shifter /= 2;
293
+ t = 1;
294
+ for (let i = 0; i < CmmmCalculations.maxNewtonAttempts; ++i) {
295
+ prod = 0;
296
+ prod1 = 0;
297
+ sum = 0;
298
+ sum1 = 0;
299
+ skip = false;
300
+ for (let [coinType, coin] of Object.entries(coins)) {
301
+ balance = fixed_1.Fixed.convertFromInt(coin.balance);
302
+ weight = fixed_1.Fixed.directCast(coin.weight);
303
+ amountIn = fixed_1.Fixed.convertFromInt(amountsIn[coinType] || BigInt(0));
304
+ amountOut = fixed_1.Fixed.convertFromInt(amountsOutDirection[coinType] || BigInt(0));
305
+ feeIn = fixed_1.Fixed.complement(fixed_1.Fixed.directCast(coin.tradeFeeIn));
306
+ feeOut = fixed_1.Fixed.complement(fixed_1.Fixed.directCast(coin.tradeFeeOut));
307
+ // pseudoin
308
+ part1 = feeIn * amountIn;
309
+ // pseudoout
310
+ part2 = (t * amountOut) / feeOut;
311
+ // pseudobalance
312
+ if (part2 >= balance + part1 + 1) {
313
+ skip = true;
314
+ break;
315
+ }
316
+ part3 = balance + part1 - part2;
317
+ // for derivatives: weight * expected_amounts_out / fee_out
318
+ part4 = (weight * amountOut) / feeOut;
319
+ prod += weight * Math.log(part3);
320
+ prod1 += part4 / part3;
321
+ sum += weight * part3;
322
+ sum1 += part4;
323
+ }
324
+ prod = Math.exp(prod);
325
+ part1 = a * sum;
326
+ part2 = ac * prod;
327
+ part3 = part1 + part2;
328
+ part4 = a * invariant * prod1;
329
+ t =
330
+ (a * (sum + 2 * t * sum1) +
331
+ part3 +
332
+ 2 * prod1 * t * part3 -
333
+ (t * part4 + invariant * (a + invariant / prod))) /
334
+ (2 * (prod1 * part3 + a * sum1) - part4);
335
+ if (utils_1.Helpers.closeEnough(t, prevT, CmmmCalculations.convergenceBound)) {
336
+ if (!CmmmCalculations.checkValidSwap(pool, amountsIn, 1, amountsOutDirection, t))
337
+ throw Error("invalid swap");
338
+ return fixed_1.Fixed.directUncast(t);
339
+ }
340
+ prevT = t;
341
+ }
342
+ throw Error("Newton diverged");
343
+ };
344
+ // Swaps but fixed amounts out. Given the pool's current state and a guaranteed out vector, and a expected in vector,
345
+ // scale expected_amounts_in by t > 0 so that this swap is valid and return the correct value for t
346
+ CmmmCalculations.calcSwapFixedOut = (pool, amountsInDirection, amountsOut) => {
347
+ let coins = pool.coins;
348
+ let invariant = CmmmCalculations.calcInvariant(pool);
349
+ let a = fixed_1.Fixed.directCast(pool.flatness);
350
+ let ac = 1 - a;
351
+ let t = 1; // assume that the expected amounts out are close to the true amounts out
352
+ // this allows faster convergence if the caller chooses expected_amounts_out well
353
+ let prevT = 0;
354
+ let balance;
355
+ let weight;
356
+ let amountIn;
357
+ let amountOut;
358
+ let feeIn;
359
+ let feeOut;
360
+ let prod;
361
+ let prod1;
362
+ let sum;
363
+ let sum1;
364
+ let part1;
365
+ let part2;
366
+ let part3;
367
+ let part4;
368
+ // make sure no disabled coin type is expected
369
+ for (let [coinType, coin] of Object.entries(coins)) {
370
+ if (coin.tradeFeeOut >= fixed_1.Fixed.fixedOneB &&
371
+ (amountsOut[coinType] || BigInt(0)) > BigInt(0))
372
+ throw Error("this trade is disabled");
373
+ }
374
+ for (let i = 0; i < CmmmCalculations.maxNewtonAttempts; ++i) {
375
+ prod = 0;
376
+ prod1 = 0;
377
+ sum = 0;
378
+ sum1 = 0;
379
+ for (let [coinType, coin] of Object.entries(coins)) {
380
+ balance = fixed_1.Fixed.convertFromInt(coin.balance);
381
+ weight = fixed_1.Fixed.directCast(coin.weight);
382
+ amountIn = fixed_1.Fixed.convertFromInt(amountsInDirection[coinType] || BigInt(0));
383
+ amountOut = fixed_1.Fixed.convertFromInt(amountsOut[coinType] || BigInt(0));
384
+ feeIn = 1 - fixed_1.Fixed.directCast(coin.tradeFeeIn);
385
+ feeOut = 1 - fixed_1.Fixed.directCast(coin.tradeFeeOut);
386
+ // pseudoin expected
387
+ part1 = feeIn * amountIn;
388
+ // pseudoout
389
+ part2 = amountOut == 0 ? 0 : amountOut / feeOut;
390
+ // pseudobalance
391
+ part3 = balance + t * part1 - part2;
392
+ // for derivatives: weight * fee_in * expected_amounts_in
393
+ part4 = weight * part1;
394
+ prod += weight * Math.log(part3);
395
+ prod1 += part4 / part3;
396
+ sum += weight * part3;
397
+ sum1 += part4;
398
+ }
399
+ prod = Math.exp(prod);
400
+ part1 = 2 * a * sum;
401
+ part2 = ac * prod;
402
+ part3 = part1 + part2;
403
+ part4 =
404
+ (part3 + part2) * prod1 + 2 * a * sum1 - a * invariant * prod1;
405
+ t =
406
+ (t * part4 + invariant * (a + invariant / prod) - part3) /
407
+ part4;
408
+ if (utils_1.Helpers.closeEnough(t, prevT, CmmmCalculations.convergenceBound)) {
409
+ if (!CmmmCalculations.checkValidSwap(pool, amountsInDirection, 1, amountsOut, t))
410
+ throw Error("invalid swap");
411
+ return fixed_1.Fixed.directUncast(t);
412
+ }
413
+ prevT = t;
414
+ }
415
+ throw Error("Newton diverged");
299
416
  };
300
417
  // Return the expected lp ratio for this deposit
301
418
  CmmmCalculations.calcDepositFixedAmounts = (pool, amountsIn) => {
302
- if (Object.keys(amountsIn).length === 0)
303
- return utils_1.Casting.fixedOneBigInt;
304
419
  let invariant = CmmmCalculations.calcInvariant(pool);
305
420
  let coins = pool.coins;
306
- let a = CmmmCalculations.directCast(pool.flatness);
421
+ let a = fixed_1.Fixed.directCast(pool.flatness);
422
+ let ac = 1 - a;
423
+ let balance;
424
+ let weight;
425
+ let amount;
426
+ let prod = 0;
427
+ let sum = 0;
428
+ let r = CmmmCalculations.calcDepositFixedAmountsInitialEstimate(pool, amountsIn);
429
+ let prevR = r;
430
+ let fees = {};
431
+ for (let [coinType, coin] of Object.entries(coins)) {
432
+ balance = fixed_1.Fixed.convertFromInt(coin.balance);
433
+ amount = fixed_1.Fixed.convertFromInt(amountsIn[coinType] || BigInt(0));
434
+ fees[coinType] =
435
+ r * (balance + amount) >= balance
436
+ ? 1 - fixed_1.Fixed.directCast(coin.tradeFeeIn)
437
+ : 1 / (1 - fixed_1.Fixed.directCast(coin.tradeFeeOut));
438
+ }
439
+ let i = 0;
440
+ let prod1;
441
+ let sum1;
442
+ let fee;
443
+ let part1;
444
+ let part2;
445
+ let part3;
446
+ let part4;
447
+ while (i < CmmmCalculations.maxNewtonAttempts) {
448
+ prod = 0;
449
+ prod1 = 0;
450
+ sum = 0;
451
+ sum1 = 0;
452
+ for (let [coinType, coin] of Object.entries(coins)) {
453
+ balance = fixed_1.Fixed.convertFromInt(coin.balance);
454
+ weight = fixed_1.Fixed.directCast(coin.weight);
455
+ amount = fixed_1.Fixed.convertFromInt(amountsIn[coinType] || BigInt(0));
456
+ fee = fees[coinType];
457
+ part1 = balance + amount;
458
+ part2 = fee * r * part1 + balance - fee * balance;
459
+ part3 = weight * fee * part1;
460
+ prod += weight * Math.log(part2);
461
+ prod1 += part3 / part2;
462
+ sum += weight * part2;
463
+ sum1 += part3;
464
+ }
465
+ prod = Math.exp(prod);
466
+ part3 = a * invariant * prod1;
467
+ part4 = 2 * prod1 * (a * sum + ac * prod) + 2 * a * sum1;
468
+ r =
469
+ (r * part4 +
470
+ invariant * (1 + invariant / prod) -
471
+ (r * part3 + 2 * a * sum + ac * (prod + invariant))) /
472
+ (part4 - part3);
473
+ if (utils_1.Helpers.closeEnough(r, prevR, CmmmCalculations.convergenceBound)) {
474
+ let scalar = fixed_1.Fixed.directUncast(r);
475
+ if (!CmmmCalculations.checkValidDeposit(pool, amountsIn, scalar))
476
+ throw Error("invalid deposit");
477
+ return scalar;
478
+ }
479
+ prevR = r;
480
+ i += 1;
481
+ }
482
+ throw Error("Newton diverged");
483
+ };
484
+ CmmmCalculations.calcDepositFixedAmountsInitialEstimate = (pool, amountsIn) => {
485
+ let invariant = CmmmCalculations.calcInvariant(pool);
486
+ let coins = pool.coins;
487
+ let a = fixed_1.Fixed.directCast(pool.flatness);
307
488
  let ac = 1 - a;
308
489
  let balance;
309
490
  let weight;
310
491
  let prod = 0;
311
492
  let sum = 0;
312
493
  for (let [coinType, coin] of Object.entries(coins)) {
313
- balance = CmmmCalculations.convertFromInt(coin.balance + coinType in amountsIn
314
- ? amountsIn[coinType]
315
- : BigInt(0));
316
- weight = CmmmCalculations.directCast(coin.weight);
494
+ balance = fixed_1.Fixed.convertFromInt(coin.balance + (amountsIn[coinType] || BigInt(0)));
495
+ weight = fixed_1.Fixed.directCast(coin.weight);
317
496
  prod += weight * Math.log(balance);
318
497
  sum += weight * balance;
319
498
  }
@@ -326,36 +505,47 @@ CmmmCalculations.calcDepositFixedAmounts = (pool, amountsIn) => {
326
505
  let part1;
327
506
  let cf;
328
507
  let cfMin = 0;
508
+ let skip;
329
509
  for (let [coinType, coin] of Object.entries(coins)) {
330
- balance = CmmmCalculations.convertFromInt(coin.balance);
331
- weight = CmmmCalculations.directCast(coin.weight);
332
- amount = CmmmCalculations.convertFromInt(coinType in amountsIn ? amountsIn[coinType] : BigInt(0));
510
+ balance = fixed_1.Fixed.convertFromInt(coin.balance);
511
+ weight = fixed_1.Fixed.directCast(coin.weight);
512
+ amount = fixed_1.Fixed.convertFromInt(amountsIn[coinType] || BigInt(0));
333
513
  r = balance / (balance + amount);
334
514
  prod = 0;
335
515
  sum = 0;
516
+ skip = false;
336
517
  for (let [coinType2, coin2] of Object.entries(coins)) {
337
- balance = CmmmCalculations.convertFromInt(coin2.balance);
338
- weight = CmmmCalculations.directCast(coin2.weight);
339
- amount = CmmmCalculations.convertFromInt(coinType2 in amountsIn ? amountsIn[coinType2] : BigInt(0));
518
+ balance = fixed_1.Fixed.convertFromInt(coin2.balance);
519
+ weight = fixed_1.Fixed.directCast(coin2.weight);
520
+ amount = fixed_1.Fixed.convertFromInt(amountsIn[coinType2]);
340
521
  part1 = r * (balance + amount);
341
522
  if (part1 >= balance) {
342
523
  // r * (B0 + Din) >= B0 so use fees in
343
524
  part1 =
344
525
  balance +
345
- (1 - CmmmCalculations.directCast(coin2.tradeFeeIn)) *
526
+ (1 - fixed_1.Fixed.directCast(coin2.tradeFeeIn)) *
346
527
  (part1 - balance);
347
528
  }
348
529
  else {
349
530
  // r * (B0 + Din) < B0 so use fees out
350
531
  part1 =
351
- balance -
352
- (balance - part1) /
353
- (1 -
354
- CmmmCalculations.directCast(coin2.tradeFeeOut));
532
+ (balance - part1) /
533
+ fixed_1.Fixed.complement(fixed_1.Fixed.directCast(coin.tradeFeeOut));
534
+ if (part1 + 1 >= balance) {
535
+ skip = true;
536
+ break;
537
+ }
538
+ else {
539
+ part1 = balance - part1;
540
+ }
355
541
  }
356
542
  prod += weight * Math.log(part1);
357
543
  sum += weight * part1;
358
544
  }
545
+ if (skip) {
546
+ // this discontinuity occurs beyond draining the pool
547
+ continue;
548
+ }
359
549
  prod = Math.exp(prod);
360
550
  cf = (2 * a * prod * sum) / (prod + invariant) + ac * prod;
361
551
  if (cf <= invariant) {
@@ -378,72 +568,117 @@ CmmmCalculations.calcDepositFixedAmounts = (pool, amountsIn) => {
378
568
  ? rMin
379
569
  : (rMin * cfMax + (rMax - rMin) * invariant - rMax * cfMin) /
380
570
  (cfMax - cfMin);
381
- let prevR = r;
382
- let fees = {};
383
- for (let [coinType, coin] of Object.entries(coins)) {
384
- balance = CmmmCalculations.convertFromInt(coin.balance);
385
- amount = CmmmCalculations.convertFromInt(coinType in amountsIn ? amountsIn[coinType] : BigInt(0));
386
- fees[coinType] =
387
- r * (balance + amount) >= balance
388
- ? 1 - CmmmCalculations.directCast(coin.tradeFeeIn)
389
- : 1 / (1 - CmmmCalculations.directCast(coin.tradeFeeOut));
390
- }
391
- let i = 0;
571
+ return r;
572
+ };
573
+ // Return the expected amounts out for this withdrawal
574
+ CmmmCalculations.calcWithdrawFlpAmountsOut = (pool, amountsOutDirection, lpRatio) => {
575
+ let invariant = CmmmCalculations.calcInvariant(pool);
576
+ let coins = pool.coins;
577
+ let lpr = lpRatio;
578
+ let lpc = 1 - lpr;
579
+ let scaledInvariant = invariant * lpr;
580
+ let a = fixed_1.Fixed.directCast(pool.flatness);
581
+ let ac = 1 - a;
582
+ let i;
583
+ let prevR = 0;
584
+ let balance;
585
+ let weight;
586
+ let amountOut;
587
+ let fee;
588
+ let prod;
392
589
  let prod1;
590
+ let sum;
393
591
  let sum1;
394
- let fee;
592
+ let part1;
395
593
  let part2;
396
594
  let part3;
397
595
  let part4;
596
+ let skip;
597
+ let shrinker = 1;
598
+ let [r, rDrain] = CmmmCalculations.calcWithdrawFlpAmountsOutInitialEstimate(pool, amountsOutDirection, lpRatio);
599
+ while (shrinker >= rDrain)
600
+ shrinker /= 2;
601
+ let fees = {};
602
+ for (let [coinType, coin] of Object.entries(coins)) {
603
+ balance = fixed_1.Fixed.convertFromInt(coin.balance);
604
+ amountOut = fixed_1.Fixed.convertFromInt(amountsOutDirection[coinType] || BigInt(0));
605
+ fees[coinType] =
606
+ balance * lpc >= r * amountOut
607
+ ? 1 - fixed_1.Fixed.directCast(coin.tradeFeeIn)
608
+ : 1 / (1 - fixed_1.Fixed.directCast(coin.tradeFeeOut));
609
+ }
610
+ i = 0;
398
611
  while (i < CmmmCalculations.maxNewtonAttempts) {
399
612
  prod = 0;
400
613
  prod1 = 0;
401
614
  sum = 0;
402
615
  sum1 = 0;
616
+ skip = false;
403
617
  for (let [coinType, coin] of Object.entries(coins)) {
404
- balance = CmmmCalculations.convertFromInt(coin.balance);
405
- weight = CmmmCalculations.directCast(coin.weight);
406
- amount = CmmmCalculations.convertFromInt(coinType in amountsIn ? amountsIn[coinType] : BigInt(0));
618
+ balance = fixed_1.Fixed.convertFromInt(coin.balance);
619
+ weight = fixed_1.Fixed.directCast(coin.weight);
620
+ amountOut = fixed_1.Fixed.convertFromInt(amountsOutDirection[coinType] || BigInt(0));
407
621
  fee = fees[coinType];
408
- part1 = balance + amount;
409
- part2 = fee * r * part1 + balance - fee * balance;
410
- part3 = weight * fee * part1;
411
- prod += weight * Math.log(part2);
412
- prod1 += part3 / part2;
413
- sum += weight * part2;
414
- sum1 += part3;
622
+ part1 = balance * (lpr + lpc * fee);
623
+ part2 = fee * r * amountOut;
624
+ if (part2 + 1 >= part1) {
625
+ // Overshot and drained pool. Set t to be closer to t_max and try again.
626
+ skip = true;
627
+ break;
628
+ }
629
+ else {
630
+ part1 -= part2;
631
+ }
632
+ part2 = weight * fee * amountOut;
633
+ prod += weight * Math.log(part1);
634
+ prod1 += part2 / part1;
635
+ sum += weight * part1;
636
+ sum1 += part2;
637
+ }
638
+ if (skip) {
639
+ r = rDrain - shrinker / Math.pow(2, i);
640
+ i += 1;
641
+ continue;
415
642
  }
416
643
  prod = Math.exp(prod);
417
- part3 = a * invariant * prod1;
418
- part4 = 2 * prod1 * (a * sum + ac * prod) + 2 * a * sum1;
644
+ part1 = prod / scaledInvariant;
645
+ part2 = 2 * a * sum;
646
+ part3 = ac * (prod * part1 + 2 * prod + scaledInvariant) + part2;
647
+ part4 = part3 * prod1 + 2 * a * (part1 + 1) * sum1;
419
648
  r =
420
649
  (r * part4 +
421
- invariant * (1 + invariant / prod) -
422
- (r * part3 + 2 * a * sum + ac * (prod + invariant))) /
423
- (part4 - part3);
424
- if (utils_1.Helpers.closeEnough(r, prevR, CmmmCalculations.convergenceBound)) {
425
- return utils_1.Casting.numberToFixedBigInt(r);
650
+ part3 +
651
+ part1 * part2 -
652
+ prod -
653
+ scaledInvariant * (2 + scaledInvariant / prod)) /
654
+ part4;
655
+ if (i > 15 &&
656
+ utils_1.Helpers.closeEnough(r, prevR, CmmmCalculations.convergenceBound)) {
657
+ let returner = {};
658
+ for (let coinType of Object.keys(coins)) {
659
+ returner[coinType] = fixed_1.Fixed.convertToInt(r *
660
+ fixed_1.Fixed.convertFromInt(amountsOutDirection[coinType] || BigInt(0)));
661
+ }
662
+ if (!CmmmCalculations.checkValidWithdraw(pool, returner, lpRatio))
663
+ throw Error("invalid withdraw");
664
+ return returner;
426
665
  }
427
666
  prevR = r;
428
667
  i += 1;
429
668
  }
430
669
  throw Error("Newton diverged");
431
670
  };
432
- // Return the expected amounts out for this withdrawal
433
- CmmmCalculations.calcWithdrawFlpAmountsOut = (pool, amountsOutDirection, lpRatio) => {
434
- if (Object.keys(amountsOutDirection).length === 0)
435
- return {};
671
+ CmmmCalculations.calcWithdrawFlpAmountsOutInitialEstimate = (pool, amountsOutDirection, lpRatio) => {
436
672
  let invariant = CmmmCalculations.calcInvariant(pool);
437
673
  let coins = pool.coins;
438
674
  let lpr = lpRatio;
439
675
  let lpc = 1 - lpr;
440
676
  let scaledInvariant = invariant * lpr;
441
- let a = CmmmCalculations.directCast(pool.flatness);
677
+ let a = fixed_1.Fixed.directCast(pool.flatness);
442
678
  let ac = 1 - a;
443
- let i;
444
679
  let keepT;
680
+ let tDrain;
445
681
  let t;
446
- let prevT = 0;
447
682
  let cf;
448
683
  let tMin;
449
684
  let cfMin;
@@ -454,21 +689,18 @@ CmmmCalculations.calcWithdrawFlpAmountsOut = (pool, amountsOutDirection, lpRatio
454
689
  let amountOut;
455
690
  let fee;
456
691
  let prod;
457
- let prod1;
458
692
  let sum;
459
- let sum1;
460
693
  let part1;
461
694
  let part2;
462
695
  let part3;
463
- let part4;
464
696
  // the biggest cfMax can possibly be is f(0) which is this:
465
697
  tMax = 0;
466
698
  prod = 0;
467
699
  sum = 0;
468
700
  for (let coin of Object.values(coins)) {
469
- balance = CmmmCalculations.convertFromInt(coin.balance);
470
- weight = CmmmCalculations.directCast(coin.weight);
471
- fee = CmmmCalculations.directCast(coin.tradeFeeIn);
701
+ balance = fixed_1.Fixed.convertFromInt(coin.balance);
702
+ weight = fixed_1.Fixed.directCast(coin.weight);
703
+ fee = fixed_1.Fixed.directCast(coin.tradeFeeIn);
472
704
  part1 = balance * (1 + lpr * fee - fee);
473
705
  prod += weight * Math.log(part1);
474
706
  sum += weight * part1;
@@ -479,33 +711,31 @@ CmmmCalculations.calcWithdrawFlpAmountsOut = (pool, amountsOutDirection, lpRatio
479
711
  cfMin = 0;
480
712
  tMin = Number.POSITIVE_INFINITY;
481
713
  for (let [coinType, coin] of Object.entries(coins)) {
714
+ amountOut = fixed_1.Fixed.convertFromInt(amountsOutDirection[coinType] || BigInt(0));
715
+ if (amountOut == 0)
716
+ continue;
482
717
  t =
483
- (CmmmCalculations.convertFromInt(coin.balance) *
484
- (1 - CmmmCalculations.directCast(coin.tradeFeeOut) * lpr)) /
485
- CmmmCalculations.convertFromInt(coinType in amountsOutDirection
486
- ? amountsOutDirection[coinType]
487
- : BigInt(0));
718
+ (fixed_1.Fixed.convertFromInt(coin.balance) *
719
+ fixed_1.Fixed.complement(fixed_1.Fixed.directCast(coin.tradeFeeOut) * lpRatio)) /
720
+ amountOut;
488
721
  if (t < tMin)
489
722
  tMin = t;
490
723
  }
724
+ tDrain = tMin;
491
725
  // remaining test points are the CF discontinuities: where B0 - t*D = R*B0
492
726
  for (let [coinTypeT, coinT] of Object.entries(coins)) {
493
- amountOut = CmmmCalculations.convertFromInt(coinTypeT in amountsOutDirection
494
- ? amountsOutDirection[coinTypeT]
495
- : BigInt(0));
727
+ amountOut = fixed_1.Fixed.convertFromInt(amountsOutDirection[coinTypeT] || BigInt(0));
496
728
  if (amountOut == 0)
497
729
  continue;
498
- balance = CmmmCalculations.convertFromInt(coinT.balance);
730
+ balance = fixed_1.Fixed.convertFromInt(coinT.balance);
499
731
  t = (balance * lpc) / amountOut;
500
732
  prod = 0;
501
733
  sum = 0;
502
734
  keepT = true;
503
735
  for (let [coinType, coin] of Object.entries(coins)) {
504
- balance = CmmmCalculations.convertFromInt(coin.balance);
505
- weight = CmmmCalculations.directCast(coin.weight);
506
- amountOut = CmmmCalculations.convertFromInt(coinType in amountsOutDirection
507
- ? amountsOutDirection[coinType]
508
- : BigInt(0));
736
+ balance = fixed_1.Fixed.convertFromInt(coin.balance);
737
+ weight = fixed_1.Fixed.directCast(coin.weight);
738
+ amountOut = fixed_1.Fixed.convertFromInt(amountsOutDirection[coinType] || BigInt(0));
509
739
  part1 = t * amountOut;
510
740
  if (part1 >= balance) {
511
741
  // this t is too large to be a bound because B0 - t*D overdraws the pool
@@ -517,12 +747,11 @@ CmmmCalculations.calcWithdrawFlpAmountsOut = (pool, amountsOutDirection, lpRatio
517
747
  part3 =
518
748
  part1 >= part2
519
749
  ? part2 +
520
- (1 - CmmmCalculations.directCast(coin.tradeFeeIn)) *
750
+ fixed_1.Fixed.complement(fixed_1.Fixed.directCast(coin.tradeFeeIn)) *
521
751
  (part1 - part2)
522
752
  : part2 -
523
- (part1 - part1) /
524
- (1 -
525
- CmmmCalculations.directCast(coin.tradeFeeOut));
753
+ (part2 - part1) /
754
+ fixed_1.Fixed.complement(fixed_1.Fixed.directCast(coin.tradeFeeOut));
526
755
  prod += weight * Math.log(part3);
527
756
  sum += weight * part3;
528
757
  }
@@ -554,62 +783,581 @@ CmmmCalculations.calcWithdrawFlpAmountsOut = (pool, amountsOutDirection, lpRatio
554
783
  tMax * scaledInvariant -
555
784
  tMax * cfMin -
556
785
  tMin * scaledInvariant) /
557
- cfMax -
558
- cfMin;
559
- let fees = {};
786
+ (cfMax - cfMin);
787
+ return [t, tDrain];
788
+ };
789
+ // Dusty direct all-coin deposit, returns the number s >= 0 so that amounts_in = s*B0 + dust.
790
+ // When performing an all-coin deposit, call this function to get t then split amounts_in into s*B0 + dust.
791
+ // At least one coordinate of dust will be 0. Send the s*B0 balances into the pool and mint s*total_lp.
792
+ // The caller keeps the dust.
793
+ CmmmCalculations.calcAllCoinDeposit = (pool, amountsIn) => {
794
+ let coins = pool.coins;
795
+ let balance;
796
+ let amountIn;
797
+ let s;
798
+ let sMin = Number.POSITIVE_INFINITY;
560
799
  for (let [coinType, coin] of Object.entries(coins)) {
561
- balance = CmmmCalculations.convertFromInt(coin.balance);
562
- amountOut = CmmmCalculations.convertFromInt(coinType in amountsOutDirection
563
- ? amountsOutDirection[coinType]
564
- : BigInt(0));
565
- fees[coinType] =
566
- balance * lpc >= t * amountOut
567
- ? 1 - CmmmCalculations.directCast(coin.tradeFeeIn)
568
- : 1 / (1 - CmmmCalculations.directCast(coin.tradeFeeOut));
800
+ balance = fixed_1.Fixed.convertFromInt(coin.balance);
801
+ amountIn = fixed_1.Fixed.convertFromInt(amountsIn[coinType] || BigInt(0));
802
+ s = amountIn / balance;
803
+ if (s < sMin)
804
+ sMin = s;
569
805
  }
570
- i = 0;
806
+ let returner = {};
807
+ for (let coinType of Object.keys(coins))
808
+ returner[coinType] = utils_1.Helpers.blendedOperations.mulNBB(sMin, amountsIn[coinType] || BigInt(0));
809
+ return returner;
810
+ };
811
+ // Dusty direct all-coin withdraw, returns the number s >= 0 so that amounts_out + dust = s*B0.
812
+ // The normal all-coin withdraw (take this exact amount of lp and give however much balances out)
813
+ // should be done directly without this function -- just burn the lp and give the user
814
+ // lp/total_lp * balance_i in each coordinate. This function is for finding how much lp it takes to
815
+ // ensure that at least amounts_out comes out.
816
+ CmmmCalculations.calcAllCoinWithdraw = (pool, amountsOut) => {
817
+ let coins = pool.coins;
818
+ let balance;
819
+ let amountOut;
820
+ let s;
821
+ let sMax = 0;
822
+ for (let [coinType, coin] of Object.entries(coins)) {
823
+ balance = fixed_1.Fixed.convertFromInt(coin.balance);
824
+ amountOut = fixed_1.Fixed.convertFromInt(amountsOut[coinType] || BigInt(0));
825
+ s = amountOut / balance;
826
+ if (s > sMax)
827
+ sMax = s;
828
+ }
829
+ let returner = {};
830
+ for (let coinType of Object.keys(coins))
831
+ returner[coinType] = utils_1.Helpers.blendedOperations.mulNBB(sMax, amountsOut[coinType] || BigInt(0));
832
+ return returner;
833
+ };
834
+ // This function calculates the balance of a given token (index) given all the other balances (combined in p0, s0)
835
+ // and the invariant along with an initial estimate. It is useful for 1d optimization.
836
+ CmmmCalculations.getTokenBalanceGivenInvariantAndAllOtherBalances = (flatness, w, h, xi, // initial estimate -- default can be (P(X) / p0)^n
837
+ p0, // P(B) / xi^(1/n) (everything but the missing part)
838
+ s0 // S(B) - xi / n (everything but the missing part)
839
+ ) => {
840
+ // Standard Newton method used here
841
+ // ---------------- setting constants ----------------
842
+ // c1 = 2*A*w*w
843
+ // c2 = 2*(1-A)*w*p0
844
+ // c3 = A*(2*w*s0+t)
845
+ // c4 = t*t/p0
846
+ // c5 = (1-A)*p0
847
+ // c6 = A*(2*s0+w*t)
848
+ // c7 = 2*A*w*(w+1)
849
+ // c8 = 2*(1-A)*p0
850
+ // c9 = 2*A*w*s0
851
+ // c10= A*w*t
852
+ let ac = 1 - flatness;
853
+ let aw = flatness * w;
854
+ let acw = ac * w;
855
+ let as0 = flatness * s0;
856
+ let ah = flatness * h;
857
+ let c1 = 2 * aw * w;
858
+ let c2 = 2 * acw * p0;
859
+ let c3 = 2 * w * as0 + ah;
860
+ let c4 = (h * h) / p0;
861
+ let c5 = ac * p0;
862
+ let c6 = 2 * as0 + w * ah;
863
+ let c7 = 2 * aw * (w + 1);
864
+ let c8 = 2 * acw * p0;
865
+ let c9 = 2 * aw * s0;
866
+ let c10 = aw * h;
867
+ // ---------------- iterating ----------------
868
+ //x = (
869
+ // x * (
870
+ // (
871
+ // x^w * (
872
+ // c1 * x + c2 * x^w + c3
873
+ // ) + c4
874
+ // ) - x^w * (
875
+ // c5 * x^w + c6
876
+ // )
877
+ // )
878
+ //) / (
879
+ // x^w * (
880
+ // (
881
+ // c7 * x + c8 * x^w + c9
882
+ // ) - c10
883
+ // )
884
+ //)
885
+ let x = xi;
886
+ let xw; // x^w
887
+ let topPos;
888
+ let topNeg;
889
+ let bottomPos;
890
+ //let bottomNeg;
891
+ let prevX = x;
892
+ let i = 0;
571
893
  while (i < CmmmCalculations.maxNewtonAttempts) {
572
- prod = 0;
573
- prod1 = 0;
574
- sum = 0;
575
- sum1 = 0;
576
- for (let [coinType, coin] of Object.entries(coins)) {
577
- balance = CmmmCalculations.convertFromInt(coin.balance);
578
- weight = CmmmCalculations.directCast(coin.weight);
579
- amountOut = CmmmCalculations.convertFromInt(coinType in amountsOutDirection
580
- ? amountsOutDirection[coinType]
581
- : BigInt(0));
582
- fee = fees[coinType];
583
- part1 = balance * (lpr + lpc * fee) - fee * t * amountOut;
584
- part2 = weight * fee * amountOut;
585
- prod += weight * Math.log(part1);
586
- prod1 += part2 / part1;
587
- sum += weight * part1;
588
- sum1 += part2;
894
+ xw = Math.pow(x, w);
895
+ topPos = x * (xw * (c1 * x + c2 * xw + c3) + c4);
896
+ topNeg = x * (xw * (c5 * xw + c6));
897
+ bottomPos = c7 * x + c8 * xw + c9;
898
+ //bottomNeg = c10;
899
+ // If x jumps too much (bad initial estimate) then g(x) might overshoot into a negative number.
900
+ // This only happens if x is supposed to be small. In this case, replace x with a small number and try again.
901
+ // Once x is close enough to the true value g(x) won't overshoot anymore and this test will be skipped from then on.
902
+ if (topPos < topNeg || bottomPos < c10) {
903
+ x = 1 / Math.pow(2, i);
904
+ i = i + 1;
905
+ continue;
589
906
  }
590
- prod = Math.exp(prod);
591
- part1 = prod / scaledInvariant;
592
- part2 = 2 * a * sum;
593
- part3 = ac * (prod * part1 + 2 * prod + scaledInvariant) + part2;
594
- part4 = part3 * prod1 + 2 * a * (part1 + 1) * sum1;
595
- t =
596
- (t * part4 +
597
- part3 +
598
- part1 * part2 -
599
- prod -
600
- scaledInvariant * (2 + scaledInvariant / prod)) /
601
- part4;
602
- if (utils_1.Helpers.closeEnough(t, prevT, CmmmCalculations.withdrawConvergenceBound)) {
603
- let returner = {};
604
- for (let coinType of Object.keys(coins)) {
605
- returner[coinType] = utils_1.Casting.scaleNumberByBigInt(t, coinType in amountsOutDirection
606
- ? amountsOutDirection[coinType]
607
- : BigInt(0));
608
- }
609
- return returner;
907
+ x = (topPos - topNeg) / (xw * (bottomPos - c10));
908
+ // using relative error here (easier to pass) because js numbers are less precise
909
+ if (utils_1.Helpers.closeEnough(x, prevX, CmmmCalculations.convergenceBound)) {
910
+ return x;
610
911
  }
611
- prevT = t;
612
- i += 1;
912
+ prevX = x;
913
+ i = i + 1;
613
914
  }
614
915
  throw Error("Newton diverged");
615
916
  };
917
+ // Compute the invariant before swap and pseudoinvariant (invariant considering fees)
918
+ // after the swap and see if they are the same up to a tolerance.
919
+ // It also checks that this balance does not drain the pool i.e. the final balance is at least 1.
920
+ // The scalars are here to avoid unnecessary vector creation. In most calls one scalar will be 10^18 (1).
921
+ CmmmCalculations.checkValidSwap = (pool, amountsIn, amountsInScalar, amountsOut, amountsOutScalar) => {
922
+ let coins = pool.coins;
923
+ let flatness = fixed_1.Fixed.directCast(pool.flatness);
924
+ // balance = balances[i]
925
+ let balance;
926
+ // pseudobalance = balance + feedAmountIn - feedAmountOut
927
+ let pseudobalance;
928
+ // postbalance = balance + amountIn - amountOut
929
+ let postbalance;
930
+ let weight;
931
+ let amountIn;
932
+ let amountOut;
933
+ let feedAmountIn;
934
+ let feedAmountOut;
935
+ let preprod = 0;
936
+ let presum = 0;
937
+ let pseudoprod = 0;
938
+ let pseudosum = 0;
939
+ let postprod = 0;
940
+ let postsum = 0;
941
+ for (let [coinType, coin] of Object.entries(coins)) {
942
+ balance = fixed_1.Fixed.convertFromInt(coin.balance);
943
+ weight = fixed_1.Fixed.directCast(coin.weight);
944
+ amountIn =
945
+ fixed_1.Fixed.convertFromInt(amountsIn[coinType] || BigInt(0)) *
946
+ amountsInScalar;
947
+ amountOut =
948
+ fixed_1.Fixed.convertFromInt(amountsOut[coinType] || BigInt(0)) *
949
+ amountsOutScalar;
950
+ if (amountIn > 0 && amountOut > 0)
951
+ return false;
952
+ feedAmountIn = amountIn * (1 - fixed_1.Fixed.directCast(coin.tradeFeeIn));
953
+ feedAmountOut =
954
+ amountOut == 0
955
+ ? 0
956
+ : amountOut / (1 - fixed_1.Fixed.directCast(coin.tradeFeeOut));
957
+ postbalance = balance + amountIn;
958
+ if (amountOut > postbalance + 1)
959
+ return false;
960
+ postbalance -= -amountOut;
961
+ pseudobalance = balance + feedAmountIn;
962
+ if (feedAmountOut > pseudobalance + 1)
963
+ return false;
964
+ pseudobalance -= -feedAmountOut;
965
+ preprod += weight * Math.log(balance);
966
+ presum += weight * balance;
967
+ postprod += weight * Math.log(postbalance);
968
+ postsum += weight * postbalance;
969
+ pseudoprod += weight * Math.log(pseudobalance);
970
+ pseudosum += weight * pseudobalance;
971
+ }
972
+ preprod = Math.exp(preprod);
973
+ postprod = Math.exp(postprod);
974
+ pseudoprod = Math.exp(pseudoprod);
975
+ let preinvariant = CmmmCalculations.calcInvariantQuadratic(preprod, presum, flatness);
976
+ let postinvariant = CmmmCalculations.calcInvariantQuadratic(postprod, postsum, flatness);
977
+ let pseudoinvariant = CmmmCalculations.calcInvariantQuadratic(pseudoprod, pseudosum, flatness);
978
+ return (postinvariant >= preinvariant &&
979
+ (utils_1.Helpers.veryCloseInt(preinvariant, pseudoinvariant, fixed_1.Fixed.fixedOneN) ||
980
+ utils_1.Helpers.closeEnough(preinvariant, pseudoinvariant, CmmmCalculations.validityTolerance)));
981
+ };
982
+ // Compute the invariant before swap and pseudoinvariant (invariant considering fees)
983
+ // after the swap and see if they are the same up to a tolerance.
984
+ // It also checks that this balance does not drain the pool i.e. the final balance is at least 1.
985
+ CmmmCalculations.checkValid1dSwap = (pool, coinTypeIn, coinTypeOut, amountInB, amountOutB) => {
986
+ if (coinTypeIn == coinTypeOut)
987
+ return false;
988
+ let coins = pool.coins;
989
+ let flatness = fixed_1.Fixed.directCast(pool.flatness);
990
+ // balance = balances[i]
991
+ let balance;
992
+ // pseudobalance = balance + feed amount in - feed amount out
993
+ let pseudobalance;
994
+ // postbalance = balance + amount in - amount out
995
+ let postbalance;
996
+ let weight;
997
+ let amountIn = fixed_1.Fixed.convertFromInt(amountInB);
998
+ let amountOut = fixed_1.Fixed.convertFromInt(amountOutB);
999
+ let feedAmountIn = amountIn * (1 - fixed_1.Fixed.directCast(coins[coinTypeIn].tradeFeeIn));
1000
+ let feedAmountOut = amountOut == 0
1001
+ ? 0
1002
+ : amountOut /
1003
+ (1 - fixed_1.Fixed.directCast(coins[coinTypeOut].tradeFeeOut));
1004
+ let preprod = 0;
1005
+ let presum = 0;
1006
+ let pseudoprod = 0;
1007
+ let pseudosum = 0;
1008
+ let postprod = 0;
1009
+ let postsum = 0;
1010
+ let p;
1011
+ let s;
1012
+ for (let [coinType, coin] of Object.entries(coins)) {
1013
+ balance = fixed_1.Fixed.convertFromInt(coin.balance);
1014
+ weight = fixed_1.Fixed.directCast(coin.weight);
1015
+ p = weight * Math.log(balance);
1016
+ s = weight * balance;
1017
+ preprod += p;
1018
+ presum += s;
1019
+ if (coinType == coinTypeIn) {
1020
+ pseudobalance = balance + feedAmountIn;
1021
+ postbalance = balance + amountIn;
1022
+ pseudoprod += weight * Math.log(pseudobalance);
1023
+ pseudosum += weight * pseudobalance;
1024
+ postprod += weight * Math.log(postbalance);
1025
+ postsum += weight * postbalance;
1026
+ }
1027
+ else {
1028
+ if (coinType == coinTypeOut) {
1029
+ if (feedAmountOut > balance + 1 || amountOut > balance + 1)
1030
+ return false;
1031
+ pseudobalance = balance - feedAmountOut;
1032
+ postbalance = balance - amountOut;
1033
+ pseudoprod += weight * Math.log(pseudobalance);
1034
+ pseudosum += weight * pseudobalance;
1035
+ postprod += weight * Math.log(postbalance);
1036
+ postsum += weight * postbalance;
1037
+ }
1038
+ else {
1039
+ pseudoprod += p;
1040
+ pseudosum += s;
1041
+ postprod += p;
1042
+ postsum += s;
1043
+ }
1044
+ }
1045
+ }
1046
+ preprod = Math.exp(preprod);
1047
+ postprod = Math.exp(postprod);
1048
+ pseudoprod = Math.exp(pseudoprod);
1049
+ let preinvariant = CmmmCalculations.calcInvariantQuadratic(preprod, presum, flatness);
1050
+ let postinvariant = CmmmCalculations.calcInvariantQuadratic(postprod, postsum, flatness);
1051
+ let pseudoinvariant = CmmmCalculations.calcInvariantQuadratic(pseudoprod, pseudosum, flatness);
1052
+ return (postinvariant >= preinvariant &&
1053
+ (utils_1.Helpers.veryCloseInt(preinvariant, pseudoinvariant, fixed_1.Fixed.fixedOneN) ||
1054
+ utils_1.Helpers.closeEnough(preinvariant, pseudoinvariant, CmmmCalculations.validityTolerance)));
1055
+ };
1056
+ // A fixed amount investment is a swap followed by an all coin investment. This function checks that the
1057
+ // intermediate swap is allowed and corresponds to the claimed lp ratio.
1058
+ CmmmCalculations.checkValidDeposit = (pool, amountsIn, lpRatioRaw) => {
1059
+ // The supposed swap is from B0 to R*(B0 + Din)
1060
+ // This test is check_valid_swap for those data
1061
+ let coins = pool.coins;
1062
+ let lpRatio = fixed_1.Fixed.directCast(lpRatioRaw);
1063
+ if (lpRatio > 1)
1064
+ return false;
1065
+ let flatness = fixed_1.Fixed.directCast(pool.flatness);
1066
+ // balance = balances[i]
1067
+ let balance;
1068
+ let weight;
1069
+ // amount = amountsIn[i]
1070
+ let amount;
1071
+ // postbalance = lpRatio * (balance + amount)
1072
+ let postbalance;
1073
+ // pseudobalance = fee(postbalance - balance) + balance
1074
+ let pseudobalance;
1075
+ // diff = postbalance - balance
1076
+ let diff;
1077
+ // pseudodiff = fee(diff)
1078
+ let pseudodiff;
1079
+ let preprod = 0;
1080
+ let presum = 0;
1081
+ let pseudoprod = 0;
1082
+ let pseudosum = 0;
1083
+ let postprod = 0;
1084
+ let postsum = 0;
1085
+ for (let [coinType, coin] of Object.entries(coins)) {
1086
+ balance = fixed_1.Fixed.convertFromInt(coin.balance);
1087
+ weight = fixed_1.Fixed.directCast(coin.weight);
1088
+ amount = fixed_1.Fixed.convertFromInt(amountsIn[coinType] || BigInt(0));
1089
+ postbalance = lpRatio * (balance + amount);
1090
+ if (postbalance >= balance) {
1091
+ // use fee in
1092
+ diff = postbalance - balance;
1093
+ pseudodiff = diff * (1 - fixed_1.Fixed.directCast(coin.tradeFeeIn));
1094
+ pseudobalance = balance + pseudodiff;
1095
+ }
1096
+ else {
1097
+ // use fee out
1098
+ diff = balance - postbalance;
1099
+ pseudodiff =
1100
+ diff == 0
1101
+ ? 0
1102
+ : diff / (1 - fixed_1.Fixed.directCast(coin.tradeFeeOut));
1103
+ if (pseudodiff >= balance + 1)
1104
+ return false;
1105
+ pseudobalance = balance - pseudodiff;
1106
+ }
1107
+ preprod += weight * Math.log(balance);
1108
+ presum += weight * balance;
1109
+ postprod += weight * Math.log(postbalance);
1110
+ postsum += weight * postbalance;
1111
+ pseudoprod += weight * Math.log(pseudobalance);
1112
+ pseudosum += weight * pseudobalance;
1113
+ }
1114
+ preprod = Math.exp(preprod);
1115
+ postprod = Math.exp(postprod);
1116
+ pseudoprod = Math.exp(pseudoprod);
1117
+ let preinvariant = CmmmCalculations.calcInvariantQuadratic(preprod, presum, flatness);
1118
+ let postinvariant = CmmmCalculations.calcInvariantQuadratic(postprod, postsum, flatness);
1119
+ let pseudoinvariant = CmmmCalculations.calcInvariantQuadratic(pseudoprod, pseudosum, flatness);
1120
+ return (postinvariant >= preinvariant &&
1121
+ (utils_1.Helpers.veryCloseInt(preinvariant, pseudoinvariant, fixed_1.Fixed.fixedOneN) ||
1122
+ utils_1.Helpers.closeEnough(preinvariant, pseudoinvariant, CmmmCalculations.validityTolerance)));
1123
+ };
1124
+ // A fixed lp withdraw is an all coin withdraw followed by a swap.
1125
+ // This function checks that the swap is valid.
1126
+ CmmmCalculations.checkValidWithdraw = (pool, amountsOutSrc, lpRatio) => {
1127
+ // Check that the swap from R*B0 to B0 - Dout is valid
1128
+ let coins = pool.coins;
1129
+ if (lpRatio > 1)
1130
+ return false;
1131
+ let flatness = fixed_1.Fixed.directCast(pool.flatness);
1132
+ // balance = balances[i]
1133
+ let balance;
1134
+ let weight;
1135
+ // amount is scaled amounts out at i
1136
+ let amount;
1137
+ // scaledBalance = lpRatio * balance
1138
+ let scaledBalance;
1139
+ // postbalance = balance - amount
1140
+ let postbalance;
1141
+ // pseudobalance is postbalance but considering fees
1142
+ let pseudobalance;
1143
+ let diff;
1144
+ let pseudodiff;
1145
+ let preprod = 0;
1146
+ let presum = 0;
1147
+ let pseudoprod = 0;
1148
+ let pseudosum = 0;
1149
+ let postprod = 0;
1150
+ let postsum = 0;
1151
+ for (let [coinType, coin] of Object.entries(coins)) {
1152
+ balance = fixed_1.Fixed.convertFromInt(coin.balance);
1153
+ scaledBalance = lpRatio * balance;
1154
+ weight = fixed_1.Fixed.directCast(coin.weight);
1155
+ amount = fixed_1.Fixed.convertFromInt(amountsOutSrc[coinType] || BigInt(0));
1156
+ if (amount > scaledBalance + 1)
1157
+ return false;
1158
+ postbalance = balance - amount;
1159
+ if (postbalance >= scaledBalance) {
1160
+ // use fee in
1161
+ diff = postbalance - scaledBalance;
1162
+ pseudodiff =
1163
+ diff * fixed_1.Fixed.complement(fixed_1.Fixed.directCast(coin.tradeFeeIn));
1164
+ pseudobalance = scaledBalance + pseudodiff;
1165
+ }
1166
+ else {
1167
+ // use fee out
1168
+ diff = scaledBalance - postbalance;
1169
+ pseudodiff =
1170
+ diff == 0
1171
+ ? 0
1172
+ : diff /
1173
+ fixed_1.Fixed.complement(fixed_1.Fixed.directCast(coin.tradeFeeOut));
1174
+ if (pseudodiff > scaledBalance + 1)
1175
+ return false;
1176
+ pseudobalance = scaledBalance - pseudodiff;
1177
+ }
1178
+ preprod += weight * Math.log(scaledBalance);
1179
+ presum += weight * scaledBalance;
1180
+ postprod += weight * Math.log(postbalance);
1181
+ postsum += weight * postbalance;
1182
+ pseudoprod += weight * Math.log(pseudobalance);
1183
+ pseudosum += weight * pseudobalance;
1184
+ }
1185
+ preprod = Math.exp(preprod);
1186
+ postprod = Math.exp(postprod);
1187
+ pseudoprod = Math.exp(pseudoprod);
1188
+ let preinvariant = CmmmCalculations.calcInvariantQuadratic(preprod, presum, flatness);
1189
+ let postinvariant = CmmmCalculations.calcInvariantQuadratic(postprod, postsum, flatness);
1190
+ let pseudoinvariant = CmmmCalculations.calcInvariantQuadratic(pseudoprod, pseudosum, flatness);
1191
+ return (postinvariant >= preinvariant &&
1192
+ (utils_1.Helpers.veryCloseInt(preinvariant, pseudoinvariant, fixed_1.Fixed.fixedOneN) ||
1193
+ utils_1.Helpers.closeEnough(preinvariant, pseudoinvariant, CmmmCalculations.validityTolerance)));
1194
+ };
1195
+ // get an estimate for outGivenIn based on the spot price
1196
+ CmmmCalculations.getEstimateOutGivenIn = (pool, coinTypeIn, coinTypeOut, amountIn) => utils_1.Helpers.blendedOperations.mulNBB(CmmmCalculations.calcSpotPriceWithFees(pool, coinTypeIn, coinTypeOut), amountIn);
1197
+ // get an estimate for inGivenOut based on the spot price
1198
+ CmmmCalculations.getEstimateInGivenOut = (pool, coinTypeIn, coinTypeOut, amountOut) => utils_1.Helpers.blendedOperations.mulNBB(1 /
1199
+ CmmmCalculations.calcSpotPriceWithFees(pool, coinTypeIn, coinTypeOut), amountOut);
1200
+ // get an estimate for swapFixedIn using the spot prices
1201
+ // returns t > 0 such that t*amountsOutDirection agrees with amountsIn wrt spot prices
1202
+ CmmmCalculations.getEstimateSwapFixedIn = (pool, amountsIn, amountsOutDirection) => {
1203
+ // find t such that Ain + t*Aout lies in the tangent plane to the swap surface at balances in the given directions
1204
+ // the gradient of the invariant function with fees is (with spot body E)
1205
+ // Win * (1 - Sin) * (E + 2*A * Bin) / Bin or
1206
+ // Wout * (E + 2*A * Bout) / (1-Sout) * Bout
1207
+ // depending on whether the balance is coming in or going out
1208
+ let coins = pool.coins;
1209
+ let spotBody = CmmmCalculations.calcSpotPriceBody(pool);
1210
+ let a = fixed_1.Fixed.directCast(pool.flatness);
1211
+ let balance;
1212
+ let grad;
1213
+ let amountIn;
1214
+ let amountOut;
1215
+ let inDotGrad = 0;
1216
+ let outDotGrad = 0;
1217
+ for (let [coinType, coin] of Object.entries(coins)) {
1218
+ balance = fixed_1.Fixed.convertFromInt(coin.balance);
1219
+ amountIn = fixed_1.Fixed.convertFromInt(amountsIn[coinType] || BigInt(0));
1220
+ amountOut = fixed_1.Fixed.convertFromInt(amountsOutDirection[coinType] || BigInt(0));
1221
+ grad =
1222
+ amountIn == 0
1223
+ ? (fixed_1.Fixed.directCast(coin.weight) *
1224
+ (spotBody + 2 * a * balance)) /
1225
+ (balance * (1 - fixed_1.Fixed.directCast(coin.tradeFeeOut)))
1226
+ : (fixed_1.Fixed.directCast(coin.weight) *
1227
+ (1 - fixed_1.Fixed.directCast(coin.tradeFeeIn)) *
1228
+ (spotBody + 2 * a * balance)) /
1229
+ balance;
1230
+ inDotGrad += amountIn * grad;
1231
+ outDotGrad += amountOut * grad;
1232
+ }
1233
+ return inDotGrad / outDotGrad;
1234
+ };
1235
+ // get an estimate for swapFixedOut using the spot prices
1236
+ // returns t > 0 such that t*amountsInDirection agrees with amountsOut wrt spot prices
1237
+ CmmmCalculations.getEstimateSwapFixedOut = (pool, amountsInDirection, amountsOut) => {
1238
+ // find t such that Ain + t*Aout lies in the tangent plane to the swap surface at balances in the given directions
1239
+ // the gradient of the invariant function with fees is (with spot body E)
1240
+ // Win * (1 - Sin) * (E + 2*A * Bin) / Bin or
1241
+ // Wout * (E + 2*A * Bout) / (1-Sout) * Bout
1242
+ // depending on whether the balance is coming in or going out
1243
+ let coins = pool.coins;
1244
+ let spotBody = CmmmCalculations.calcSpotPriceBody(pool);
1245
+ let a = fixed_1.Fixed.directCast(pool.flatness);
1246
+ let balance;
1247
+ let grad;
1248
+ let amountIn;
1249
+ let amountOut;
1250
+ let inDotGrad = 0;
1251
+ let outDotGrad = 0;
1252
+ for (let [coinType, coin] of Object.entries(coins)) {
1253
+ balance = fixed_1.Fixed.convertFromInt(coin.balance);
1254
+ amountIn = fixed_1.Fixed.convertFromInt(amountsInDirection[coinType] || BigInt(0));
1255
+ amountOut = fixed_1.Fixed.convertFromInt(amountsOut[coinType] || BigInt(0));
1256
+ grad =
1257
+ amountIn == 0
1258
+ ? (fixed_1.Fixed.directCast(coin.weight) *
1259
+ (spotBody + 2 * a * balance)) /
1260
+ (balance * (1 - fixed_1.Fixed.directCast(coin.tradeFeeOut)))
1261
+ : (fixed_1.Fixed.directCast(coin.weight) *
1262
+ (1 - fixed_1.Fixed.directCast(coin.tradeFeeIn)) *
1263
+ (spotBody + 2 * a * balance)) /
1264
+ balance;
1265
+ inDotGrad += amountIn * grad;
1266
+ outDotGrad += amountOut * grad;
1267
+ }
1268
+ return outDotGrad / inDotGrad;
1269
+ };
1270
+ // Calculate an estimate for lpRatio using the spot price (linear estiamtion)
1271
+ // This estimation will be very good for small values in amountsIn
1272
+ CmmmCalculations.getEstimateDepositFixedAmounts = (pool, amountsIn) => {
1273
+ // Initial estimate comes from testing the discontinuities and doing a linear
1274
+ // approximation off the two closest test points. We use it to get the correct fees.
1275
+ let r0 = CmmmCalculations.calcDepositFixedAmountsInitialEstimate(pool, amountsIn);
1276
+ // Now r0 is on the correct side of B0 as the final t*(B0+Din). This tells us which fees apply.
1277
+ // All we have to do is find the value of r for which r*(B0+Din) lies on the feed tangent plane at B0.
1278
+ // the gradient of the invariant function with fees is (with spot body E)
1279
+ // Win * (1 - Sin) * (E + 2*A * Bin) / Bin or
1280
+ // Wout * (E + 2*A * Bout) / (1-Sout) * Bout
1281
+ // depending on whether the balance is coming in or going out
1282
+ let coins = pool.coins;
1283
+ let spotBody = CmmmCalculations.calcSpotPriceBody(pool);
1284
+ let a = fixed_1.Fixed.directCast(pool.flatness);
1285
+ // dot(B0, g)
1286
+ let d1 = 0;
1287
+ // dot(B0 + Din, g)
1288
+ let d2 = 0;
1289
+ let balance;
1290
+ let weight;
1291
+ let amount;
1292
+ let grad;
1293
+ let scaledAmount;
1294
+ for (let [coinType, coin] of Object.entries(coins)) {
1295
+ balance = fixed_1.Fixed.convertFromInt(coin.balance);
1296
+ weight = fixed_1.Fixed.directCast(coin.weight);
1297
+ amount =
1298
+ balance +
1299
+ fixed_1.Fixed.convertFromInt(amountsIn[coinType] || BigInt(0));
1300
+ scaledAmount = amount * r0;
1301
+ grad =
1302
+ scaledAmount < balance
1303
+ ? // use amount out
1304
+ (weight * (spotBody + 2 * a * balance)) /
1305
+ (balance * (1 - fixed_1.Fixed.directCast(coin.tradeFeeOut)))
1306
+ : // use amount in
1307
+ (weight *
1308
+ (1 - fixed_1.Fixed.directCast(coin.tradeFeeIn)) *
1309
+ (spotBody + 2 * a * balance)) /
1310
+ balance;
1311
+ d1 += balance * grad;
1312
+ d2 += amount * grad;
1313
+ }
1314
+ return d1 / d2;
1315
+ };
1316
+ // Calculate an estimate for amountsOut using the spot price (linear estiamtion)
1317
+ // This estimation will be very good for lpRatios close to 1
1318
+ // Since we still need the out vector for its direction we return t s.t. t*amountsOutDirection is the estimate.
1319
+ CmmmCalculations.getEstimateWithdrawFlpAmountsOut = (pool, amountsOutDirection, lpRatio) => {
1320
+ // Initial estimate comes from testing the discontinuities and doing a linear
1321
+ // approximation off the two closest test points. We use it to get the correct fees.
1322
+ let [r0, _rDrain] = CmmmCalculations.calcWithdrawFlpAmountsOutInitialEstimate(pool, amountsOutDirection, lpRatio);
1323
+ // Now r0 is on the correct side of R*B0 as the final B0-t*Deout. This tells us which fees apply.
1324
+ // All we have to do is find the value of t for which B0-t*Deout lies on the feed tangent plane at R*B0.
1325
+ // the gradient of the invariant function with fees is (with spot body E)
1326
+ // Win * (1 - Sin) * (E + 2*A * Bin) / Bin or
1327
+ // Wout * (E + 2*A * Bout) / (1-Sout) * Bout
1328
+ // depending on whether the balance is coming in or going out
1329
+ let coins = pool.coins;
1330
+ // Swap center is R*B0, not B0. Luckily the spot body formula is homogeneous.
1331
+ let spotBody = CmmmCalculations.calcSpotPriceBody(pool) * lpRatio;
1332
+ let a = fixed_1.Fixed.directCast(pool.flatness);
1333
+ // dot(B0, g)
1334
+ let d1 = 0;
1335
+ // dot(Deout, g)
1336
+ let d2 = 0;
1337
+ let balance;
1338
+ let scaledAmount;
1339
+ let weight;
1340
+ let amount;
1341
+ let grad;
1342
+ for (let [coinType, coin] of Object.entries(coins)) {
1343
+ balance = fixed_1.Fixed.convertFromInt(coin.balance);
1344
+ weight = fixed_1.Fixed.directCast(coin.weight);
1345
+ amount =
1346
+ balance +
1347
+ fixed_1.Fixed.convertFromInt(amountsOutDirection[coinType] || BigInt(0));
1348
+ scaledAmount = amount * r0;
1349
+ grad =
1350
+ scaledAmount < balance
1351
+ ? // use amount out
1352
+ (weight * (spotBody + 2 * a * balance)) /
1353
+ (balance * (1 - fixed_1.Fixed.directCast(coin.tradeFeeOut)))
1354
+ : // use amount in
1355
+ (weight *
1356
+ (1 - fixed_1.Fixed.directCast(coin.tradeFeeIn)) *
1357
+ (spotBody + 2 * a * balance)) /
1358
+ balance;
1359
+ d1 += balance * grad;
1360
+ d2 += amount * grad;
1361
+ }
1362
+ return ((1 - lpRatio) * d1) / d2;
1363
+ };