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.
- package/dist/general/utils/fixed.d.ts +15 -0
- package/dist/general/utils/fixed.d.ts.map +1 -0
- package/dist/general/utils/fixed.js +15 -0
- package/dist/general/utils/helpers.d.ts +9 -0
- package/dist/general/utils/helpers.d.ts.map +1 -1
- package/dist/general/utils/helpers.js +9 -0
- package/dist/packages/pools/api/poolsApiCasting.d.ts.map +1 -1
- package/dist/packages/pools/api/poolsApiCasting.js +7 -7
- package/dist/packages/pools/api/poolsApiHelpers.js +1 -1
- package/dist/packages/pools/utils/cmmmCalculations.d.ts +22 -8
- package/dist/packages/pools/utils/cmmmCalculations.d.ts.map +1 -1
- package/dist/packages/pools/utils/cmmmCalculations.js +1009 -261
- package/dist/packages/router/utils/synchronous/routerGraph.d.ts.map +1 -1
- package/dist/packages/router/utils/synchronous/routerGraph.js +1 -13
- package/dist/packages/router/utils/synchronous/routerPools/aftermathRouterPool.d.ts.map +1 -1
- package/dist/packages/router/utils/synchronous/routerPools/aftermathRouterPool.js +26 -8
- package/dist/packages/staking/api/stakingApi.d.ts +4 -1
- package/dist/packages/staking/api/stakingApi.d.ts.map +1 -1
- package/dist/packages/staking/api/stakingApi.js +3 -0
- package/dist/packages/staking/api/stakingApiCasting.d.ts.map +1 -1
- package/dist/packages/staking/api/stakingApiCasting.js +0 -1
- package/dist/packages/staking/api/stakingApiCastingTypes.d.ts +0 -1
- package/dist/packages/staking/api/stakingApiCastingTypes.d.ts.map +1 -1
- package/dist/packages/staking/api/stakingApiHelpers.d.ts +2 -0
- package/dist/packages/staking/api/stakingApiHelpers.d.ts.map +1 -1
- package/dist/packages/staking/api/stakingApiHelpers.js +12 -0
- package/dist/packages/staking/staking.d.ts +2 -1
- package/dist/packages/staking/staking.d.ts.map +1 -1
- package/dist/packages/staking/staking.js +5 -0
- package/dist/packages/staking/stakingTypes.d.ts +3 -2
- package/dist/packages/staking/stakingTypes.d.ts.map +1 -1
- 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.
|
|
30
|
-
|
|
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 =
|
|
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 =
|
|
53
|
-
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 =
|
|
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 =
|
|
87
|
-
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
|
-
//
|
|
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 =
|
|
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 =
|
|
198
|
-
let balanceOut =
|
|
199
|
-
let weightIn =
|
|
200
|
-
let weightOut =
|
|
201
|
-
let swapFeeIn = ignoreFees
|
|
202
|
-
|
|
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 =
|
|
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 =
|
|
226
|
-
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 =
|
|
241
|
-
let swapFeeOut =
|
|
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 =
|
|
247
|
-
let oldIn =
|
|
248
|
-
let oldOut =
|
|
249
|
-
let wIn =
|
|
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) *
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
273
|
-
let swapFeeOut =
|
|
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 =
|
|
281
|
-
let oldIn =
|
|
282
|
-
let oldOut =
|
|
283
|
-
let wOut =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
314
|
-
|
|
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 =
|
|
331
|
-
weight =
|
|
332
|
-
amount =
|
|
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 =
|
|
338
|
-
weight =
|
|
339
|
-
amount =
|
|
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 -
|
|
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
|
-
(
|
|
353
|
-
|
|
354
|
-
|
|
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
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
let
|
|
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
|
|
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 =
|
|
405
|
-
weight =
|
|
406
|
-
|
|
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 +
|
|
409
|
-
part2 = fee * r *
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
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
|
-
|
|
418
|
-
|
|
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
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
470
|
-
weight =
|
|
471
|
-
fee =
|
|
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
|
-
(
|
|
484
|
-
(
|
|
485
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
505
|
-
weight =
|
|
506
|
-
amountOut =
|
|
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
|
-
(
|
|
750
|
+
fixed_1.Fixed.complement(fixed_1.Fixed.directCast(coin.tradeFeeIn)) *
|
|
521
751
|
(part1 - part2)
|
|
522
752
|
: part2 -
|
|
523
|
-
(
|
|
524
|
-
(
|
|
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
|
-
|
|
559
|
-
|
|
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 =
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
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
|
-
|
|
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
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
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
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
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
|
-
|
|
612
|
-
i
|
|
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
|
+
};
|