@paraswap/dex-lib 2.16.3 → 2.16.4-balancer-v2-buy.2
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/.idea/aws.xml +17 -0
- package/.idea/codeStyles/Project.xml +19 -0
- package/.idea/codeStyles/codeStyleConfig.xml +5 -0
- package/.idea/inspectionProfiles/Project_Default.xml +6 -0
- package/.idea/misc.xml +6 -0
- package/.idea/modules.xml +8 -0
- package/.idea/paraswap-dex-lib.iml +9 -0
- package/.idea/vcs.xml +6 -0
- package/build/abi/TraderJoeV21Router.json +1489 -0
- package/build/config.js +10 -10
- package/build/dex/balancer-v2/balancer-v2.d.ts +9 -14
- package/build/dex/balancer-v2/balancer-v2.js +19 -16
- package/build/dex/balancer-v2/balancer-v2.js.map +1 -1
- package/build/dex/balancer-v2/config.d.ts +2 -7
- package/build/dex/balancer-v2/config.js +20 -30
- package/build/dex/balancer-v2/config.js.map +1 -1
- package/build/dex/balancer-v2/pools/balancer-v2-pool.d.ts +25 -0
- package/build/dex/balancer-v2/pools/balancer-v2-pool.js +107 -0
- package/build/dex/balancer-v2/pools/balancer-v2-pool.js.map +1 -0
- package/build/dex/balancer-v2/pools/linear/LinearMath.d.ts +20 -0
- package/build/dex/balancer-v2/pools/linear/LinearMath.js +186 -0
- package/build/dex/balancer-v2/pools/linear/LinearMath.js.map +1 -0
- package/build/dex/balancer-v2/pools/linear/LinearPool.d.ts +51 -0
- package/build/dex/balancer-v2/pools/linear/LinearPool.js +477 -0
- package/build/dex/balancer-v2/pools/linear/LinearPool.js.map +1 -0
- package/build/dex/balancer-v2/pools/phantom-stable/PhantomStableMath.d.ts +10 -0
- package/build/dex/balancer-v2/pools/phantom-stable/PhantomStableMath.js +314 -0
- package/build/dex/balancer-v2/pools/phantom-stable/PhantomStableMath.js.map +1 -0
- package/build/dex/balancer-v2/pools/phantom-stable/PhantomStablePool.d.ts +50 -0
- package/build/dex/balancer-v2/pools/phantom-stable/PhantomStablePool.js +367 -0
- package/build/dex/balancer-v2/pools/phantom-stable/PhantomStablePool.js.map +1 -0
- package/build/dex/balancer-v2/pools/stable/StableMath.d.ts +7 -0
- package/build/dex/balancer-v2/pools/stable/StableMath.js +111 -0
- package/build/dex/balancer-v2/pools/stable/StableMath.js.map +1 -0
- package/build/dex/balancer-v2/pools/stable/StablePool.d.ts +30 -0
- package/build/dex/balancer-v2/pools/stable/StablePool.js +129 -0
- package/build/dex/balancer-v2/pools/stable/StablePool.js.map +1 -0
- package/build/dex/balancer-v2/pools/weighted/WeightedMath.d.ts +6 -0
- package/build/dex/balancer-v2/pools/weighted/WeightedMath.js +61 -0
- package/build/dex/balancer-v2/pools/weighted/WeightedMath.js.map +1 -0
- package/build/dex/balancer-v2/pools/weighted/WeightedPool.d.ts +30 -0
- package/build/dex/balancer-v2/pools/weighted/WeightedPool.js +112 -0
- package/build/dex/balancer-v2/pools/weighted/WeightedPool.js.map +1 -0
- package/build/dex/balancer-v2/types.d.ts +1 -0
- package/build/dex/balancer-v2/types.js.map +1 -1
- package/build/dex/index.js +2 -0
- package/build/dex/index.js.map +1 -1
- package/build/dex/trader-joe-v2.1.d.ts +43 -0
- package/build/dex/trader-joe-v2.1.js +78 -0
- package/build/dex/trader-joe-v2.1.js.map +1 -0
- package/package.json +1 -1
- package/src/abi/TraderJoeV21Router.json +1489 -0
- package/src/config.ts +10 -10
- package/src/dex/balancer-v2/balancer-v2-e2e.test.ts +207 -224
- package/src/dex/balancer-v2/balancer-v2.ts +41 -29
- package/src/dex/balancer-v2/config.ts +23 -35
- package/src/dex/balancer-v2/pools/balancer-v2-pool.ts +274 -0
- package/src/dex/balancer-v2/{LinearMath.ts → pools/linear/LinearMath.ts} +81 -23
- package/src/dex/balancer-v2/{LinearPool.ts → pools/linear/LinearPool.ts} +241 -5
- package/src/dex/balancer-v2/{StableMath.ts → pools/phantom-stable/PhantomStableMath.ts} +101 -21
- package/src/dex/balancer-v2/{PhantomStablePool.ts → pools/phantom-stable/PhantomStablePool.ts} +165 -11
- package/src/dex/balancer-v2/pools/stable/StableMath.ts +204 -0
- package/src/dex/balancer-v2/pools/stable/StablePool.ts +221 -0
- package/src/dex/balancer-v2/pools/weighted/WeightedMath.ts +84 -0
- package/src/dex/balancer-v2/pools/weighted/WeightedPool.ts +189 -0
- package/src/dex/balancer-v2/types.ts +1 -0
- package/src/dex/index.ts +2 -0
- package/src/dex/trader-joe-v2.1.ts +155 -0
- package/tests/constants-e2e.ts +9 -4
- package/build/abi/DirectSwap.json +0 -970
- package/build/dex/test-dex/config.d.ts +0 -4
- package/build/dex/test-dex/config.js +0 -15
- package/build/dex/test-dex/config.js.map +0 -1
- package/build/dex/test-dex/test-dex-pool.d.ts +0 -39
- package/build/dex/test-dex/test-dex-pool.js +0 -63
- package/build/dex/test-dex/test-dex-pool.js.map +0 -1
- package/build/dex/test-dex/test-dex.d.ts +0 -37
- package/build/dex/test-dex/test-dex.js +0 -126
- package/build/dex/test-dex/test-dex.js.map +0 -1
- package/build/dex/test-dex/types.d.ts +0 -6
- package/build/dex/test-dex/types.js +0 -3
- package/build/dex/test-dex/types.js.map +0 -1
- package/build/dex/woo-fi-v2/woo-fi-math.d.ts +0 -16
- package/build/dex/woo-fi-v2/woo-fi-math.js +0 -137
- package/build/dex/woo-fi-v2/woo-fi-math.js.map +0 -1
- package/src/dex/balancer-v2/balancer-v2-pool.ts +0 -744
package/src/dex/balancer-v2/{PhantomStablePool.ts → pools/phantom-stable/PhantomStablePool.ts}
RENAMED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { Interface } from '@ethersproject/abi';
|
|
2
2
|
import { BigNumber } from '@ethersproject/bignumber';
|
|
3
|
-
import { BasePool } from '
|
|
4
|
-
import { isSameAddress, decodeThrowError } from '
|
|
5
|
-
import * as
|
|
6
|
-
import { SubgraphPoolBase, PoolState, callData
|
|
7
|
-
import { SwapSide } from '
|
|
8
|
-
import MetaStablePoolABI from '
|
|
9
|
-
import ComposableStablePoolABI from '
|
|
3
|
+
import { BasePool } from '../balancer-v2-pool';
|
|
4
|
+
import { isSameAddress, decodeThrowError } from '../../utils';
|
|
5
|
+
import * as PhantomStableMath from './PhantomStableMath';
|
|
6
|
+
import { SubgraphPoolBase, PoolState, callData } from '../../types';
|
|
7
|
+
import { SwapSide } from '../../../../constants';
|
|
8
|
+
import MetaStablePoolABI from '../../../../abi/balancer-v2/meta-stable-pool.json';
|
|
9
|
+
import ComposableStablePoolABI from '../../../../abi/balancer-v2/ComposableStable.json';
|
|
10
10
|
import { keyBy } from 'lodash';
|
|
11
11
|
|
|
12
12
|
enum PairTypes {
|
|
@@ -84,6 +84,21 @@ export class PhantomStablePool extends BasePool {
|
|
|
84
84
|
);
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
onBuy(amounts: bigint[], poolPairData: PhantomStablePoolPairData): bigint[] {
|
|
88
|
+
return this._swapGivenOut(
|
|
89
|
+
amounts,
|
|
90
|
+
poolPairData.tokens,
|
|
91
|
+
poolPairData.balances,
|
|
92
|
+
poolPairData.indexIn,
|
|
93
|
+
poolPairData.indexOut,
|
|
94
|
+
poolPairData.bptIndex,
|
|
95
|
+
poolPairData.scalingFactors,
|
|
96
|
+
poolPairData.swapFee,
|
|
97
|
+
poolPairData.amp,
|
|
98
|
+
poolPairData.actualSupply,
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
87
102
|
// StablePool's `_onSwapGivenIn` and `_onSwapGivenOut` handlers are meant to process swaps between Pool tokens.
|
|
88
103
|
// Since one of the Pool's tokens is the preminted BPT, we need to a) handle swaps where that tokens is involved
|
|
89
104
|
// separately (as they are effectively single-token joins or exits), and b) remove BPT from the balances array when
|
|
@@ -110,6 +125,64 @@ export class PhantomStablePool extends BasePool {
|
|
|
110
125
|
};
|
|
111
126
|
}
|
|
112
127
|
|
|
128
|
+
_swapGivenOut(
|
|
129
|
+
tokenAmountsOut: bigint[],
|
|
130
|
+
tokens: string[],
|
|
131
|
+
balances: bigint[],
|
|
132
|
+
indexIn: number,
|
|
133
|
+
indexOut: number,
|
|
134
|
+
bptIndex: number,
|
|
135
|
+
scalingFactors: bigint[],
|
|
136
|
+
swapFeePercentage: bigint,
|
|
137
|
+
amplificationParameter: bigint,
|
|
138
|
+
actualSupply: bigint | undefined,
|
|
139
|
+
) {
|
|
140
|
+
// Phantom pools allow trading between token and pool BPT
|
|
141
|
+
let pairType: PairTypes;
|
|
142
|
+
if (isSameAddress(tokens[indexIn], tokens[bptIndex])) {
|
|
143
|
+
pairType = PairTypes.BptToToken;
|
|
144
|
+
} else if (isSameAddress(tokens[indexOut], tokens[bptIndex])) {
|
|
145
|
+
pairType = PairTypes.TokenToBpt;
|
|
146
|
+
} else {
|
|
147
|
+
pairType = PairTypes.TokenToToken;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const balancesUpscaled = this._upscaleArray(balances, scalingFactors);
|
|
151
|
+
const tokenAmountsOutScaled = tokenAmountsOut.map(a =>
|
|
152
|
+
this._upscale(a, scalingFactors[indexOut]),
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
let bptSupply: bigint;
|
|
156
|
+
if (actualSupply) bptSupply = actualSupply;
|
|
157
|
+
// VirtualBPTSupply must be used for the maths
|
|
158
|
+
else bptSupply = this.MAX_TOKEN_BALANCE.sub(balances[bptIndex]).toBigInt();
|
|
159
|
+
|
|
160
|
+
const droppedBpt = this.removeBPT(
|
|
161
|
+
balancesUpscaled,
|
|
162
|
+
indexIn,
|
|
163
|
+
indexOut,
|
|
164
|
+
bptIndex,
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
const amountsIn = this._onSwapGivenOut(
|
|
168
|
+
tokenAmountsOutScaled,
|
|
169
|
+
droppedBpt.balances,
|
|
170
|
+
droppedBpt.indexIn,
|
|
171
|
+
droppedBpt.indexOut,
|
|
172
|
+
amplificationParameter,
|
|
173
|
+
bptSupply,
|
|
174
|
+
pairType,
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
const amountsInDownscaled = amountsIn.map(a =>
|
|
178
|
+
this._downscaleDown(a, scalingFactors[indexIn]),
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
return amountsInDownscaled.map(a =>
|
|
182
|
+
this._subtractSwapFeeAmount(a, swapFeePercentage),
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
113
186
|
_swapGivenIn(
|
|
114
187
|
tokenAmountsIn: bigint[],
|
|
115
188
|
tokens: string[],
|
|
@@ -169,6 +242,87 @@ export class PhantomStablePool extends BasePool {
|
|
|
169
242
|
);
|
|
170
243
|
}
|
|
171
244
|
|
|
245
|
+
_onSwapGivenOut(
|
|
246
|
+
tokenAmountsOut: bigint[],
|
|
247
|
+
balances: bigint[],
|
|
248
|
+
indexIn: number,
|
|
249
|
+
indexOut: number,
|
|
250
|
+
_amplificationParameter: bigint,
|
|
251
|
+
virtualBptSupply: bigint,
|
|
252
|
+
pairType: PairTypes,
|
|
253
|
+
) {
|
|
254
|
+
const invariant = PhantomStableMath._calculateInvariant(
|
|
255
|
+
_amplificationParameter,
|
|
256
|
+
balances,
|
|
257
|
+
true,
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
const amountsIn: bigint[] = [];
|
|
261
|
+
|
|
262
|
+
if (pairType === PairTypes.TokenToBpt) {
|
|
263
|
+
tokenAmountsOut.forEach(amountOut => {
|
|
264
|
+
let amt: bigint;
|
|
265
|
+
try {
|
|
266
|
+
const amountsInBigInt = Array(balances.length).fill(0n);
|
|
267
|
+
amountsInBigInt[indexIn] = amountOut;
|
|
268
|
+
|
|
269
|
+
amt = PhantomStableMath._calcTokenInGivenExactBptOut(
|
|
270
|
+
_amplificationParameter,
|
|
271
|
+
balances,
|
|
272
|
+
indexIn,
|
|
273
|
+
amountOut,
|
|
274
|
+
virtualBptSupply,
|
|
275
|
+
0n,
|
|
276
|
+
invariant,
|
|
277
|
+
);
|
|
278
|
+
} catch (err) {
|
|
279
|
+
amt = 0n;
|
|
280
|
+
}
|
|
281
|
+
amountsIn.push(amt);
|
|
282
|
+
});
|
|
283
|
+
} else if (pairType === PairTypes.BptToToken) {
|
|
284
|
+
|
|
285
|
+
tokenAmountsOut.forEach(amountOut => {
|
|
286
|
+
let amt: bigint;
|
|
287
|
+
try {
|
|
288
|
+
const amountsOutBigInt = Array(balances.length).fill(0n);
|
|
289
|
+
amountsOutBigInt[indexOut] = amountOut;
|
|
290
|
+
|
|
291
|
+
amt = PhantomStableMath._calcBptInGivenExactTokensOut(
|
|
292
|
+
_amplificationParameter,
|
|
293
|
+
balances,
|
|
294
|
+
amountsOutBigInt,
|
|
295
|
+
virtualBptSupply,
|
|
296
|
+
0n,
|
|
297
|
+
invariant,
|
|
298
|
+
);
|
|
299
|
+
} catch (err) {
|
|
300
|
+
amt = 0n;
|
|
301
|
+
}
|
|
302
|
+
amountsIn.push(amt);
|
|
303
|
+
});
|
|
304
|
+
} else {
|
|
305
|
+
tokenAmountsOut.forEach(amountOut => {
|
|
306
|
+
let amt: bigint;
|
|
307
|
+
try {
|
|
308
|
+
amt = PhantomStableMath._calcInGivenOut(
|
|
309
|
+
_amplificationParameter,
|
|
310
|
+
balances,
|
|
311
|
+
indexIn,
|
|
312
|
+
indexOut,
|
|
313
|
+
amountOut,
|
|
314
|
+
0n,
|
|
315
|
+
invariant,
|
|
316
|
+
);
|
|
317
|
+
} catch (err) {
|
|
318
|
+
amt = 0n;
|
|
319
|
+
}
|
|
320
|
+
amountsIn.push(amt);
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
return amountsIn;
|
|
324
|
+
}
|
|
325
|
+
|
|
172
326
|
/*
|
|
173
327
|
Called when a swap with the Pool occurs, where the amount of tokens entering the Pool is known.
|
|
174
328
|
All amounts are upscaled.
|
|
@@ -184,7 +338,7 @@ export class PhantomStablePool extends BasePool {
|
|
|
184
338
|
virtualBptSupply: bigint,
|
|
185
339
|
pairType: PairTypes,
|
|
186
340
|
): bigint[] {
|
|
187
|
-
const invariant =
|
|
341
|
+
const invariant = PhantomStableMath._calculateInvariant(
|
|
188
342
|
_amplificationParameter,
|
|
189
343
|
balances,
|
|
190
344
|
true,
|
|
@@ -199,7 +353,7 @@ export class PhantomStablePool extends BasePool {
|
|
|
199
353
|
const amountsInBigInt = Array(balances.length).fill(0n);
|
|
200
354
|
amountsInBigInt[indexIn] = amountIn;
|
|
201
355
|
|
|
202
|
-
amt =
|
|
356
|
+
amt = PhantomStableMath._calcBptOutGivenExactTokensIn(
|
|
203
357
|
_amplificationParameter,
|
|
204
358
|
balances,
|
|
205
359
|
amountsInBigInt,
|
|
@@ -215,7 +369,7 @@ export class PhantomStablePool extends BasePool {
|
|
|
215
369
|
tokenAmountsIn.forEach(amountIn => {
|
|
216
370
|
let amt: bigint;
|
|
217
371
|
try {
|
|
218
|
-
amt =
|
|
372
|
+
amt = PhantomStableMath._calcTokenOutGivenExactBptIn(
|
|
219
373
|
_amplificationParameter,
|
|
220
374
|
balances,
|
|
221
375
|
indexOut,
|
|
@@ -232,7 +386,7 @@ export class PhantomStablePool extends BasePool {
|
|
|
232
386
|
tokenAmountsIn.forEach(amountIn => {
|
|
233
387
|
let amt: bigint;
|
|
234
388
|
try {
|
|
235
|
-
amt =
|
|
389
|
+
amt = PhantomStableMath._calcOutGivenIn(
|
|
236
390
|
_amplificationParameter,
|
|
237
391
|
balances,
|
|
238
392
|
indexIn,
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { BI_POWS } from '../../../../bigint-constants';
|
|
2
|
+
import { MathSol } from '../../balancer-v2-math';
|
|
3
|
+
|
|
4
|
+
export class StableMath {
|
|
5
|
+
static _AMP_PRECISION = BI_POWS[3];
|
|
6
|
+
|
|
7
|
+
static _calculateInvariant(
|
|
8
|
+
amplificationParameter: bigint,
|
|
9
|
+
balances: bigint[],
|
|
10
|
+
roundUp: boolean,
|
|
11
|
+
): bigint {
|
|
12
|
+
/**********************************************************************************************
|
|
13
|
+
// invariant //
|
|
14
|
+
// D = invariant D^(n+1) //
|
|
15
|
+
// A = amplification coefficient A n^n S + D = A D n^n + ----------- //
|
|
16
|
+
// S = sum of balances n^n P //
|
|
17
|
+
// P = product of balances //
|
|
18
|
+
// n = number of tokens //
|
|
19
|
+
*********x************************************************************************************/
|
|
20
|
+
|
|
21
|
+
// We support rounding up or down.
|
|
22
|
+
let sum = 0n;
|
|
23
|
+
const numTokens = balances.length;
|
|
24
|
+
for (let i = 0; i < numTokens; i++) {
|
|
25
|
+
sum = sum + balances[i];
|
|
26
|
+
}
|
|
27
|
+
if (sum == 0n) {
|
|
28
|
+
return 0n;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let prevInvariant = 0n;
|
|
32
|
+
let invariant = sum;
|
|
33
|
+
const ampTimesTotal = amplificationParameter * BigInt(numTokens);
|
|
34
|
+
|
|
35
|
+
for (let i = 0; i < 255; i++) {
|
|
36
|
+
let P_D = balances[0] * BigInt(numTokens);
|
|
37
|
+
for (let j = 1; j < numTokens; j++) {
|
|
38
|
+
P_D = MathSol.div(
|
|
39
|
+
MathSol.mul(MathSol.mul(P_D, balances[j]), BigInt(numTokens)),
|
|
40
|
+
invariant,
|
|
41
|
+
roundUp,
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
prevInvariant = invariant;
|
|
45
|
+
invariant = MathSol.div(
|
|
46
|
+
MathSol.mul(MathSol.mul(BigInt(numTokens), invariant), invariant) +
|
|
47
|
+
MathSol.div(
|
|
48
|
+
MathSol.mul(MathSol.mul(ampTimesTotal, sum), P_D),
|
|
49
|
+
this._AMP_PRECISION,
|
|
50
|
+
roundUp,
|
|
51
|
+
),
|
|
52
|
+
MathSol.mul(BigInt(numTokens + 1), invariant) +
|
|
53
|
+
// No need to use checked arithmetic for the amp precision, the amp is guaranteed to be at least 1
|
|
54
|
+
MathSol.div(
|
|
55
|
+
MathSol.mul(ampTimesTotal - this._AMP_PRECISION, P_D),
|
|
56
|
+
this._AMP_PRECISION,
|
|
57
|
+
!roundUp,
|
|
58
|
+
),
|
|
59
|
+
roundUp,
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
if (invariant > prevInvariant) {
|
|
63
|
+
if (invariant - prevInvariant <= 1) {
|
|
64
|
+
return invariant;
|
|
65
|
+
}
|
|
66
|
+
} else if (prevInvariant - invariant <= 1) {
|
|
67
|
+
return invariant;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
throw new Error('Errors.STABLE_INVARIANT_DIDNT_CONVERGE');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
static _calcInGivenOut(
|
|
76
|
+
amplificationParameter: bigint,
|
|
77
|
+
balances: bigint[],
|
|
78
|
+
tokenIndexIn: number,
|
|
79
|
+
tokenIndexOut: number,
|
|
80
|
+
tokenAmountsOut: bigint[],
|
|
81
|
+
): bigint[] {
|
|
82
|
+
const invariant = this._calculateInvariant(
|
|
83
|
+
amplificationParameter,
|
|
84
|
+
balances,
|
|
85
|
+
false,
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
return tokenAmountsOut.map(a => {
|
|
89
|
+
balances[tokenIndexOut] = MathSol.sub(balances[tokenIndexOut], a);
|
|
90
|
+
|
|
91
|
+
const finalBalanceIn =
|
|
92
|
+
this._getTokenBalanceGivenInvariantAndAllOtherBalances(
|
|
93
|
+
amplificationParameter,
|
|
94
|
+
balances,
|
|
95
|
+
invariant,
|
|
96
|
+
tokenIndexIn,
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
return MathSol.add(
|
|
100
|
+
MathSol.sub(finalBalanceIn, balances[tokenIndexIn]),
|
|
101
|
+
1n,
|
|
102
|
+
);
|
|
103
|
+
});
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
static _calcOutGivenIn(
|
|
107
|
+
amplificationParameter: bigint,
|
|
108
|
+
balances: bigint[],
|
|
109
|
+
tokenIndexIn: number,
|
|
110
|
+
tokenIndexOut: number,
|
|
111
|
+
tokenAmountsIn: bigint[],
|
|
112
|
+
): bigint[] {
|
|
113
|
+
// Amount out, so we round down overall.
|
|
114
|
+
|
|
115
|
+
// Given that we need to have a greater final balance out, the invariant needs to be rounded up
|
|
116
|
+
const invariant = this._calculateInvariant(
|
|
117
|
+
amplificationParameter,
|
|
118
|
+
balances,
|
|
119
|
+
true,
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
const initBalance = balances[tokenIndexIn];
|
|
123
|
+
// Modification: The original code was implemented for a single tokenAmountsIn
|
|
124
|
+
return tokenAmountsIn.map(a => {
|
|
125
|
+
balances[tokenIndexIn] = initBalance + a;
|
|
126
|
+
|
|
127
|
+
const finalBalanceOut =
|
|
128
|
+
this._getTokenBalanceGivenInvariantAndAllOtherBalances(
|
|
129
|
+
amplificationParameter,
|
|
130
|
+
balances,
|
|
131
|
+
invariant,
|
|
132
|
+
tokenIndexOut,
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
// No need to use checked arithmetic since `tokenAmountIn` was actually added to the same balance right before
|
|
136
|
+
// calling `_getTokenBalanceGivenInvariantAndAllOtherBalances` which doesn't alter the balances array.
|
|
137
|
+
// balances[tokenIndexIn] = balances[tokenIndexIn] - tokenAmountIn;
|
|
138
|
+
return balances[tokenIndexOut] - finalBalanceOut - 1n;
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
static _getTokenBalanceGivenInvariantAndAllOtherBalances(
|
|
143
|
+
amplificationParameter: bigint,
|
|
144
|
+
balances: bigint[],
|
|
145
|
+
invariant: bigint,
|
|
146
|
+
tokenIndex: number,
|
|
147
|
+
): bigint {
|
|
148
|
+
// Rounds result up overall
|
|
149
|
+
|
|
150
|
+
const ampTimesTotal = amplificationParameter * BigInt(balances.length);
|
|
151
|
+
let sum = balances[0];
|
|
152
|
+
let P_D = balances[0] * BigInt(balances.length);
|
|
153
|
+
for (let j = 1; j < balances.length; j++) {
|
|
154
|
+
P_D = MathSol.divDown(
|
|
155
|
+
MathSol.mul(MathSol.mul(P_D, balances[j]), BigInt(balances.length)),
|
|
156
|
+
invariant,
|
|
157
|
+
);
|
|
158
|
+
sum = sum + balances[j];
|
|
159
|
+
}
|
|
160
|
+
// No need to use safe math, based on the loop above `sum` is greater than or equal to `balances[tokenIndex]`
|
|
161
|
+
sum = sum - balances[tokenIndex];
|
|
162
|
+
|
|
163
|
+
const inv2 = MathSol.mul(invariant, invariant);
|
|
164
|
+
// We remove the balance fromm c by multiplying it
|
|
165
|
+
const c = MathSol.mul(
|
|
166
|
+
MathSol.mul(
|
|
167
|
+
MathSol.divUp(inv2, MathSol.mul(ampTimesTotal, P_D)),
|
|
168
|
+
this._AMP_PRECISION,
|
|
169
|
+
),
|
|
170
|
+
balances[tokenIndex],
|
|
171
|
+
);
|
|
172
|
+
const b =
|
|
173
|
+
sum +
|
|
174
|
+
MathSol.mul(
|
|
175
|
+
MathSol.divDown(invariant, ampTimesTotal),
|
|
176
|
+
this._AMP_PRECISION,
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
// We iterate to find the balance
|
|
180
|
+
let prevTokenBalance = 0n;
|
|
181
|
+
// We multiply the first iteration outside the loop with the invariant to set the value of the
|
|
182
|
+
// initial approximation.
|
|
183
|
+
let tokenBalance = MathSol.divUp(inv2 + c, invariant + b);
|
|
184
|
+
|
|
185
|
+
for (let i = 0; i < 255; i++) {
|
|
186
|
+
prevTokenBalance = tokenBalance;
|
|
187
|
+
|
|
188
|
+
tokenBalance = MathSol.divUp(
|
|
189
|
+
MathSol.mul(tokenBalance, tokenBalance) + c,
|
|
190
|
+
MathSol.mul(tokenBalance, 2n) + b - invariant,
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
if (tokenBalance > prevTokenBalance) {
|
|
194
|
+
if (tokenBalance - prevTokenBalance <= 1) {
|
|
195
|
+
return tokenBalance;
|
|
196
|
+
}
|
|
197
|
+
} else if (prevTokenBalance - tokenBalance <= 1) {
|
|
198
|
+
return tokenBalance;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
throw new Error('Errors.STABLE_GET_BALANCE_DIDNT_CONVERGE');
|
|
203
|
+
}
|
|
204
|
+
}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { Interface } from '@ethersproject/abi';
|
|
2
|
+
import StablePoolABI from '../../../../abi/balancer-v2/stable-pool.json';
|
|
3
|
+
import MetaStablePoolABI from '../../../../abi/balancer-v2/meta-stable-pool.json';
|
|
4
|
+
import { callData, PoolState, SubgraphPoolBase, TokenState } from '../../types';
|
|
5
|
+
import { decodeThrowError, getTokenScalingFactor } from '../../utils';
|
|
6
|
+
import { BigNumber } from '@ethersproject/bignumber';
|
|
7
|
+
import { SwapSide } from '../../../../constants';
|
|
8
|
+
import { BaseGeneralPool } from '../balancer-v2-pool';
|
|
9
|
+
import { StableMath } from './StableMath';
|
|
10
|
+
|
|
11
|
+
export type StablePoolPairData = {
|
|
12
|
+
balances: bigint[];
|
|
13
|
+
indexIn: number;
|
|
14
|
+
indexOut: number;
|
|
15
|
+
scalingFactors: bigint[];
|
|
16
|
+
swapFee: bigint;
|
|
17
|
+
amp: bigint;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export class StablePool extends BaseGeneralPool {
|
|
21
|
+
vaultAddress: string;
|
|
22
|
+
vaultInterface: Interface;
|
|
23
|
+
poolInterface: Interface;
|
|
24
|
+
metaPoolInterface: Interface;
|
|
25
|
+
|
|
26
|
+
constructor(vaultAddress: string, vaultInterface: Interface) {
|
|
27
|
+
super();
|
|
28
|
+
this.vaultAddress = vaultAddress;
|
|
29
|
+
this.vaultInterface = vaultInterface;
|
|
30
|
+
this.poolInterface = new Interface(StablePoolABI);
|
|
31
|
+
this.metaPoolInterface = new Interface(MetaStablePoolABI);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
_onSwapGivenOut(
|
|
35
|
+
tokenAmountsOut: bigint[],
|
|
36
|
+
balances: bigint[],
|
|
37
|
+
indexIn: number,
|
|
38
|
+
indexOut: number,
|
|
39
|
+
_amplificationParameter: bigint,
|
|
40
|
+
): bigint[] {
|
|
41
|
+
return StableMath._calcInGivenOut(
|
|
42
|
+
_amplificationParameter,
|
|
43
|
+
balances,
|
|
44
|
+
indexIn,
|
|
45
|
+
indexOut,
|
|
46
|
+
tokenAmountsOut,
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
_onSwapGivenIn(
|
|
51
|
+
tokenAmountsIn: bigint[],
|
|
52
|
+
balances: bigint[],
|
|
53
|
+
indexIn: number,
|
|
54
|
+
indexOut: number,
|
|
55
|
+
_amplificationParameter: bigint,
|
|
56
|
+
): bigint[] {
|
|
57
|
+
return StableMath._calcOutGivenIn(
|
|
58
|
+
_amplificationParameter,
|
|
59
|
+
balances,
|
|
60
|
+
indexIn,
|
|
61
|
+
indexOut,
|
|
62
|
+
tokenAmountsIn,
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/*
|
|
67
|
+
Helper function to parse pool data into params for onSell/onBuy functions.
|
|
68
|
+
*/
|
|
69
|
+
parsePoolPairData(
|
|
70
|
+
pool: SubgraphPoolBase,
|
|
71
|
+
poolState: PoolState,
|
|
72
|
+
tokenIn: string,
|
|
73
|
+
tokenOut: string,
|
|
74
|
+
): StablePoolPairData {
|
|
75
|
+
let indexIn = 0,
|
|
76
|
+
indexOut = 0;
|
|
77
|
+
|
|
78
|
+
const scalingFactors: bigint[] = [];
|
|
79
|
+
const balances = poolState.orderedTokens.map((tokenAddress, i) => {
|
|
80
|
+
const t = pool.tokensMap[tokenAddress.toLowerCase()] || poolState.tokens[tokenAddress.toLowerCase()];
|
|
81
|
+
|
|
82
|
+
if (t.address.toLowerCase() === tokenIn.toLowerCase()) {
|
|
83
|
+
indexIn = i;
|
|
84
|
+
}
|
|
85
|
+
if (t.address.toLowerCase() === tokenOut.toLowerCase()) {
|
|
86
|
+
indexOut = i;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (pool.poolType === 'MetaStable') {
|
|
90
|
+
scalingFactors.push(
|
|
91
|
+
poolState.tokens[t.address.toLowerCase()].scalingFactor || 0n,
|
|
92
|
+
);
|
|
93
|
+
} else scalingFactors.push(getTokenScalingFactor(t.decimals));
|
|
94
|
+
return poolState.tokens[t.address.toLowerCase()].balance;
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const poolPairData: StablePoolPairData = {
|
|
98
|
+
balances,
|
|
99
|
+
indexIn,
|
|
100
|
+
indexOut,
|
|
101
|
+
scalingFactors,
|
|
102
|
+
swapFee: poolState.swapFee,
|
|
103
|
+
amp: poolState.amp ? poolState.amp : 0n,
|
|
104
|
+
};
|
|
105
|
+
return poolPairData;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/*
|
|
109
|
+
Helper function to construct onchain multicall data for StablePool.
|
|
110
|
+
*/
|
|
111
|
+
getOnChainCalls(pool: SubgraphPoolBase): callData[] {
|
|
112
|
+
const poolCallData: callData[] = [
|
|
113
|
+
{
|
|
114
|
+
target: this.vaultAddress,
|
|
115
|
+
callData: this.vaultInterface.encodeFunctionData('getPoolTokens', [
|
|
116
|
+
pool.id,
|
|
117
|
+
]),
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
target: pool.address,
|
|
121
|
+
callData: this.poolInterface.encodeFunctionData('getSwapFeePercentage'),
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
target: pool.address,
|
|
125
|
+
callData: this.poolInterface.encodeFunctionData(
|
|
126
|
+
'getAmplificationParameter',
|
|
127
|
+
),
|
|
128
|
+
},
|
|
129
|
+
];
|
|
130
|
+
if (pool.poolType === 'MetaStable') {
|
|
131
|
+
poolCallData.push({
|
|
132
|
+
target: pool.address,
|
|
133
|
+
callData:
|
|
134
|
+
this.metaPoolInterface.encodeFunctionData('getScalingFactors'),
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
return poolCallData;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/*
|
|
141
|
+
Helper function to decodes multicall data for a Stable Pool.
|
|
142
|
+
data must contain returnData
|
|
143
|
+
startIndex is where to start in returnData. Allows this decode function to be called along with other pool types.
|
|
144
|
+
*/
|
|
145
|
+
decodeOnChainCalls(
|
|
146
|
+
pool: SubgraphPoolBase,
|
|
147
|
+
data: { success: boolean; returnData: any }[],
|
|
148
|
+
startIndex: number,
|
|
149
|
+
): [{ [address: string]: PoolState }, number] {
|
|
150
|
+
const pools = {} as { [address: string]: PoolState };
|
|
151
|
+
|
|
152
|
+
const poolTokens = decodeThrowError(
|
|
153
|
+
this.vaultInterface,
|
|
154
|
+
'getPoolTokens',
|
|
155
|
+
data[startIndex++],
|
|
156
|
+
pool.address,
|
|
157
|
+
);
|
|
158
|
+
const swapFee = decodeThrowError(
|
|
159
|
+
this.poolInterface,
|
|
160
|
+
'getSwapFeePercentage',
|
|
161
|
+
data[startIndex++],
|
|
162
|
+
pool.address,
|
|
163
|
+
)[0];
|
|
164
|
+
const amp = decodeThrowError(
|
|
165
|
+
this.poolInterface,
|
|
166
|
+
'getAmplificationParameter',
|
|
167
|
+
data[startIndex++],
|
|
168
|
+
pool.address,
|
|
169
|
+
);
|
|
170
|
+
let scalingFactors: BigNumber[] | undefined;
|
|
171
|
+
if (pool.poolType === 'MetaStable') {
|
|
172
|
+
scalingFactors = decodeThrowError(
|
|
173
|
+
this.metaPoolInterface,
|
|
174
|
+
'getScalingFactors',
|
|
175
|
+
data[startIndex++],
|
|
176
|
+
pool.address,
|
|
177
|
+
)[0];
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const poolState: PoolState = {
|
|
181
|
+
swapFee: BigInt(swapFee.toString()),
|
|
182
|
+
tokens: poolTokens.tokens.reduce(
|
|
183
|
+
(ptAcc: { [address: string]: TokenState }, pt: string, j: number) => {
|
|
184
|
+
const tokenState: TokenState = {
|
|
185
|
+
balance: BigInt(poolTokens.balances[j].toString()),
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
if (scalingFactors)
|
|
189
|
+
tokenState.scalingFactor = BigInt(scalingFactors[j].toString());
|
|
190
|
+
|
|
191
|
+
ptAcc[pt.toLowerCase()] = tokenState;
|
|
192
|
+
return ptAcc;
|
|
193
|
+
},
|
|
194
|
+
{},
|
|
195
|
+
),
|
|
196
|
+
orderedTokens: poolTokens.tokens,
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
if (amp) {
|
|
200
|
+
poolState.amp = BigInt(amp.value.toString());
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
pools[pool.address] = poolState;
|
|
204
|
+
|
|
205
|
+
return [pools, startIndex];
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/*
|
|
209
|
+
For stable pools there is no Swap limit. As an approx - use almost the total balance of token out as we can add any amount of tokenIn and expect some back.
|
|
210
|
+
*/
|
|
211
|
+
getSwapMaxAmount(poolPairData: StablePoolPairData, side: SwapSide): bigint {
|
|
212
|
+
return (
|
|
213
|
+
(this._upscale(
|
|
214
|
+
poolPairData.balances[poolPairData.indexOut],
|
|
215
|
+
poolPairData.scalingFactors[poolPairData.indexOut],
|
|
216
|
+
) *
|
|
217
|
+
99n) /
|
|
218
|
+
100n
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
}
|