@paraswap/dex-lib 2.42.23 → 2.42.24-smardex

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 (65) hide show
  1. package/build/abi/smardex/all/smardex-router.json +479 -0
  2. package/build/abi/smardex/layer-1/smardex-factory.json +176 -0
  3. package/build/abi/smardex/layer-1/smardex-pool.json +768 -0
  4. package/build/abi/smardex/layer-2/smardex-factory.json +265 -0
  5. package/build/abi/smardex/layer-2/smardex-pool.json +898 -0
  6. package/build/config.js +20 -10
  7. package/build/config.js.map +1 -1
  8. package/build/dex/dexalot/constants.js +1 -1
  9. package/build/dex/index.js +2 -0
  10. package/build/dex/index.js.map +1 -1
  11. package/build/dex/smardex/config.d.ts +4 -0
  12. package/build/dex/smardex/config.js +64 -0
  13. package/build/dex/smardex/config.js.map +1 -0
  14. package/build/dex/smardex/constants.d.ts +18 -0
  15. package/build/dex/smardex/constants.js +34 -0
  16. package/build/dex/smardex/constants.js.map +1 -0
  17. package/build/dex/smardex/sdk/constants.d.ts +6 -0
  18. package/build/dex/smardex/sdk/constants.js +13 -0
  19. package/build/dex/smardex/sdk/constants.js.map +1 -0
  20. package/build/dex/smardex/sdk/core.d.ts +113 -0
  21. package/build/dex/smardex/sdk/core.js +499 -0
  22. package/build/dex/smardex/sdk/core.js.map +1 -0
  23. package/build/dex/smardex/sdk/errors.d.ts +15 -0
  24. package/build/dex/smardex/sdk/errors.js +22 -0
  25. package/build/dex/smardex/sdk/errors.js.map +1 -0
  26. package/build/dex/smardex/sdk/types.d.ts +62 -0
  27. package/build/dex/smardex/sdk/types.js +3 -0
  28. package/build/dex/smardex/sdk/types.js.map +1 -0
  29. package/build/dex/smardex/sdk/utils.d.ts +44 -0
  30. package/build/dex/smardex/sdk/utils.js +133 -0
  31. package/build/dex/smardex/sdk/utils.js.map +1 -0
  32. package/build/dex/smardex/smardex-event-pool.d.ts +28 -0
  33. package/build/dex/smardex/smardex-event-pool.js +135 -0
  34. package/build/dex/smardex/smardex-event-pool.js.map +1 -0
  35. package/build/dex/smardex/smardex.d.ts +66 -0
  36. package/build/dex/smardex/smardex.js +567 -0
  37. package/build/dex/smardex/smardex.js.map +1 -0
  38. package/build/dex/smardex/types.d.ts +55 -0
  39. package/build/dex/smardex/types.js +3 -0
  40. package/build/dex/smardex/types.js.map +1 -0
  41. package/build/types.d.ts +1 -0
  42. package/package.json +1 -1
  43. package/src/abi/smardex/all/smardex-router.json +479 -0
  44. package/src/abi/smardex/layer-1/smardex-factory.json +176 -0
  45. package/src/abi/smardex/layer-1/smardex-pool.json +768 -0
  46. package/src/abi/smardex/layer-2/smardex-factory.json +265 -0
  47. package/src/abi/smardex/layer-2/smardex-pool.json +898 -0
  48. package/src/config.ts +21 -10
  49. package/src/dex/dexalot/constants.ts +1 -1
  50. package/src/dex/index.ts +2 -0
  51. package/src/dex/smardex/config.ts +67 -0
  52. package/src/dex/smardex/constants.ts +36 -0
  53. package/src/dex/smardex/sdk/constants.ts +10 -0
  54. package/src/dex/smardex/sdk/core.ts +832 -0
  55. package/src/dex/smardex/sdk/errors.ts +18 -0
  56. package/src/dex/smardex/sdk/types.ts +68 -0
  57. package/src/dex/smardex/sdk/utils.ts +156 -0
  58. package/src/dex/smardex/smardex-e2e.test.ts +275 -0
  59. package/src/dex/smardex/smardex-event-pool.ts +185 -0
  60. package/src/dex/smardex/smardex-events.test.ts +296 -0
  61. package/src/dex/smardex/smardex-integration.test.ts +258 -0
  62. package/src/dex/smardex/smardex.ts +842 -0
  63. package/src/dex/smardex/types.ts +69 -0
  64. package/src/types.ts +1 -0
  65. package/tests/constants-e2e.ts +25 -0
