@paraswap/dex-lib 3.4.2-balancer-v2-single-swap.0 → 3.4.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.
Files changed (110) hide show
  1. package/README.md +377 -377
  2. package/build/config.js +9 -5
  3. package/build/config.js.map +1 -1
  4. package/build/dex/algebra/algebra.js +25 -25
  5. package/build/dex/balancer-v1/balancer-v1.js +34 -34
  6. package/build/dex/balancer-v2/balancer-v2.d.ts +1 -1
  7. package/build/dex/balancer-v2/balancer-v2.js +65 -78
  8. package/build/dex/balancer-v2/balancer-v2.js.map +1 -1
  9. package/build/dex/camelot/camelot.js +27 -27
  10. package/build/dex/kyberdmm/kyberdmm.js +27 -27
  11. package/build/dex/pancakeswap-v3/pancakeswap-v3.js +25 -25
  12. package/build/dex/solidly/solidly.js +27 -27
  13. package/build/dex/solidly-v3/solidly-v3.js +25 -25
  14. package/build/dex/swaap-v2/config.js +15 -0
  15. package/build/dex/swaap-v2/config.js.map +1 -1
  16. package/build/dex/uniswap-v2/uniswap-v2.js +26 -26
  17. package/build/dex/uniswap-v3/uniswap-v3.js +25 -25
  18. package/funding.json +5 -5
  19. package/package.json +1 -1
  20. package/src/config.ts +500 -496
  21. package/src/constants.ts +67 -67
  22. package/src/dex/aave-v2/aave-v2.ts +409 -409
  23. package/src/dex/aave-v3/aave-v3.ts +320 -320
  24. package/src/dex/algebra/algebra.ts +1180 -1180
  25. package/src/dex/angle-staked-stable/angle-staked-stable.ts +347 -347
  26. package/src/dex/augustus-rfq.ts +180 -180
  27. package/src/dex/bProtocol/bProtocol.ts +108 -108
  28. package/src/dex/balancer-v1/balancer-v1.ts +572 -572
  29. package/src/dex/balancer-v2/balancer-v2-e2e.test.ts +1699 -1699
  30. package/src/dex/balancer-v2/balancer-v2.ts +1669 -1701
  31. package/src/dex/bancor/bancor.ts +128 -128
  32. package/src/dex/camelot/camelot.ts +873 -873
  33. package/src/dex/compound/compound.ts +107 -107
  34. package/src/dex/curve-v1/config.ts +1179 -1179
  35. package/src/dex/curve-v1/curve-v1-e2e.test.ts +507 -507
  36. package/src/dex/curve-v1/curve-v1.ts +1315 -1315
  37. package/src/dex/curve-v1-factory/config.ts +1018 -1018
  38. package/src/dex/curve-v1-factory/curve-v1-factory.ts +1288 -1288
  39. package/src/dex/curve-v2/curve-v2.ts +440 -440
  40. package/src/dex/dexalot/dexalot.ts +1100 -1100
  41. package/src/dex/dodo-v1/dodo-v1.ts +132 -132
  42. package/src/dex/dodo-v2.ts +266 -266
  43. package/src/dex/etherfi.ts +239 -239
  44. package/src/dex/generic-rfq/generic-rfq.ts +431 -431
  45. package/src/dex/gmx/gmx.ts +321 -321
  46. package/src/dex/hashflow/hashflow.ts +1135 -1135
  47. package/src/dex/idex.ts +254 -254
  48. package/src/dex/index.ts +357 -357
  49. package/src/dex/jarvis-v6/jarvis-v6.ts +509 -509
  50. package/src/dex/jarvis.ts +262 -262
  51. package/src/dex/kyberdmm/kyberdmm.ts +649 -649
  52. package/src/dex/lido/lido.ts +110 -110
  53. package/src/dex/maker-psm/maker-psm.ts +592 -592
  54. package/src/dex/maverick-v1/maverick-v1.ts +537 -537
  55. package/src/dex/nerve/config.ts +635 -635
  56. package/src/dex/nerve/nerve-e2e.test.ts +731 -731
  57. package/src/dex/nerve/nerve-events.test.ts +911 -911
  58. package/src/dex/nerve/nerve.ts +467 -467
  59. package/src/dex/onebit/onebit.ts +113 -113
  60. package/src/dex/pancakeswap-v3/pancakeswap-v3.ts +1150 -1150
  61. package/src/dex/paraswap-limit-orders/config.ts +60 -60
  62. package/src/dex/paraswap-limit-orders/paraswap-limit-orders-e2e.test.ts +137 -137
  63. package/src/dex/paraswap-limit-orders/paraswap-limit-orders-integration.test.ts +328 -328
  64. package/src/dex/paraswap-limit-orders/paraswap-limit-orders.ts +631 -631
  65. package/src/dex/polygon-migrator/polygon-migrator.ts +194 -194
  66. package/src/dex/quick-perps/quick-perps.ts +337 -337
  67. package/src/dex/quickswap/config.ts +16 -16
  68. package/src/dex/quickswap/quickswap-e2e.test.ts +384 -384
  69. package/src/dex/smoothy/smoothy.ts +110 -110
  70. package/src/dex/solidly/config.ts +225 -225
  71. package/src/dex/solidly/solidly-e2e.test.ts +1421 -1421
  72. package/src/dex/solidly/solidly-integration.test.ts +1753 -1753
  73. package/src/dex/solidly/solidly.ts +687 -687
  74. package/src/dex/solidly-v3/solidly-v3.ts +968 -968
  75. package/src/dex/spark/spark.ts +284 -284
  76. package/src/dex/stable-pool/stable-pool.ts +129 -129
  77. package/src/dex/swaap-v2/config.ts +15 -0
  78. package/src/dex/swaap-v2/swaap-v2-e2e.test.ts +233 -17
  79. package/src/dex/swaap-v2/swaap-v2-integration.test.ts +1 -1
  80. package/src/dex/swaap-v2/swaap-v2.ts +1056 -1056
  81. package/src/dex/swell/swell.ts +312 -312
  82. package/src/dex/synthetix/synthetix.ts +420 -420
  83. package/src/dex/trader-joe-v2.1/trader-joe-v2.1.ts +180 -180
  84. package/src/dex/uniswap-v2/config.ts +637 -637
  85. package/src/dex/uniswap-v2/constants.ts +58 -58
  86. package/src/dex/uniswap-v2/uniswap-v2-e2e-avalanche.test.ts +1257 -1257
  87. package/src/dex/uniswap-v2/uniswap-v2-e2e-base.test.ts +42 -42
  88. package/src/dex/uniswap-v2/uniswap-v2-e2e-bsc.test.ts +1295 -1295
  89. package/src/dex/uniswap-v2/uniswap-v2-e2e-mainnet.test.ts +1834 -1834
  90. package/src/dex/uniswap-v2/uniswap-v2-e2e-polygon.test.ts +871 -871
  91. package/src/dex/uniswap-v2/uniswap-v2.ts +1117 -1117
  92. package/src/dex/uniswap-v3/uniswap-v3.ts +1501 -1501
  93. package/src/dex/uniswap-v3.ts +242 -242
  94. package/src/dex/weth/config.ts +73 -73
  95. package/src/dex/weth/weth.ts +230 -230
  96. package/src/dex/wombat/wombat.ts +467 -467
  97. package/src/dex/woo-fi-v2/woo-fi-v2.ts +529 -529
  98. package/src/dex/wsteth/wsteth.ts +304 -304
  99. package/src/dex/zerox/index.ts +328 -328
  100. package/src/executor/Executor01BytecodeBuilder.ts +474 -474
  101. package/src/executor/Executor02BytecodeBuilder.ts +1392 -1392
  102. package/src/executor/ExecutorBytecodeBuilder.ts +390 -390
  103. package/src/executor/ExecutorDetector.ts +131 -131
  104. package/src/executor/constants.ts +66 -66
  105. package/src/executor/executor01-bytecode-builder-e2e.test.ts +1478 -1478
  106. package/src/executor/utils.ts +62 -62
  107. package/src/router/simpleswap.ts +366 -366
  108. package/src/types.ts +342 -342
  109. package/tests/constants-e2e.ts +1632 -1622
  110. package/tests/generate-new-limit-order.ts +107 -107
@@ -1,687 +1,687 @@
1
- import { UniswapV2 } from '../uniswap-v2/uniswap-v2';
2
- import {
3
- Network,
4
- NULL_ADDRESS,
5
- SUBGRAPH_TIMEOUT,
6
- DEST_TOKEN_PARASWAP_TRANSFERS,
7
- SRC_TOKEN_PARASWAP_TRANSFERS,
8
- } from '../../constants';
9
- import {
10
- AdapterExchangeParam,
11
- Address,
12
- DexExchangeParam,
13
- ExchangePrices,
14
- PoolLiquidity,
15
- SimpleExchangeParam,
16
- Token,
17
- TransferFeeParams,
18
- } from '../../types';
19
- import { IDexHelper } from '../../dex-helper';
20
- import erc20ABI from '../../abi/erc20.json';
21
- import { UniswapData, UniswapV2Data } from '../uniswap-v2/types';
22
- import { getBigIntPow, getDexKeysWithNetwork } from '../../utils';
23
- import solidlyFactoryABI from '../../abi/solidly/SolidlyFactory.json';
24
- import solidlyPair from '../../abi/solidly/SolidlyPair.json';
25
- import _ from 'lodash';
26
- import { NumberAsString, SwapSide } from '@paraswap/core';
27
- import { Interface, AbiCoder } from '@ethersproject/abi';
28
- import { SolidlyStablePool } from './solidly-stable-pool';
29
- import { Uniswapv2ConstantProductPool } from '../uniswap-v2/uniswap-v2-constant-product-pool';
30
- import {
31
- PoolState,
32
- SolidlyData,
33
- SolidlyPair,
34
- SolidlyPool,
35
- SolidlyPoolOrderedParams,
36
- } from './types';
37
- import { SolidlyConfig, Adapters } from './config';
38
- import { applyTransferFee } from '../../lib/token-transfer-fee';
39
- import {
40
- hexDataLength,
41
- hexZeroPad,
42
- hexlify,
43
- id,
44
- solidityPack,
45
- } from 'ethers/lib/utils';
46
- import { BigNumber } from 'ethers';
47
- import { Flag, SpecialDex } from '../../executor/types';
48
-
49
- const erc20Iface = new Interface(erc20ABI);
50
- const solidlyPairIface = new Interface(solidlyPair);
51
- const defaultAbiCoder = new AbiCoder();
52
-
53
- function encodePools(
54
- pools: SolidlyPool[],
55
- feeFactor: number,
56
- ): NumberAsString[] {
57
- return pools.map(({ fee, direction, address }) => {
58
- return (
59
- (BigInt(feeFactor - fee) << 161n) +
60
- ((direction ? 0n : 1n) << 160n) +
61
- BigInt(address)
62
- ).toString();
63
- });
64
- }
65
-
66
- export class Solidly extends UniswapV2 {
67
- pairs: { [key: string]: SolidlyPair } = {};
68
- stableFee?: number;
69
- volatileFee?: number;
70
-
71
- readonly isFeeOnTransferSupported: boolean = true;
72
- readonly SRC_TOKEN_DEX_TRANSFERS = 1;
73
- readonly DEST_TOKEN_DEX_TRANSFERS = 1;
74
-
75
- public static dexKeysWithNetwork: { key: string; networks: Network[] }[] =
76
- getDexKeysWithNetwork(
77
- _.omit(SolidlyConfig, [
78
- 'Velodrome',
79
- 'VelodromeV2',
80
- 'Aerodrome',
81
- 'SpiritSwapV2',
82
- 'SolidlyV2',
83
- 'Thena',
84
- 'Chronos',
85
- 'Ramses',
86
- 'Equalizer',
87
- 'Velocimeter',
88
- 'Usdfi',
89
- 'PharaohV1',
90
- ]),
91
- );
92
-
93
- constructor(
94
- protected network: Network,
95
- dexKey: string,
96
- protected dexHelper: IDexHelper,
97
- isDynamicFees = false,
98
- factoryAddress?: Address,
99
- subgraphURL?: string,
100
- initCode?: string,
101
- feeCode?: number,
102
- poolGasCost?: number,
103
- routerAddress?: Address,
104
- ) {
105
- super(
106
- network,
107
- dexKey,
108
- dexHelper,
109
- isDynamicFees,
110
- factoryAddress !== undefined
111
- ? factoryAddress
112
- : SolidlyConfig[dexKey][network].factoryAddress,
113
- subgraphURL === ''
114
- ? undefined
115
- : subgraphURL !== undefined
116
- ? subgraphURL
117
- : SolidlyConfig[dexKey][network].subgraphURL,
118
- initCode !== undefined
119
- ? initCode
120
- : SolidlyConfig[dexKey][network].initCode,
121
- feeCode !== undefined ? feeCode : SolidlyConfig[dexKey][network].feeCode,
122
- poolGasCost !== undefined
123
- ? poolGasCost
124
- : SolidlyConfig[dexKey][network].poolGasCost,
125
- solidlyPairIface,
126
- Adapters[network] || undefined,
127
- );
128
-
129
- this.stableFee = SolidlyConfig[dexKey][network].stableFee;
130
- this.volatileFee = SolidlyConfig[dexKey][network].volatileFee;
131
-
132
- this.factory = new dexHelper.web3Provider.eth.Contract(
133
- solidlyFactoryABI as any,
134
- factoryAddress !== undefined
135
- ? factoryAddress
136
- : SolidlyConfig[dexKey][network].factoryAddress,
137
- );
138
-
139
- this.router =
140
- routerAddress !== undefined
141
- ? routerAddress
142
- : SolidlyConfig[dexKey][network].router || '';
143
-
144
- this.feeFactor = SolidlyConfig[dexKey][network].feeFactor || this.feeFactor;
145
- }
146
-
147
- async findSolidlyPair(from: Token, to: Token, stable: boolean) {
148
- if (from.address.toLowerCase() === to.address.toLowerCase()) return null;
149
- const [token0, token1] =
150
- from.address.toLowerCase() < to.address.toLowerCase()
151
- ? [from, to]
152
- : [to, from];
153
-
154
- const typePostfix = this.poolPostfix(stable);
155
- const key = `${token0.address.toLowerCase()}-${token1.address.toLowerCase()}-${typePostfix}`;
156
- let pair = this.pairs[key];
157
- if (pair) return pair;
158
-
159
- let exchange = await this.factory.methods
160
- // Solidly has additional boolean parameter "StablePool"
161
- // At first we look for uniswap-like volatile pool
162
- .getPair(token0.address, token1.address, stable)
163
- .call();
164
-
165
- if (exchange === NULL_ADDRESS) {
166
- pair = { token0, token1, stable };
167
- } else {
168
- pair = { token0, token1, exchange, stable };
169
- }
170
- this.pairs[key] = pair;
171
- return pair;
172
- }
173
-
174
- async batchCatchUpPairs(pairs: [Token, Token][], blockNumber: number) {
175
- if (!blockNumber) return;
176
- const pairsToFetch: SolidlyPair[] = [];
177
- for (const _pair of pairs) {
178
- for (const stable of [false, true]) {
179
- const pair = await this.findSolidlyPair(_pair[0], _pair[1], stable);
180
- if (!(pair && pair.exchange)) continue;
181
- if (!pair.pool) {
182
- pairsToFetch.push(pair);
183
- } else if (!pair.pool.getState(blockNumber)) {
184
- pairsToFetch.push(pair);
185
- }
186
- }
187
- }
188
-
189
- if (!pairsToFetch.length) return;
190
-
191
- const reserves = await this.getManyPoolReserves(pairsToFetch, blockNumber);
192
-
193
- if (reserves.length !== pairsToFetch.length) {
194
- this.logger.error(
195
- `Error_getManyPoolReserves didn't get any pool reserves`,
196
- );
197
- }
198
-
199
- for (let i = 0; i < pairsToFetch.length; i++) {
200
- const pairState = reserves[i];
201
- const pair = pairsToFetch[i];
202
- if (!pair.pool) {
203
- await this.addPool(
204
- pair,
205
- pairState.reserves0,
206
- pairState.reserves1,
207
- pairState.feeCode,
208
- blockNumber,
209
- );
210
- } else pair.pool.setState(pairState, blockNumber);
211
- }
212
- }
213
-
214
- async getManyPoolReserves(
215
- pairs: SolidlyPair[],
216
- blockNumber: number,
217
- ): Promise<PoolState[]> {
218
- try {
219
- const multiCallFeeData = pairs.map(pair =>
220
- this.getFeesMultiCallData(pair),
221
- );
222
- const calldata = pairs
223
- .map((pair, i) => {
224
- let calldata = [
225
- {
226
- target: pair.token0.address,
227
- callData: erc20Iface.encodeFunctionData('balanceOf', [
228
- pair.exchange!,
229
- ]),
230
- },
231
- {
232
- target: pair.token1.address,
233
- callData: erc20Iface.encodeFunctionData('balanceOf', [
234
- pair.exchange!,
235
- ]),
236
- },
237
- ];
238
- if (this.isDynamicFees) calldata.push(multiCallFeeData[i]!.callEntry);
239
- return calldata;
240
- })
241
- .flat();
242
-
243
- const data: { returnData: any[] } =
244
- await this.dexHelper.multiContract.methods
245
- .aggregate(calldata)
246
- .call({}, blockNumber);
247
-
248
- const returnData = _.chunk(data.returnData, this.isDynamicFees ? 3 : 2);
249
-
250
- return pairs.map((pair, i) => ({
251
- reserves0: defaultAbiCoder
252
- .decode(['uint256'], returnData[i][0])[0]
253
- .toString(),
254
- reserves1: defaultAbiCoder
255
- .decode(['uint256'], returnData[i][1])[0]
256
- .toString(),
257
- feeCode: this.isDynamicFees
258
- ? multiCallFeeData[i]!.callDecoder(returnData[i][2])
259
- : (pair.stable ? this.stableFee : this.volatileFee) || this.feeCode,
260
- }));
261
- } catch (e) {
262
- this.logger.error(
263
- `Error_getManyPoolReserves could not get reserves with error:`,
264
- e,
265
- );
266
- return [];
267
- }
268
- }
269
-
270
- async getSellPrice(
271
- priceParams: SolidlyPoolOrderedParams,
272
- srcAmount: bigint,
273
- ): Promise<bigint> {
274
- return priceParams.stable
275
- ? SolidlyStablePool.getSellPrice(priceParams, srcAmount, this.feeFactor)
276
- : Uniswapv2ConstantProductPool.getSellPrice(
277
- priceParams,
278
- srcAmount,
279
- this.feeFactor,
280
- );
281
- }
282
-
283
- async getBuyPrice(
284
- priceParams: SolidlyPoolOrderedParams,
285
- srcAmount: bigint,
286
- ): Promise<bigint> {
287
- if (priceParams.stable) throw new Error(`Buy not supported`);
288
- return Uniswapv2ConstantProductPool.getBuyPrice(
289
- priceParams,
290
- srcAmount,
291
- this.feeFactor,
292
- );
293
- }
294
-
295
- async getPricesVolume(
296
- _from: Token,
297
- _to: Token,
298
- amounts: bigint[],
299
- side: SwapSide,
300
- blockNumber: number,
301
- // list of pool identifiers to use for pricing, if undefined use all pools
302
- limitPools?: string[],
303
- transferFees: TransferFeeParams = {
304
- srcFee: 0,
305
- destFee: 0,
306
- srcDexFee: 0,
307
- destDexFee: 0,
308
- },
309
- ): Promise<ExchangePrices<UniswapV2Data> | null> {
310
- try {
311
- if (side === SwapSide.BUY) return null; // Buy side not implemented yet
312
- const from = this.dexHelper.config.wrapETH(_from);
313
- const to = this.dexHelper.config.wrapETH(_to);
314
-
315
- if (from.address.toLowerCase() === to.address.toLowerCase()) {
316
- return null;
317
- }
318
-
319
- const tokenAddress = [
320
- from.address.toLowerCase(),
321
- to.address.toLowerCase(),
322
- ]
323
- .sort((a, b) => (a > b ? 1 : -1))
324
- .join('_');
325
-
326
- await this.batchCatchUpPairs([[from, to]], blockNumber);
327
-
328
- const resultPromises = [false, true].map(async stable => {
329
- // We don't support fee on transfer for stable pools yet
330
- if (
331
- stable &&
332
- (transferFees.srcFee !== 0 || transferFees.srcDexFee !== 0)
333
- ) {
334
- return null;
335
- }
336
-
337
- const poolIdentifier =
338
- `${this.dexKey}_${tokenAddress}` + this.poolPostfix(stable);
339
-
340
- if (limitPools && limitPools.every(p => p !== poolIdentifier))
341
- return null;
342
-
343
- const isSell = side === SwapSide.SELL;
344
- const pairParam = await this.getSolidlyPairOrderedParams(
345
- from,
346
- to,
347
- blockNumber,
348
- stable,
349
- transferFees.srcDexFee,
350
- );
351
-
352
- if (!pairParam) return null;
353
-
354
- const unitAmount = getBigIntPow(
355
- // @ts-expect-error Buy side is not implemented yet
356
- side === SwapSide.BUY ? to.decimals : from.decimals,
357
- );
358
-
359
- const [unitVolumeWithFee, ...amountsWithFee] = applyTransferFee(
360
- [unitAmount, ...amounts],
361
- side,
362
- isSell ? transferFees.srcFee : transferFees.destFee,
363
- isSell ? SRC_TOKEN_PARASWAP_TRANSFERS : DEST_TOKEN_PARASWAP_TRANSFERS,
364
- );
365
-
366
- const unit =
367
- // @ts-expect-error Buy side is not implemented yet
368
- side === SwapSide.BUY
369
- ? await this.getBuyPricePath(unitVolumeWithFee, [pairParam])
370
- : await this.getSellPricePath(unitVolumeWithFee, [pairParam]);
371
-
372
- const prices =
373
- // @ts-expect-error Buy side is not implemented yet
374
- side === SwapSide.BUY
375
- ? await Promise.all(
376
- amountsWithFee.map(amount =>
377
- amount === 0n
378
- ? 0n
379
- : this.getBuyPricePath(amount, [pairParam]),
380
- ),
381
- )
382
- : await Promise.all(
383
- amountsWithFee.map(amount =>
384
- amount === 0n
385
- ? 0n
386
- : this.getSellPricePath(amount, [pairParam]),
387
- ),
388
- );
389
-
390
- const [unitOutWithFee, ...outputsWithFee] = applyTransferFee(
391
- [unit, ...prices],
392
- side,
393
- // This part is confusing, because we treat differently SELL and BUY fees
394
- // If Buy, we should apply transfer fee on srcToken on top of dexFee applied earlier
395
- // But for Sell we should apply only one dexFee
396
- isSell ? transferFees.destDexFee : transferFees.srcFee,
397
- isSell ? this.DEST_TOKEN_DEX_TRANSFERS : SRC_TOKEN_PARASWAP_TRANSFERS,
398
- );
399
-
400
- return {
401
- prices: outputsWithFee,
402
- unit: unitOutWithFee,
403
- data: {
404
- router: this.router,
405
- path: [from.address.toLowerCase(), to.address.toLowerCase()],
406
- factory: this.factoryAddress,
407
- initCode: this.initCode,
408
- feeFactor: this.feeFactor,
409
- isFeeTokenInRoute: Object.values(transferFees).some(f => f !== 0),
410
- pools: [
411
- {
412
- address: pairParam.exchange,
413
- fee: parseInt(pairParam.fee),
414
- direction: pairParam.direction,
415
- },
416
- ],
417
- },
418
- exchange: this.dexKey,
419
- poolIdentifier,
420
- gasCost: this.poolGasCost,
421
- poolAddresses: [pairParam.exchange],
422
- };
423
- });
424
-
425
- const resultPools = (await Promise.all(
426
- resultPromises,
427
- )) as ExchangePrices<UniswapV2Data>;
428
- const resultPoolsFiltered = resultPools.filter(item => !!item); // filter null elements
429
- return resultPoolsFiltered.length > 0 ? resultPoolsFiltered : null;
430
- } catch (e) {
431
- if (blockNumber === 0)
432
- this.logger.error(
433
- `Error_getPricesVolume: Aurelius block manager not yet instantiated`,
434
- );
435
- this.logger.error(`Error_getPrices:`, e);
436
- return null;
437
- }
438
- }
439
-
440
- async getTopPoolsForToken(
441
- tokenAddress: Address,
442
- count: number,
443
- ): Promise<PoolLiquidity[]> {
444
- if (!this.subgraphURL) return [];
445
-
446
- let stableFieldKey = '';
447
-
448
- if (this.dexKey.toLowerCase() === 'solidly') {
449
- stableFieldKey = 'stable';
450
- } else if (this.dexKey.toLowerCase() !== 'solidlyv2') {
451
- stableFieldKey = 'isStable';
452
- }
453
-
454
- const query = `query ($token: Bytes!, $count: Int) {
455
- pools0: pairs(first: $count, orderBy: reserveUSD, orderDirection: desc, where: {token0: $token, reserve0_gt: 1, reserve1_gt: 1}) {
456
- id
457
- ${stableFieldKey}
458
- token0 {
459
- id
460
- decimals
461
- }
462
- token1 {
463
- id
464
- decimals
465
- }
466
- reserveUSD
467
- }
468
- pools1: pairs(first: $count, orderBy: reserveUSD, orderDirection: desc, where: {token1: $token, reserve0_gt: 1, reserve1_gt: 1}) {
469
- id
470
- ${stableFieldKey}
471
- token0 {
472
- id
473
- decimals
474
- }
475
- token1 {
476
- id
477
- decimals
478
- }
479
- reserveUSD
480
- }
481
- }`;
482
-
483
- const { data } = await this.dexHelper.httpRequest.post(
484
- this.subgraphURL,
485
- {
486
- query,
487
- variables: { token: tokenAddress.toLowerCase(), count },
488
- },
489
- SUBGRAPH_TIMEOUT,
490
- );
491
-
492
- if (!(data && data.pools0 && data.pools1))
493
- throw new Error("Couldn't fetch the pools from the subgraph");
494
- const pools0 = _.map(data.pools0, pool => ({
495
- exchange: this.dexKey,
496
- stable: pool[stableFieldKey],
497
- address: pool.id.toLowerCase(),
498
- connectorTokens: [
499
- {
500
- address: pool.token1.id.toLowerCase(),
501
- decimals: parseInt(pool.token1.decimals),
502
- },
503
- ],
504
- liquidityUSD: parseFloat(pool.reserveUSD),
505
- }));
506
-
507
- const pools1 = _.map(data.pools1, pool => ({
508
- exchange: this.dexKey,
509
- stable: pool[stableFieldKey],
510
- address: pool.id.toLowerCase(),
511
- connectorTokens: [
512
- {
513
- address: pool.token0.id.toLowerCase(),
514
- decimals: parseInt(pool.token0.decimals),
515
- },
516
- ],
517
- liquidityUSD: parseFloat(pool.reserveUSD),
518
- }));
519
-
520
- return _.slice(
521
- _.sortBy(_.concat(pools0, pools1), [pool => -1 * pool.liquidityUSD]),
522
- 0,
523
- count,
524
- );
525
- }
526
-
527
- // Same as at uniswap-v2-pool.json, but extended with decimals and stable
528
- async getSolidlyPairOrderedParams(
529
- from: Token,
530
- to: Token,
531
- blockNumber: number,
532
- stable: boolean,
533
- tokenDexTransferFee: number,
534
- ): Promise<SolidlyPoolOrderedParams | null> {
535
- const pair = await this.findSolidlyPair(from, to, stable);
536
- if (!(pair && pair.pool && pair.exchange)) return null;
537
- const pairState = pair.pool.getState(blockNumber);
538
-
539
- if (!pairState) {
540
- this.logger.error(
541
- `Error_orderPairParams expected reserves, got none (maybe the pool doesn't exist) ${
542
- from.symbol || from.address
543
- } ${to.symbol || to.address}`,
544
- );
545
- return null;
546
- }
547
-
548
- const fee = (pairState.feeCode + tokenDexTransferFee).toString();
549
- const pairReversed =
550
- pair.token1.address.toLowerCase() === from.address.toLowerCase();
551
- if (pairReversed) {
552
- return {
553
- tokenIn: from.address,
554
- tokenOut: to.address,
555
- reservesIn: pairState.reserves1,
556
- reservesOut: pairState.reserves0,
557
- fee,
558
- direction: false,
559
- exchange: pair.exchange,
560
- decimalsIn: from.decimals,
561
- decimalsOut: to.decimals,
562
- stable,
563
- };
564
- }
565
- return {
566
- tokenIn: from.address,
567
- tokenOut: to.address,
568
- reservesIn: pairState.reserves0,
569
- reservesOut: pairState.reserves1,
570
- fee,
571
- direction: true,
572
- exchange: pair.exchange,
573
- decimalsIn: from.decimals,
574
- decimalsOut: to.decimals,
575
- stable,
576
- };
577
- }
578
-
579
- async getPoolIdentifiers(
580
- _from: Token,
581
- _to: Token,
582
- side: SwapSide,
583
- blockNumber: number,
584
- ): Promise<string[]> {
585
- if (side === SwapSide.BUY) return [];
586
-
587
- const from = this.dexHelper.config.wrapETH(_from);
588
- const to = this.dexHelper.config.wrapETH(_to);
589
-
590
- if (from.address.toLowerCase() === to.address.toLowerCase()) {
591
- return [];
592
- }
593
-
594
- const tokenAddress = [from.address.toLowerCase(), to.address.toLowerCase()]
595
- .sort((a, b) => (a > b ? 1 : -1))
596
- .join('_');
597
-
598
- const poolIdentifier = `${this.dexKey}_${tokenAddress}`;
599
- const poolIdentifierUniswap = poolIdentifier + this.poolPostfix(false);
600
- const poolIdentifierStable = poolIdentifier + this.poolPostfix(true);
601
- return [poolIdentifierUniswap, poolIdentifierStable];
602
- }
603
-
604
- poolPostfix(stable: boolean) {
605
- return stable ? 'S' : 'U';
606
- }
607
-
608
- async getSimpleParam(
609
- src: Address,
610
- dest: Address,
611
- srcAmount: NumberAsString,
612
- destAmount: NumberAsString,
613
- data: UniswapData,
614
- side: SwapSide,
615
- ): Promise<SimpleExchangeParam> {
616
- if (side === SwapSide.BUY) throw new Error(`Buy not supported`);
617
- return super.getSimpleParam(src, dest, srcAmount, destAmount, data, side);
618
- }
619
-
620
- getAdapterParam(
621
- srcToken: Address,
622
- destToken: Address,
623
- srcAmount: NumberAsString,
624
- toAmount: NumberAsString, // required for buy case
625
- data: SolidlyData,
626
- side: SwapSide,
627
- ): AdapterExchangeParam {
628
- if (side === SwapSide.BUY) throw new Error(`Buy not supported`);
629
- const pools = encodePools(data.pools, this.feeFactor);
630
- const weth = this.getWETHAddress(srcToken, destToken, data.wethAddress);
631
- const payload = this.abiCoder.encodeParameter(
632
- {
633
- ParentStruct: {
634
- weth: 'address',
635
- pools: 'uint256[]',
636
- isFeeTokenInRoute: 'bool',
637
- },
638
- },
639
- { weth, pools, isFeeTokenInRoute: data.isFeeTokenInRoute },
640
- );
641
- return {
642
- targetExchange: data.router,
643
- payload,
644
- networkFee: '0',
645
- };
646
- }
647
-
648
- getDexParam(
649
- srcToken: Address,
650
- destToken: Address,
651
- srcAmount: NumberAsString,
652
- destAmount: NumberAsString,
653
- recipient: Address,
654
- data: SolidlyData,
655
- side: SwapSide,
656
- ): DexExchangeParam {
657
- if (side === SwapSide.BUY) throw new Error(`Buy not supported`);
658
- let exchangeDataTypes = ['bytes4', 'bytes32'];
659
-
660
- let exchangeDataToPack = [
661
- hexZeroPad(hexlify(0), 4),
662
- hexZeroPad(hexlify(data.pools.length), 32),
663
- ];
664
-
665
- const pools = encodePools(data.pools, this.feeFactor);
666
- pools.forEach(pool => {
667
- exchangeDataTypes.push('bytes32');
668
- exchangeDataToPack.push(hexZeroPad(hexlify(BigNumber.from(pool)), 32));
669
- });
670
-
671
- const exchangeData = solidityPack(exchangeDataTypes, exchangeDataToPack);
672
-
673
- return {
674
- needWrapNative: this.needWrapNative,
675
- dexFuncHasRecipient: true,
676
- exchangeData,
677
- targetExchange: recipient,
678
- specialDexFlag: data.isFeeTokenInRoute
679
- ? SpecialDex.SWAP_ON_DYSTOPIA_UNISWAP_V2_FORK_WITH_FEE
680
- : SpecialDex.SWAP_ON_DYSTOPIA_UNISWAP_V2_FORK,
681
- transferSrcTokenBeforeSwap: data.isFeeTokenInRoute
682
- ? undefined
683
- : data.pools[0].address,
684
- returnAmountPos: undefined,
685
- };
686
- }
687
- }
1
+ import { UniswapV2 } from '../uniswap-v2/uniswap-v2';
2
+ import {
3
+ Network,
4
+ NULL_ADDRESS,
5
+ SUBGRAPH_TIMEOUT,
6
+ DEST_TOKEN_PARASWAP_TRANSFERS,
7
+ SRC_TOKEN_PARASWAP_TRANSFERS,
8
+ } from '../../constants';
9
+ import {
10
+ AdapterExchangeParam,
11
+ Address,
12
+ DexExchangeParam,
13
+ ExchangePrices,
14
+ PoolLiquidity,
15
+ SimpleExchangeParam,
16
+ Token,
17
+ TransferFeeParams,
18
+ } from '../../types';
19
+ import { IDexHelper } from '../../dex-helper';
20
+ import erc20ABI from '../../abi/erc20.json';
21
+ import { UniswapData, UniswapV2Data } from '../uniswap-v2/types';
22
+ import { getBigIntPow, getDexKeysWithNetwork } from '../../utils';
23
+ import solidlyFactoryABI from '../../abi/solidly/SolidlyFactory.json';
24
+ import solidlyPair from '../../abi/solidly/SolidlyPair.json';
25
+ import _ from 'lodash';
26
+ import { NumberAsString, SwapSide } from '@paraswap/core';
27
+ import { Interface, AbiCoder } from '@ethersproject/abi';
28
+ import { SolidlyStablePool } from './solidly-stable-pool';
29
+ import { Uniswapv2ConstantProductPool } from '../uniswap-v2/uniswap-v2-constant-product-pool';
30
+ import {
31
+ PoolState,
32
+ SolidlyData,
33
+ SolidlyPair,
34
+ SolidlyPool,
35
+ SolidlyPoolOrderedParams,
36
+ } from './types';
37
+ import { SolidlyConfig, Adapters } from './config';
38
+ import { applyTransferFee } from '../../lib/token-transfer-fee';
39
+ import {
40
+ hexDataLength,
41
+ hexZeroPad,
42
+ hexlify,
43
+ id,
44
+ solidityPack,
45
+ } from 'ethers/lib/utils';
46
+ import { BigNumber } from 'ethers';
47
+ import { Flag, SpecialDex } from '../../executor/types';
48
+
49
+ const erc20Iface = new Interface(erc20ABI);
50
+ const solidlyPairIface = new Interface(solidlyPair);
51
+ const defaultAbiCoder = new AbiCoder();
52
+
53
+ function encodePools(
54
+ pools: SolidlyPool[],
55
+ feeFactor: number,
56
+ ): NumberAsString[] {
57
+ return pools.map(({ fee, direction, address }) => {
58
+ return (
59
+ (BigInt(feeFactor - fee) << 161n) +
60
+ ((direction ? 0n : 1n) << 160n) +
61
+ BigInt(address)
62
+ ).toString();
63
+ });
64
+ }
65
+
66
+ export class Solidly extends UniswapV2 {
67
+ pairs: { [key: string]: SolidlyPair } = {};
68
+ stableFee?: number;
69
+ volatileFee?: number;
70
+
71
+ readonly isFeeOnTransferSupported: boolean = true;
72
+ readonly SRC_TOKEN_DEX_TRANSFERS = 1;
73
+ readonly DEST_TOKEN_DEX_TRANSFERS = 1;
74
+
75
+ public static dexKeysWithNetwork: { key: string; networks: Network[] }[] =
76
+ getDexKeysWithNetwork(
77
+ _.omit(SolidlyConfig, [
78
+ 'Velodrome',
79
+ 'VelodromeV2',
80
+ 'Aerodrome',
81
+ 'SpiritSwapV2',
82
+ 'SolidlyV2',
83
+ 'Thena',
84
+ 'Chronos',
85
+ 'Ramses',
86
+ 'Equalizer',
87
+ 'Velocimeter',
88
+ 'Usdfi',
89
+ 'PharaohV1',
90
+ ]),
91
+ );
92
+
93
+ constructor(
94
+ protected network: Network,
95
+ dexKey: string,
96
+ protected dexHelper: IDexHelper,
97
+ isDynamicFees = false,
98
+ factoryAddress?: Address,
99
+ subgraphURL?: string,
100
+ initCode?: string,
101
+ feeCode?: number,
102
+ poolGasCost?: number,
103
+ routerAddress?: Address,
104
+ ) {
105
+ super(
106
+ network,
107
+ dexKey,
108
+ dexHelper,
109
+ isDynamicFees,
110
+ factoryAddress !== undefined
111
+ ? factoryAddress
112
+ : SolidlyConfig[dexKey][network].factoryAddress,
113
+ subgraphURL === ''
114
+ ? undefined
115
+ : subgraphURL !== undefined
116
+ ? subgraphURL
117
+ : SolidlyConfig[dexKey][network].subgraphURL,
118
+ initCode !== undefined
119
+ ? initCode
120
+ : SolidlyConfig[dexKey][network].initCode,
121
+ feeCode !== undefined ? feeCode : SolidlyConfig[dexKey][network].feeCode,
122
+ poolGasCost !== undefined
123
+ ? poolGasCost
124
+ : SolidlyConfig[dexKey][network].poolGasCost,
125
+ solidlyPairIface,
126
+ Adapters[network] || undefined,
127
+ );
128
+
129
+ this.stableFee = SolidlyConfig[dexKey][network].stableFee;
130
+ this.volatileFee = SolidlyConfig[dexKey][network].volatileFee;
131
+
132
+ this.factory = new dexHelper.web3Provider.eth.Contract(
133
+ solidlyFactoryABI as any,
134
+ factoryAddress !== undefined
135
+ ? factoryAddress
136
+ : SolidlyConfig[dexKey][network].factoryAddress,
137
+ );
138
+
139
+ this.router =
140
+ routerAddress !== undefined
141
+ ? routerAddress
142
+ : SolidlyConfig[dexKey][network].router || '';
143
+
144
+ this.feeFactor = SolidlyConfig[dexKey][network].feeFactor || this.feeFactor;
145
+ }
146
+
147
+ async findSolidlyPair(from: Token, to: Token, stable: boolean) {
148
+ if (from.address.toLowerCase() === to.address.toLowerCase()) return null;
149
+ const [token0, token1] =
150
+ from.address.toLowerCase() < to.address.toLowerCase()
151
+ ? [from, to]
152
+ : [to, from];
153
+
154
+ const typePostfix = this.poolPostfix(stable);
155
+ const key = `${token0.address.toLowerCase()}-${token1.address.toLowerCase()}-${typePostfix}`;
156
+ let pair = this.pairs[key];
157
+ if (pair) return pair;
158
+
159
+ let exchange = await this.factory.methods
160
+ // Solidly has additional boolean parameter "StablePool"
161
+ // At first we look for uniswap-like volatile pool
162
+ .getPair(token0.address, token1.address, stable)
163
+ .call();
164
+
165
+ if (exchange === NULL_ADDRESS) {
166
+ pair = { token0, token1, stable };
167
+ } else {
168
+ pair = { token0, token1, exchange, stable };
169
+ }
170
+ this.pairs[key] = pair;
171
+ return pair;
172
+ }
173
+
174
+ async batchCatchUpPairs(pairs: [Token, Token][], blockNumber: number) {
175
+ if (!blockNumber) return;
176
+ const pairsToFetch: SolidlyPair[] = [];
177
+ for (const _pair of pairs) {
178
+ for (const stable of [false, true]) {
179
+ const pair = await this.findSolidlyPair(_pair[0], _pair[1], stable);
180
+ if (!(pair && pair.exchange)) continue;
181
+ if (!pair.pool) {
182
+ pairsToFetch.push(pair);
183
+ } else if (!pair.pool.getState(blockNumber)) {
184
+ pairsToFetch.push(pair);
185
+ }
186
+ }
187
+ }
188
+
189
+ if (!pairsToFetch.length) return;
190
+
191
+ const reserves = await this.getManyPoolReserves(pairsToFetch, blockNumber);
192
+
193
+ if (reserves.length !== pairsToFetch.length) {
194
+ this.logger.error(
195
+ `Error_getManyPoolReserves didn't get any pool reserves`,
196
+ );
197
+ }
198
+
199
+ for (let i = 0; i < pairsToFetch.length; i++) {
200
+ const pairState = reserves[i];
201
+ const pair = pairsToFetch[i];
202
+ if (!pair.pool) {
203
+ await this.addPool(
204
+ pair,
205
+ pairState.reserves0,
206
+ pairState.reserves1,
207
+ pairState.feeCode,
208
+ blockNumber,
209
+ );
210
+ } else pair.pool.setState(pairState, blockNumber);
211
+ }
212
+ }
213
+
214
+ async getManyPoolReserves(
215
+ pairs: SolidlyPair[],
216
+ blockNumber: number,
217
+ ): Promise<PoolState[]> {
218
+ try {
219
+ const multiCallFeeData = pairs.map(pair =>
220
+ this.getFeesMultiCallData(pair),
221
+ );
222
+ const calldata = pairs
223
+ .map((pair, i) => {
224
+ let calldata = [
225
+ {
226
+ target: pair.token0.address,
227
+ callData: erc20Iface.encodeFunctionData('balanceOf', [
228
+ pair.exchange!,
229
+ ]),
230
+ },
231
+ {
232
+ target: pair.token1.address,
233
+ callData: erc20Iface.encodeFunctionData('balanceOf', [
234
+ pair.exchange!,
235
+ ]),
236
+ },
237
+ ];
238
+ if (this.isDynamicFees) calldata.push(multiCallFeeData[i]!.callEntry);
239
+ return calldata;
240
+ })
241
+ .flat();
242
+
243
+ const data: { returnData: any[] } =
244
+ await this.dexHelper.multiContract.methods
245
+ .aggregate(calldata)
246
+ .call({}, blockNumber);
247
+
248
+ const returnData = _.chunk(data.returnData, this.isDynamicFees ? 3 : 2);
249
+
250
+ return pairs.map((pair, i) => ({
251
+ reserves0: defaultAbiCoder
252
+ .decode(['uint256'], returnData[i][0])[0]
253
+ .toString(),
254
+ reserves1: defaultAbiCoder
255
+ .decode(['uint256'], returnData[i][1])[0]
256
+ .toString(),
257
+ feeCode: this.isDynamicFees
258
+ ? multiCallFeeData[i]!.callDecoder(returnData[i][2])
259
+ : (pair.stable ? this.stableFee : this.volatileFee) || this.feeCode,
260
+ }));
261
+ } catch (e) {
262
+ this.logger.error(
263
+ `Error_getManyPoolReserves could not get reserves with error:`,
264
+ e,
265
+ );
266
+ return [];
267
+ }
268
+ }
269
+
270
+ async getSellPrice(
271
+ priceParams: SolidlyPoolOrderedParams,
272
+ srcAmount: bigint,
273
+ ): Promise<bigint> {
274
+ return priceParams.stable
275
+ ? SolidlyStablePool.getSellPrice(priceParams, srcAmount, this.feeFactor)
276
+ : Uniswapv2ConstantProductPool.getSellPrice(
277
+ priceParams,
278
+ srcAmount,
279
+ this.feeFactor,
280
+ );
281
+ }
282
+
283
+ async getBuyPrice(
284
+ priceParams: SolidlyPoolOrderedParams,
285
+ srcAmount: bigint,
286
+ ): Promise<bigint> {
287
+ if (priceParams.stable) throw new Error(`Buy not supported`);
288
+ return Uniswapv2ConstantProductPool.getBuyPrice(
289
+ priceParams,
290
+ srcAmount,
291
+ this.feeFactor,
292
+ );
293
+ }
294
+
295
+ async getPricesVolume(
296
+ _from: Token,
297
+ _to: Token,
298
+ amounts: bigint[],
299
+ side: SwapSide,
300
+ blockNumber: number,
301
+ // list of pool identifiers to use for pricing, if undefined use all pools
302
+ limitPools?: string[],
303
+ transferFees: TransferFeeParams = {
304
+ srcFee: 0,
305
+ destFee: 0,
306
+ srcDexFee: 0,
307
+ destDexFee: 0,
308
+ },
309
+ ): Promise<ExchangePrices<UniswapV2Data> | null> {
310
+ try {
311
+ if (side === SwapSide.BUY) return null; // Buy side not implemented yet
312
+ const from = this.dexHelper.config.wrapETH(_from);
313
+ const to = this.dexHelper.config.wrapETH(_to);
314
+
315
+ if (from.address.toLowerCase() === to.address.toLowerCase()) {
316
+ return null;
317
+ }
318
+
319
+ const tokenAddress = [
320
+ from.address.toLowerCase(),
321
+ to.address.toLowerCase(),
322
+ ]
323
+ .sort((a, b) => (a > b ? 1 : -1))
324
+ .join('_');
325
+
326
+ await this.batchCatchUpPairs([[from, to]], blockNumber);
327
+
328
+ const resultPromises = [false, true].map(async stable => {
329
+ // We don't support fee on transfer for stable pools yet
330
+ if (
331
+ stable &&
332
+ (transferFees.srcFee !== 0 || transferFees.srcDexFee !== 0)
333
+ ) {
334
+ return null;
335
+ }
336
+
337
+ const poolIdentifier =
338
+ `${this.dexKey}_${tokenAddress}` + this.poolPostfix(stable);
339
+
340
+ if (limitPools && limitPools.every(p => p !== poolIdentifier))
341
+ return null;
342
+
343
+ const isSell = side === SwapSide.SELL;
344
+ const pairParam = await this.getSolidlyPairOrderedParams(
345
+ from,
346
+ to,
347
+ blockNumber,
348
+ stable,
349
+ transferFees.srcDexFee,
350
+ );
351
+
352
+ if (!pairParam) return null;
353
+
354
+ const unitAmount = getBigIntPow(
355
+ // @ts-expect-error Buy side is not implemented yet
356
+ side === SwapSide.BUY ? to.decimals : from.decimals,
357
+ );
358
+
359
+ const [unitVolumeWithFee, ...amountsWithFee] = applyTransferFee(
360
+ [unitAmount, ...amounts],
361
+ side,
362
+ isSell ? transferFees.srcFee : transferFees.destFee,
363
+ isSell ? SRC_TOKEN_PARASWAP_TRANSFERS : DEST_TOKEN_PARASWAP_TRANSFERS,
364
+ );
365
+
366
+ const unit =
367
+ // @ts-expect-error Buy side is not implemented yet
368
+ side === SwapSide.BUY
369
+ ? await this.getBuyPricePath(unitVolumeWithFee, [pairParam])
370
+ : await this.getSellPricePath(unitVolumeWithFee, [pairParam]);
371
+
372
+ const prices =
373
+ // @ts-expect-error Buy side is not implemented yet
374
+ side === SwapSide.BUY
375
+ ? await Promise.all(
376
+ amountsWithFee.map(amount =>
377
+ amount === 0n
378
+ ? 0n
379
+ : this.getBuyPricePath(amount, [pairParam]),
380
+ ),
381
+ )
382
+ : await Promise.all(
383
+ amountsWithFee.map(amount =>
384
+ amount === 0n
385
+ ? 0n
386
+ : this.getSellPricePath(amount, [pairParam]),
387
+ ),
388
+ );
389
+
390
+ const [unitOutWithFee, ...outputsWithFee] = applyTransferFee(
391
+ [unit, ...prices],
392
+ side,
393
+ // This part is confusing, because we treat differently SELL and BUY fees
394
+ // If Buy, we should apply transfer fee on srcToken on top of dexFee applied earlier
395
+ // But for Sell we should apply only one dexFee
396
+ isSell ? transferFees.destDexFee : transferFees.srcFee,
397
+ isSell ? this.DEST_TOKEN_DEX_TRANSFERS : SRC_TOKEN_PARASWAP_TRANSFERS,
398
+ );
399
+
400
+ return {
401
+ prices: outputsWithFee,
402
+ unit: unitOutWithFee,
403
+ data: {
404
+ router: this.router,
405
+ path: [from.address.toLowerCase(), to.address.toLowerCase()],
406
+ factory: this.factoryAddress,
407
+ initCode: this.initCode,
408
+ feeFactor: this.feeFactor,
409
+ isFeeTokenInRoute: Object.values(transferFees).some(f => f !== 0),
410
+ pools: [
411
+ {
412
+ address: pairParam.exchange,
413
+ fee: parseInt(pairParam.fee),
414
+ direction: pairParam.direction,
415
+ },
416
+ ],
417
+ },
418
+ exchange: this.dexKey,
419
+ poolIdentifier,
420
+ gasCost: this.poolGasCost,
421
+ poolAddresses: [pairParam.exchange],
422
+ };
423
+ });
424
+
425
+ const resultPools = (await Promise.all(
426
+ resultPromises,
427
+ )) as ExchangePrices<UniswapV2Data>;
428
+ const resultPoolsFiltered = resultPools.filter(item => !!item); // filter null elements
429
+ return resultPoolsFiltered.length > 0 ? resultPoolsFiltered : null;
430
+ } catch (e) {
431
+ if (blockNumber === 0)
432
+ this.logger.error(
433
+ `Error_getPricesVolume: Aurelius block manager not yet instantiated`,
434
+ );
435
+ this.logger.error(`Error_getPrices:`, e);
436
+ return null;
437
+ }
438
+ }
439
+
440
+ async getTopPoolsForToken(
441
+ tokenAddress: Address,
442
+ count: number,
443
+ ): Promise<PoolLiquidity[]> {
444
+ if (!this.subgraphURL) return [];
445
+
446
+ let stableFieldKey = '';
447
+
448
+ if (this.dexKey.toLowerCase() === 'solidly') {
449
+ stableFieldKey = 'stable';
450
+ } else if (this.dexKey.toLowerCase() !== 'solidlyv2') {
451
+ stableFieldKey = 'isStable';
452
+ }
453
+
454
+ const query = `query ($token: Bytes!, $count: Int) {
455
+ pools0: pairs(first: $count, orderBy: reserveUSD, orderDirection: desc, where: {token0: $token, reserve0_gt: 1, reserve1_gt: 1}) {
456
+ id
457
+ ${stableFieldKey}
458
+ token0 {
459
+ id
460
+ decimals
461
+ }
462
+ token1 {
463
+ id
464
+ decimals
465
+ }
466
+ reserveUSD
467
+ }
468
+ pools1: pairs(first: $count, orderBy: reserveUSD, orderDirection: desc, where: {token1: $token, reserve0_gt: 1, reserve1_gt: 1}) {
469
+ id
470
+ ${stableFieldKey}
471
+ token0 {
472
+ id
473
+ decimals
474
+ }
475
+ token1 {
476
+ id
477
+ decimals
478
+ }
479
+ reserveUSD
480
+ }
481
+ }`;
482
+
483
+ const { data } = await this.dexHelper.httpRequest.post(
484
+ this.subgraphURL,
485
+ {
486
+ query,
487
+ variables: { token: tokenAddress.toLowerCase(), count },
488
+ },
489
+ SUBGRAPH_TIMEOUT,
490
+ );
491
+
492
+ if (!(data && data.pools0 && data.pools1))
493
+ throw new Error("Couldn't fetch the pools from the subgraph");
494
+ const pools0 = _.map(data.pools0, pool => ({
495
+ exchange: this.dexKey,
496
+ stable: pool[stableFieldKey],
497
+ address: pool.id.toLowerCase(),
498
+ connectorTokens: [
499
+ {
500
+ address: pool.token1.id.toLowerCase(),
501
+ decimals: parseInt(pool.token1.decimals),
502
+ },
503
+ ],
504
+ liquidityUSD: parseFloat(pool.reserveUSD),
505
+ }));
506
+
507
+ const pools1 = _.map(data.pools1, pool => ({
508
+ exchange: this.dexKey,
509
+ stable: pool[stableFieldKey],
510
+ address: pool.id.toLowerCase(),
511
+ connectorTokens: [
512
+ {
513
+ address: pool.token0.id.toLowerCase(),
514
+ decimals: parseInt(pool.token0.decimals),
515
+ },
516
+ ],
517
+ liquidityUSD: parseFloat(pool.reserveUSD),
518
+ }));
519
+
520
+ return _.slice(
521
+ _.sortBy(_.concat(pools0, pools1), [pool => -1 * pool.liquidityUSD]),
522
+ 0,
523
+ count,
524
+ );
525
+ }
526
+
527
+ // Same as at uniswap-v2-pool.json, but extended with decimals and stable
528
+ async getSolidlyPairOrderedParams(
529
+ from: Token,
530
+ to: Token,
531
+ blockNumber: number,
532
+ stable: boolean,
533
+ tokenDexTransferFee: number,
534
+ ): Promise<SolidlyPoolOrderedParams | null> {
535
+ const pair = await this.findSolidlyPair(from, to, stable);
536
+ if (!(pair && pair.pool && pair.exchange)) return null;
537
+ const pairState = pair.pool.getState(blockNumber);
538
+
539
+ if (!pairState) {
540
+ this.logger.error(
541
+ `Error_orderPairParams expected reserves, got none (maybe the pool doesn't exist) ${
542
+ from.symbol || from.address
543
+ } ${to.symbol || to.address}`,
544
+ );
545
+ return null;
546
+ }
547
+
548
+ const fee = (pairState.feeCode + tokenDexTransferFee).toString();
549
+ const pairReversed =
550
+ pair.token1.address.toLowerCase() === from.address.toLowerCase();
551
+ if (pairReversed) {
552
+ return {
553
+ tokenIn: from.address,
554
+ tokenOut: to.address,
555
+ reservesIn: pairState.reserves1,
556
+ reservesOut: pairState.reserves0,
557
+ fee,
558
+ direction: false,
559
+ exchange: pair.exchange,
560
+ decimalsIn: from.decimals,
561
+ decimalsOut: to.decimals,
562
+ stable,
563
+ };
564
+ }
565
+ return {
566
+ tokenIn: from.address,
567
+ tokenOut: to.address,
568
+ reservesIn: pairState.reserves0,
569
+ reservesOut: pairState.reserves1,
570
+ fee,
571
+ direction: true,
572
+ exchange: pair.exchange,
573
+ decimalsIn: from.decimals,
574
+ decimalsOut: to.decimals,
575
+ stable,
576
+ };
577
+ }
578
+
579
+ async getPoolIdentifiers(
580
+ _from: Token,
581
+ _to: Token,
582
+ side: SwapSide,
583
+ blockNumber: number,
584
+ ): Promise<string[]> {
585
+ if (side === SwapSide.BUY) return [];
586
+
587
+ const from = this.dexHelper.config.wrapETH(_from);
588
+ const to = this.dexHelper.config.wrapETH(_to);
589
+
590
+ if (from.address.toLowerCase() === to.address.toLowerCase()) {
591
+ return [];
592
+ }
593
+
594
+ const tokenAddress = [from.address.toLowerCase(), to.address.toLowerCase()]
595
+ .sort((a, b) => (a > b ? 1 : -1))
596
+ .join('_');
597
+
598
+ const poolIdentifier = `${this.dexKey}_${tokenAddress}`;
599
+ const poolIdentifierUniswap = poolIdentifier + this.poolPostfix(false);
600
+ const poolIdentifierStable = poolIdentifier + this.poolPostfix(true);
601
+ return [poolIdentifierUniswap, poolIdentifierStable];
602
+ }
603
+
604
+ poolPostfix(stable: boolean) {
605
+ return stable ? 'S' : 'U';
606
+ }
607
+
608
+ async getSimpleParam(
609
+ src: Address,
610
+ dest: Address,
611
+ srcAmount: NumberAsString,
612
+ destAmount: NumberAsString,
613
+ data: UniswapData,
614
+ side: SwapSide,
615
+ ): Promise<SimpleExchangeParam> {
616
+ if (side === SwapSide.BUY) throw new Error(`Buy not supported`);
617
+ return super.getSimpleParam(src, dest, srcAmount, destAmount, data, side);
618
+ }
619
+
620
+ getAdapterParam(
621
+ srcToken: Address,
622
+ destToken: Address,
623
+ srcAmount: NumberAsString,
624
+ toAmount: NumberAsString, // required for buy case
625
+ data: SolidlyData,
626
+ side: SwapSide,
627
+ ): AdapterExchangeParam {
628
+ if (side === SwapSide.BUY) throw new Error(`Buy not supported`);
629
+ const pools = encodePools(data.pools, this.feeFactor);
630
+ const weth = this.getWETHAddress(srcToken, destToken, data.wethAddress);
631
+ const payload = this.abiCoder.encodeParameter(
632
+ {
633
+ ParentStruct: {
634
+ weth: 'address',
635
+ pools: 'uint256[]',
636
+ isFeeTokenInRoute: 'bool',
637
+ },
638
+ },
639
+ { weth, pools, isFeeTokenInRoute: data.isFeeTokenInRoute },
640
+ );
641
+ return {
642
+ targetExchange: data.router,
643
+ payload,
644
+ networkFee: '0',
645
+ };
646
+ }
647
+
648
+ getDexParam(
649
+ srcToken: Address,
650
+ destToken: Address,
651
+ srcAmount: NumberAsString,
652
+ destAmount: NumberAsString,
653
+ recipient: Address,
654
+ data: SolidlyData,
655
+ side: SwapSide,
656
+ ): DexExchangeParam {
657
+ if (side === SwapSide.BUY) throw new Error(`Buy not supported`);
658
+ let exchangeDataTypes = ['bytes4', 'bytes32'];
659
+
660
+ let exchangeDataToPack = [
661
+ hexZeroPad(hexlify(0), 4),
662
+ hexZeroPad(hexlify(data.pools.length), 32),
663
+ ];
664
+
665
+ const pools = encodePools(data.pools, this.feeFactor);
666
+ pools.forEach(pool => {
667
+ exchangeDataTypes.push('bytes32');
668
+ exchangeDataToPack.push(hexZeroPad(hexlify(BigNumber.from(pool)), 32));
669
+ });
670
+
671
+ const exchangeData = solidityPack(exchangeDataTypes, exchangeDataToPack);
672
+
673
+ return {
674
+ needWrapNative: this.needWrapNative,
675
+ dexFuncHasRecipient: true,
676
+ exchangeData,
677
+ targetExchange: recipient,
678
+ specialDexFlag: data.isFeeTokenInRoute
679
+ ? SpecialDex.SWAP_ON_DYSTOPIA_UNISWAP_V2_FORK_WITH_FEE
680
+ : SpecialDex.SWAP_ON_DYSTOPIA_UNISWAP_V2_FORK,
681
+ transferSrcTokenBeforeSwap: data.isFeeTokenInRoute
682
+ ? undefined
683
+ : data.pools[0].address,
684
+ returnAmountPos: undefined,
685
+ };
686
+ }
687
+ }