@@ -0,0 +1,842 @@
1
+ import { AbiCoder, Interface } from '@ethersproject/abi';
2
+ import { Contract } from 'web3-eth-contract';
3
+ import _ from 'lodash';
4
+ import {
5
+ DEST_TOKEN_PARASWAP_TRANSFERS,
6
+ ETHER_ADDRESS,
7
+ NULL_ADDRESS,
8
+ Network,
9
+ SRC_TOKEN_PARASWAP_TRANSFERS,
10
+ SwapSide,
11
+ } from '../../constants';
12
+ import {
13
+ AdapterExchangeParam,
14
+ Address,
15
+ ExchangePrices,
16
+ Logger,
17
+ NumberAsString,
18
+ PoolLiquidity,
19
+ PoolPrices,
20
+ SimpleExchangeParam,
21
+ Token,
22
+ TransferFeeParams,
23
+ TxInfo,
24
+ } from '../../types';
25
+ import { IDexHelper } from '../../dex-helper/index';
26
+ import {
27
+ SmardexFees,
28
+ SmardexPair,
29
+ SmardexPool,
30
+ SmardexPoolOrderedParams,
31
+ SmardexPoolState,
32
+ } from './types';
33
+ import { getBigIntPow, getDexKeysWithNetwork, isETHAddress } from '../../utils';
34
+ import SmardexFactoryLayerOneABI from '../../abi/smardex/layer-1/smardex-factory.json';
35
+ import SmardexFactoryLayerTwoABI from '../../abi/smardex/layer-2/smardex-factory.json';
36
+
37
+ import SmardexRouterABI from '../../abi/smardex/all/smardex-router.json';
38
+ import { SimpleExchange } from '../simple-exchange';
39
+ import {
40
+ SmardexRouterFunctions,
41
+ directSmardexFunctionName,
42
+ DefaultSmardexPoolGasCost,
43
+ SUBGRAPH_TIMEOUT,
44
+ FEES_LAYER_ONE,
45
+ } from '../smardex/constants';
46
+ import { SmardexData, SmardexParam } from '../smardex/types';
47
+ import { IDex } from '../..';
48
+ import * as CALLDATA_GAS_COST from '../../calldata-gas-cost';
49
+ import { Adapters, SmardexConfig } from './config';
50
+ import ParaSwapABI from '../../abi/IParaswap.json';
51
+ import { applyTransferFee } from '../../lib/token-transfer-fee';
52
+ import { computeAmountIn, computeAmountOut } from './sdk/core';
53
+ import { SmardexEventPool } from './smardex-event-pool';
54
+ import SmardexPoolLayerOneABI from '../../abi/smardex/layer-1/smardex-pool.json';
55
+ import SmardexPoolLayerTwoABI from '../../abi/smardex/layer-2/smardex-pool.json';
56
+
57
+ const smardexPoolL1 = new Interface(SmardexPoolLayerOneABI);
58
+ const smardexPoolL2 = new Interface(SmardexPoolLayerTwoABI);
59
+
60
+ const coder = new AbiCoder();
61
+
62
+ function encodePools(
63
+ pools: SmardexPool[],
64
+ // feeFactor: number,
65
+ ): NumberAsString[] {
66
+ return pools.map(({ fee, direction, address }) => {
67
+ return (
68
+ (BigInt(fee) << 161n) +
69
+ ((direction ? 0n : 1n) << 160n) +
70
+ BigInt(address)
71
+ ).toString();
72
+ });
73
+ }
74
+
75
+ export class Smardex
76
+ extends SimpleExchange
77
+ implements IDex<SmardexData, SmardexParam>
78
+ {
79
+ pairs: { [key: string]: SmardexPair } = {};
80
+ factory: Contract;
81
+
82
+ routerInterface: Interface;
83
+ exchangeRouterInterface: Interface;
84
+ static directFunctionName = directSmardexFunctionName;
85
+
86
+ factoryAddress: string;
87
+ routerAddress: string;
88
+
89
+ protected subgraphURL: string | undefined;
90
+ protected initCode: string;
91
+
92
+ logger: Logger;
93
+ readonly hasConstantPriceLargeAmounts = false;
94
+ readonly isFeeOnTransferSupported: boolean = true;
95
+ readonly SRC_TOKEN_DEX_TRANSFERS = 1;
96
+ readonly DEST_TOKEN_DEX_TRANSFERS = 1;
97
+
98
+ public static dexKeysWithNetwork: { key: string; networks: Network[] }[] =
99
+ getDexKeysWithNetwork(SmardexConfig);
100
+
101
+ constructor(
102
+ protected network: Network,
103
+ dexKey: string,
104
+ public dexHelper: IDexHelper,
105
+ protected adapters = Adapters[network] || {},
106
+ ) {
107
+ super(dexHelper, dexKey);
108
+ this.logger = dexHelper.getLogger(dexKey);
109
+ const config = SmardexConfig[dexKey];
110
+ this.routerAddress = config[network].router!;
111
+ this.factoryAddress = config[network].factoryAddress;
112
+ this.subgraphURL = config[network].subgraphURL;
113
+ this.initCode = config[network].initCode;
114
+ const factoryAbi = this.isLayer1()
115
+ ? SmardexFactoryLayerOneABI
116
+ : SmardexFactoryLayerTwoABI;
117
+ this.factory = new dexHelper.web3Provider.eth.Contract(
118
+ factoryAbi as any,
119
+ this.factoryAddress,
120
+ );
121
+ this.routerInterface = new Interface(ParaSwapABI);
122
+ this.exchangeRouterInterface = new Interface(SmardexRouterABI);
123
+ }
124
+
125
+ getAdapters(side: SwapSide): { name: string; index: number }[] | null {
126
+ return this.adapters[side] ? this.adapters[side] : null;
127
+ }
128
+
129
+ async getPoolIdentifiers(
130
+ _from: Token,
131
+ _to: Token,
132
+ side: SwapSide,
133
+ blockNumber: number,
134
+ ): Promise<string[]> {
135
+ const from = this.dexHelper.config.wrapETH(_from);
136
+ const to = this.dexHelper.config.wrapETH(_to);
137
+
138
+ if (from.address.toLowerCase() === to.address.toLowerCase()) {
139
+ return [];
140
+ }
141
+
142
+ const tokenAddress = [from.address.toLowerCase(), to.address.toLowerCase()]
143
+ .sort((a, b) => (BigInt(a) > BigInt(b) ? 1 : -1))
144
+ .join('_');
145
+
146
+ const poolIdentifier = `${this.dexKey}_${tokenAddress}`;
147
+ return [poolIdentifier];
148
+ }
149
+
150
+ async getPricesVolume(
151
+ _from: Token,
152
+ _to: Token,
153
+ amounts: bigint[],
154
+ side: SwapSide,
155
+ blockNumber: number,
156
+ // list of pool identifiers to use for pricing, if undefined use all pools
157
+ limitPools?: string[],
158
+ transferFees: TransferFeeParams = {
159
+ srcFee: 0,
160
+ destFee: 0,
161
+ srcDexFee: 0,
162
+ destDexFee: 0,
163
+ },
164
+ ): Promise<ExchangePrices<SmardexData> | null> {
165
+ try {
166
+ const from = this.dexHelper.config.wrapETH(_from);
167
+ const to = this.dexHelper.config.wrapETH(_to);
168
+
169
+ if (from.address.toLowerCase() === to.address.toLowerCase()) {
170
+ return null;
171
+ }
172
+
173
+ const tokenAddress = [
174
+ from.address.toLowerCase(),
175
+ to.address.toLowerCase(),
176
+ ]
177
+ .sort((a, b) => (BigInt(a) > BigInt(b) ? 1 : -1))
178
+ .join('_');
179
+
180
+ const poolIdentifier = `${this.dexKey}_${tokenAddress}`;
181
+
182
+ if (limitPools && limitPools.every(p => p !== poolIdentifier))
183
+ return null;
184
+
185
+ await this.batchCatchUpPairs([[from, to]], blockNumber);
186
+ const isSell = side === SwapSide.SELL;
187
+ const pairParam = await this.getPairOrderedParams(
188
+ from,
189
+ to,
190
+ blockNumber,
191
+ transferFees.srcDexFee,
192
+ );
193
+
194
+ if (!pairParam) return null;
195
+
196
+ const unitAmount = getBigIntPow(isSell ? from.decimals : to.decimals);
197
+
198
+ // SmarDex does not support Fees on Transfer Tokens
199
+ const [unitVolumeWithFee, ...amountsWithFee] = applyTransferFee(
200
+ [unitAmount, ...amounts],
201
+ side,
202
+ isSell ? transferFees.srcFee : transferFees.destFee,
203
+ isSell ? SRC_TOKEN_PARASWAP_TRANSFERS : DEST_TOKEN_PARASWAP_TRANSFERS,
204
+ );
205
+
206
+ const unit = isSell
207
+ ? await this.getSellPricePath(unitVolumeWithFee, [pairParam])
208
+ : await this.getBuyPricePath(unitVolumeWithFee, [pairParam]);
209
+
210
+ const prices = isSell
211
+ ? await Promise.all(
212
+ amountsWithFee.map(amount =>
213
+ this.getSellPricePath(amount, [pairParam]),
214
+ ),
215
+ )
216
+ : await Promise.all(
217
+ amountsWithFee.map(amount =>
218
+ this.getBuyPricePath(amount, [pairParam]),
219
+ ),
220
+ );
221
+
222
+ const [unitOutWithFee, ...outputsWithFee] = applyTransferFee(
223
+ [unit, ...prices],
224
+ side,
225
+ // This part is confusing, because we treat differently SELL and BUY fees
226
+ // If Buy, we should apply transfer fee on srcToken on top of dexFee applied earlier
227
+ // But for Sell we should apply only one dexFee
228
+ isSell ? transferFees.destDexFee : transferFees.srcFee,
229
+ isSell ? this.DEST_TOKEN_DEX_TRANSFERS : SRC_TOKEN_PARASWAP_TRANSFERS,
230
+ );
231
+
232
+ // As uniswapv2 just has one pool per token pair
233
+ return [
234
+ {
235
+ prices: outputsWithFee,
236
+ unit: unitOutWithFee,
237
+ data: {
238
+ deadline: Math.floor(new Date().getTime()) + 120,
239
+ receiver: this.augustusAddress,
240
+ router: this.routerAddress,
241
+ path: [from.address.toLowerCase(), to.address.toLowerCase()],
242
+ factory: this.factoryAddress,
243
+ initCode: this.initCode,
244
+ // feeFactor: this.feeFactor,
245
+ pools: [
246
+ {
247
+ address: pairParam.exchange,
248
+ fee: 0, // Smardex does not support Fees on Transfer Tokens
249
+ direction:
250
+ pairParam.fromToken.toLocaleLowerCase() ===
251
+ pairParam.token0.toLocaleLowerCase(),
252
+ },
253
+ ],
254
+ },
255
+ exchange: this.dexKey,
256
+ poolIdentifier,
257
+ gasCost: DefaultSmardexPoolGasCost,
258
+ poolAddresses: [pairParam.exchange],
259
+ },
260
+ ];
261
+ } catch (e) {
262
+ if (blockNumber === 0)
263
+ this.logger.error(
264
+ `Error_getPricesVolume: Aurelius block manager not yet instantiated`,
265
+ );
266
+ this.logger.error(`Error_getPrices:`, e);
267
+ return null;
268
+ }
269
+ }
270
+
271
+ async getBuyPricePath(
272
+ amount: bigint,
273
+ params: SmardexPoolOrderedParams[],
274
+ ): Promise<bigint> {
275
+ let price = amount;
276
+ for (const param of params.reverse()) {
277
+ price = await this.getBuyPrice(param, price);
278
+ }
279
+ return price;
280
+ }
281
+
282
+ async getSellPricePath(
283
+ amount: bigint,
284
+ params: SmardexPoolOrderedParams[],
285
+ ): Promise<bigint> {
286
+ let price = amount;
287
+ for (const param of params) {
288
+ price = await this.getSellPrice(param, price);
289
+ }
290
+ return price;
291
+ }
292
+
293
+ async getBuyPrice(
294
+ priceParams: SmardexPoolOrderedParams,
295
+ destAmount: bigint,
296
+ ): Promise<bigint> {
297
+ const amountIn = computeAmountIn(
298
+ priceParams.token0,
299
+ priceParams.token1,
300
+ priceParams.reserves0,
301
+ priceParams.reserves1,
302
+ priceParams.fictiveReserves0,
303
+ priceParams.fictiveReserves1,
304
+ destAmount,
305
+ priceParams.toToken,
306
+ +priceParams.priceAverageLastTimestamp,
307
+ priceParams.priceAverage0,
308
+ priceParams.priceAverage1,
309
+ priceParams.feesLP,
310
+ priceParams.feesPool,
311
+ ).amount;
312
+
313
+ // uncomment to log rates
314
+ // console.log("destAmount.", utils.formatEther(destAmount.toString()))
315
+ // console.log("amountIn", utils.formatEther(amountIn.toString()))
316
+
317
+ return BigInt(amountIn.toString());
318
+ }
319
+
320
+ async getSellPrice(
321
+ priceParams: SmardexPoolOrderedParams,
322
+ srcAmount: bigint,
323
+ ): Promise<bigint> {
324
+ const amountOut = computeAmountOut(
325
+ priceParams.token0,
326
+ priceParams.token1,
327
+ priceParams.reserves0,
328
+ priceParams.reserves1,
329
+ priceParams.fictiveReserves0,
330
+ priceParams.fictiveReserves1,
331
+ srcAmount,
332
+ priceParams.fromToken,
333
+ +priceParams.priceAverageLastTimestamp,
334
+ priceParams.priceAverage0,
335
+ priceParams.priceAverage1,
336
+ priceParams.feesLP,
337
+ priceParams.feesPool,
338
+ ).amount;
339
+
340
+ // uncomment to log rates
341
+ // console.log("srcAmount.", utils.formatEther(srcAmount.toString()))
342
+ // console.log("amountOut", utils.formatEther(amountOut.toString()))
343
+ return BigInt(amountOut.toString());
344
+ }
345
+
346
+ // Returns estimated gas cost of calldata for this DEX in multiSwap
347
+ getCalldataGasCost(_poolPrices: PoolPrices<SmardexData>): number | number[] {
348
+ return (
349
+ CALLDATA_GAS_COST.DEX_OVERHEAD +
350
+ CALLDATA_GAS_COST.LENGTH_SMALL +
351
+ // ParentStruct header
352
+ CALLDATA_GAS_COST.OFFSET_SMALL +
353
+ // ParentStruct -> weth
354
+ CALLDATA_GAS_COST.ADDRESS +
355
+ // ParentStruct -> pools[] header
356
+ CALLDATA_GAS_COST.OFFSET_SMALL +
357
+ // ParentStruct -> pools[]
358
+ CALLDATA_GAS_COST.LENGTH_SMALL +
359
+ // ParentStruct -> pools[0]
360
+ CALLDATA_GAS_COST.wordNonZeroBytes(22)
361
+ );
362
+ }
363
+
364
+ async findPair(from: Token, to: Token) {
365
+ if (from.address.toLowerCase() === to.address.toLowerCase()) return null;
366
+ const [token0, token1] =
367
+ BigInt(from.address.toLowerCase()) < BigInt(to.address.toLowerCase())
368
+ ? [from, to]
369
+ : [to, from];
370
+
371
+ const key = `${token0.address.toLowerCase()}-${token1.address.toLowerCase()}`;
372
+ let pair = this.pairs[key];
373
+ if (pair) return pair;
374
+ const exchange = await this.factory.methods
375
+ .getPair(token0.address, token1.address)
376
+ .call();
377
+ if (exchange === NULL_ADDRESS) {
378
+ pair = { token0, token1 };
379
+ } else {
380
+ pair = { token0, token1, exchange };
381
+ }
382
+ this.pairs[key] = pair;
383
+ return pair;
384
+ }
385
+
386
+ async batchCatchUpPairs(pairs: [Token, Token][], blockNumber: number) {
387
+ if (!blockNumber) return;
388
+ const pairsToFetch: SmardexPair[] = [];
389
+ for (const _pair of pairs) {
390
+ const pair = await this.findPair(_pair[0], _pair[1]);
391
+ if (!(pair && pair.exchange)) continue;
392
+ if (!pair.pool) {
393
+ pairsToFetch.push(pair);
394
+ } else if (!pair.pool.getState(blockNumber)) {
395
+ pairsToFetch.push(pair);
396
+ }
397
+ }
398
+
399
+ if (!pairsToFetch.length) return;
400
+
401
+ const reserves = await this.getManyPoolReserves(pairsToFetch, blockNumber);
402
+
403
+ if (reserves.length !== pairsToFetch.length) {
404
+ this.logger.error(
405
+ `Error_getManyPoolReserves didn't get any pool reserves`,
406
+ );
407
+ }
408
+
409
+ for (let i = 0; i < pairsToFetch.length; i++) {
410
+ const pairState = reserves[i];
411
+ const pair = pairsToFetch[i];
412
+ if (!pair.pool) {
413
+ await this.addPool(
414
+ pair,
415
+ pairState.reserves0,
416
+ pairState.reserves1,
417
+ pairState.fictiveReserves0,
418
+ pairState.fictiveReserves1,
419
+ pairState.priceAverage0,
420
+ pairState.priceAverage1,
421
+ pairState.feesLP,
422
+ pairState.feesPool,
423
+ blockNumber,
424
+ pairState.priceAverageLastTimestamp,
425
+ );
426
+ } else pair.pool.setState(pairState, blockNumber);
427
+ }
428
+ }
429
+
430
+ // On Smardex the fees are upgradable on layer 2.
431
+ public getFeesMultiCallData(pairAddress: string) {
432
+ if (this.isLayer1()) {
433
+ return null;
434
+ }
435
+ const callEntry = {
436
+ target: pairAddress,
437
+ callData: smardexPoolL2.encodeFunctionData('getPairFees'),
438
+ };
439
+ const callDecoder = (values: any[]): SmardexFees => {
440
+ const feesData = smardexPoolL2.decodeFunctionResult(
441
+ 'getPairFees',
442
+ values,
443
+ );
444
+ return {
445
+ feesLP: feesData.feesLP_.toBigInt(),
446
+ feesPool: feesData.feesPool_.toBigInt(),
447
+ };
448
+ };
449
+ return {
450
+ callEntry,
451
+ callDecoder,
452
+ };
453
+ }
454
+
455
+ protected async addPool(
456
+ pair: SmardexPair,
457
+ reserves0: string,
458
+ reserves1: string,
459
+ fictiveReserves0: string,
460
+ fictiveReserves1: string,
461
+ priceAverage0: string,
462
+ priceAverage1: string,
463
+ feesLp: bigint,
464
+ feesPool: bigint,
465
+ blockNumber: number,
466
+ priceAverageLastTimestamp: number,
467
+ ) {
468
+ const multiCallFeeData = this.getFeesMultiCallData(pair.exchange!);
469
+ pair.pool = new SmardexEventPool(
470
+ this.isLayer1() ? smardexPoolL1 : smardexPoolL2,
471
+ this.dexHelper,
472
+ pair.exchange!,
473
+ pair.token0,
474
+ pair.token1,
475
+ this.logger,
476
+ // For layer 2 we need to fetch the fees
477
+ multiCallFeeData?.callEntry,
478
+ multiCallFeeData?.callDecoder,
479
+ this.isLayer1(),
480
+ );
481
+ pair.pool.addressesSubscribed.push(pair.exchange!);
482
+
483
+ await pair.pool.initialize(blockNumber, {
484
+ state: {
485
+ reserves0,
486
+ reserves1,
487
+ fictiveReserves0,
488
+ fictiveReserves1,
489
+ priceAverage0,
490
+ priceAverage1,
491
+ feesLP: feesLp,
492
+ feesPool,
493
+ priceAverageLastTimestamp,
494
+ },
495
+ });
496
+ }
497
+
498
+ async getManyPoolReserves(
499
+ pairs: SmardexPair[],
500
+ blockNumber: number,
501
+ ): Promise<SmardexPoolState[]> {
502
+ try {
503
+ const multiCallFeeData = pairs.map(pair =>
504
+ this.getFeesMultiCallData(pair.exchange!),
505
+ );
506
+ const calldata = pairs
507
+ .map((pair, i) => {
508
+ let calldata = [
509
+ {
510
+ target: pair.exchange!,
511
+ callData: smardexPoolL1.encodeFunctionData('getReserves'),
512
+ },
513
+ {
514
+ target: pair.exchange!,
515
+ callData: smardexPoolL1.encodeFunctionData('getFictiveReserves'),
516
+ },
517
+ {
518
+ target: pair.exchange!,
519
+ callData: smardexPoolL1.encodeFunctionData('getPriceAverage'),
520
+ },
521
+ ];
522
+ // Fetch fees only on layer 2
523
+ !this.isLayer1() && calldata.push(multiCallFeeData[i]!.callEntry);
524
+ return calldata;
525
+ })
526
+ .flat();
527
+
528
+ const data: { returnData: any[] } =
529
+ await this.dexHelper.multiContract.methods
530
+ .aggregate(calldata)
531
+ .call({}, blockNumber);
532
+
533
+ const returnData = _.chunk(data.returnData, 4);
534
+ return pairs.map((_pair: SmardexPair, i) => ({
535
+ reserves0: coder
536
+ .decode(['uint256', 'uint256'], returnData[i][0])[0]
537
+ .toString(),
538
+ reserves1: coder
539
+ .decode(['uint256', 'uint256'], returnData[i][0])[1]
540
+ .toString(),
541
+ fictiveReserves0: coder
542
+ .decode(['uint256', 'uint256'], returnData[i][1])[0]
543
+ .toString(),
544
+ fictiveReserves1: coder
545
+ .decode(['uint256', 'uint256'], returnData[i][1])[1]
546
+ .toString(),
547
+ priceAverage0: coder
548
+ .decode(['uint256', 'uint256', 'uint256'], returnData[i][2])[0]
549
+ .toString(),
550
+ priceAverage1: coder
551
+ .decode(['uint256', 'uint256', 'uint256'], returnData[i][2])[1]
552
+ .toString(),
553
+ priceAverageLastTimestamp: coder
554
+ .decode(['uint256', 'uint256', 'uint256'], returnData[i][2])[2]
555
+ .toString(),
556
+ feesLP: this.isLayer1()
557
+ ? FEES_LAYER_ONE.feesLP
558
+ : multiCallFeeData[i]!.callDecoder(returnData[i][3]).feesLP,
559
+ feesPool: this.isLayer1()
560
+ ? FEES_LAYER_ONE.feesPool
561
+ : multiCallFeeData[i]!.callDecoder(returnData[i][3]).feesPool,
562
+ }));
563
+ } catch (e) {
564
+ this.logger.error(
565
+ `Error_getManyPoolReserves could not get reserves with error:`,
566
+ e,
567
+ );
568
+ return [];
569
+ }
570
+ }
571
+
572
+ protected fixPath(path: Address[], srcToken: Address, destToken: Address) {
573
+ return path.map((token: string, i: number) => {
574
+ if (
575
+ (i === 0 && srcToken.toLowerCase() === ETHER_ADDRESS.toLowerCase()) ||
576
+ (i === path.length - 1 &&
577
+ destToken.toLowerCase() === ETHER_ADDRESS.toLowerCase())
578
+ )
579
+ return ETHER_ADDRESS;
580
+ return token;
581
+ });
582
+ }
583
+
584
+ // Necessary to get the correct path for the router
585
+ getDirectParam(
586
+ srcToken: Address,
587
+ destToken: Address,
588
+ srcAmount: NumberAsString,
589
+ destAmount: NumberAsString,
590
+ expectedAmount: NumberAsString,
591
+ _data: SmardexData,
592
+ side: SwapSide,
593
+ permit: string,
594
+ uuid: string,
595
+ feePercent: NumberAsString,
596
+ deadline: NumberAsString,
597
+ partner: string,
598
+ beneficiary: string,
599
+ contractMethod?: string,
600
+ ): TxInfo<SmardexParam> {
601
+ if (!contractMethod) throw new Error(`contractMethod need to be passed`);
602
+ if (permit !== '0x') contractMethod += 'WithPermit';
603
+
604
+ const swapParams = ((): SmardexParam => {
605
+ const data = _data as unknown as SmardexData;
606
+ const path = this.fixPath(data.path, srcToken, destToken);
607
+
608
+ switch (contractMethod) {
609
+ case SmardexRouterFunctions.sellExactEth:
610
+ case SmardexRouterFunctions.sellExactToken:
611
+ case SmardexRouterFunctions.swapExactIn:
612
+ return [
613
+ srcAmount,
614
+ destAmount,
615
+ data.path,
616
+ data.receiver,
617
+ data.deadline,
618
+ ];
619
+
620
+ case SmardexRouterFunctions.buyExactEth:
621
+ case SmardexRouterFunctions.buyExactToken:
622
+ case SmardexRouterFunctions.swapExactOut:
623
+ return [
624
+ destAmount,
625
+ srcAmount,
626
+ data.path,
627
+ data.receiver,
628
+ data.deadline,
629
+ ];
630
+
631
+ default:
632
+ throw new Error(`contractMethod=${contractMethod} is not supported`);
633
+ }
634
+ })();
635
+
636
+ const encoder = (...params: SmardexParam) =>
637
+ this.routerInterface.encodeFunctionData(contractMethod!, params);
638
+ return {
639
+ params: swapParams,
640
+ encoder,
641
+ networkFee: '0',
642
+ };
643
+ }
644
+
645
+ async getPairOrderedParams(
646
+ from: Token,
647
+ to: Token,
648
+ blockNumber: number,
649
+ tokenDexTransferFee: number,
650
+ ): Promise<SmardexPoolOrderedParams | null> {
651
+ const pair = await this.findPair(from, to);
652
+ if (!(pair && pair.pool && pair.exchange)) return null;
653
+ const pairState = pair.pool.getState(blockNumber);
654
+ if (!pairState) {
655
+ this.logger.error(
656
+ `Error_orderPairParams expected reserves, got none (maybe the pool doesn't exist) ${
657
+ from.symbol || from.address
658
+ } ${to.symbol || to.address}`,
659
+ );
660
+ return null;
661
+ }
662
+ // const fee = (pairState.feesPool + tokenDexTransferFee).toString();
663
+
664
+ return {
665
+ fromToken: from.address,
666
+ toToken: to.address,
667
+ token0: pair.token0.address,
668
+ token1: pair.token1.address,
669
+ reserves0: BigInt(pairState.reserves0),
670
+ reserves1: BigInt(pairState.reserves1),
671
+ fictiveReserves0: BigInt(pairState.fictiveReserves0),
672
+ fictiveReserves1: BigInt(pairState.fictiveReserves1),
673
+ priceAverage0: BigInt(pairState.priceAverage0),
674
+ priceAverage1: BigInt(pairState.priceAverage1),
675
+ priceAverageLastTimestamp: pairState.priceAverageLastTimestamp,
676
+ exchange: pair.exchange,
677
+ feesLP: BigInt(pairState.feesLP),
678
+ feesPool: BigInt(pairState.feesPool),
679
+ };
680
+ }
681
+
682
+ getWETHAddress(srcToken: Address, destToken: Address, weth?: Address) {
683
+ if (!isETHAddress(srcToken) && !isETHAddress(destToken))
684
+ return NULL_ADDRESS;
685
+ return weth || this.dexHelper.config.data.wrappedNativeTokenAddress;
686
+ }
687
+
688
+ getAdapterParam(
689
+ srcToken: Address,
690
+ destToken: Address,
691
+ srcAmount: NumberAsString,
692
+ toAmount: NumberAsString,
693
+ data: SmardexData,
694
+ side: SwapSide,
695
+ ): AdapterExchangeParam {
696
+ const payload = this.abiCoder.encodeParameter(
697
+ {
698
+ ParentStruct: {
699
+ path: 'address[]',
700
+ receiver: 'address',
701
+ },
702
+ },
703
+ {
704
+ path: data.path,
705
+ receiver: data.receiver,
706
+ },
707
+ );
708
+ return {
709
+ targetExchange: data.router,
710
+ payload,
711
+ networkFee: '0',
712
+ };
713
+ }
714
+
715
+ async getSimpleParam(
716
+ src: Address,
717
+ dest: Address,
718
+ srcAmount: NumberAsString,
719
+ destAmount: NumberAsString,
720
+ data: SmardexData,
721
+ side: SwapSide,
722
+ ): Promise<SimpleExchangeParam> {
723
+ let routerMethod: any;
724
+ let routerArgs: any;
725
+ if (side === SwapSide.SELL) {
726
+ routerMethod = isETHAddress(src)
727
+ ? SmardexRouterFunctions.sellExactEth
728
+ : SmardexRouterFunctions.swapExactIn;
729
+ routerMethod = isETHAddress(dest)
730
+ ? SmardexRouterFunctions.sellExactToken
731
+ : routerMethod;
732
+
733
+ routerArgs = isETHAddress(src)
734
+ ? [destAmount, data.path, data.receiver, data.deadline]
735
+ : [srcAmount, destAmount, data.path, data.receiver, data.deadline];
736
+ } else {
737
+ routerMethod = isETHAddress(src)
738
+ ? SmardexRouterFunctions.buyExactToken
739
+ : SmardexRouterFunctions.swapExactOut;
740
+ routerMethod = isETHAddress(dest)
741
+ ? SmardexRouterFunctions.buyExactEth
742
+ : routerMethod;
743
+
744
+ routerArgs = isETHAddress(src)
745
+ ? [destAmount, data.path, data.receiver, data.deadline]
746
+ : [destAmount, srcAmount, data.path, data.receiver, data.deadline];
747
+ }
748
+
749
+ const swapData = this.exchangeRouterInterface.encodeFunctionData(
750
+ routerMethod,
751
+ routerArgs,
752
+ );
753
+ return this.buildSimpleParamWithoutWETHConversion(
754
+ src,
755
+ srcAmount,
756
+ dest,
757
+ destAmount,
758
+ swapData,
759
+ data.router,
760
+ );
761
+ }
762
+
763
+ isLayer1(): boolean {
764
+ return this.network === Network.MAINNET;
765
+ }
766
+
767
+ async getTopPoolsForToken(
768
+ tokenAddress: Address,
769
+ limit: number,
770
+ ): Promise<PoolLiquidity[]> {
771
+ if (!this.subgraphURL) return [];
772
+ const query = `
773
+ query ($token: Bytes!, $limit: Int) {
774
+ pools0: pairs(first: $limit, orderBy: reserveUSD, orderDirection: desc, where: {token0: $token, reserve0_gt: 1, reserve1_gt: 1}) {
775
+ id
776
+ token0 {
777
+ id
778
+ decimals
779
+ }
780
+ token1 {
781
+ id
782
+ decimals
783
+ }
784
+ reserveUSD
785
+ }
786
+ pools1: pairs(first: $limit, orderBy: reserveUSD, orderDirection: desc, where: {token1: $token, reserve0_gt: 1, reserve1_gt: 1}) {
787
+ id
788
+ token0 {
789
+ id
790
+ decimals
791
+ }
792
+ token1 {
793
+ id
794
+ decimals
795
+ }
796
+ reserveUSD
797
+ }
798
+ }`;
799
+
800
+ const { data } = await this.dexHelper.httpRequest.post(
801
+ this.subgraphURL,
802
+ {
803
+ query,
804
+ variables: { token: tokenAddress.toLowerCase(), limit },
805
+ },
806
+ SUBGRAPH_TIMEOUT,
807
+ { 'x-api-key': this.dexHelper.config.data.smardexSubgraphAuthToken! },
808
+ );
809
+
810
+ if (!(data && data.pools0 && data.pools1))
811
+ throw new Error("Couldn't fetch the pools from the subgraph");
812
+ const pools0 = _.map(data.pools0, pool => ({
813
+ exchange: this.dexKey,
814
+ address: pool.id.toLowerCase(),
815
+ connectorTokens: [
816
+ {
817
+ address: pool.token1.id.toLowerCase(),
818
+ decimals: parseInt(pool.token1.decimals),
819
+ },
820
+ ],
821
+ liquidityUSD: parseFloat(pool.reserveUSD),
822
+ }));
823
+
824
+ const pools1 = _.map(data.pools1, pool => ({
825
+ exchange: this.dexKey,
826
+ address: pool.id.toLowerCase(),
827
+ connectorTokens: [
828
+ {
829
+ address: pool.token0.id.toLowerCase(),
830
+ decimals: parseInt(pool.token0.decimals),
831
+ },
832
+ ],
833
+ liquidityUSD: parseFloat(pool.reserveUSD),
834
+ }));
835
+
836
+ return _.slice(
837
+ _.sortBy(_.concat(pools0, pools1), [pool => -1 * pool.liquidityUSD]),
838
+ 0,
839
+ limit,
840
+ );
841
+ }
842
+ }