@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,1288 +1,1288 @@
1
- import _ from 'lodash';
2
- import { AbiItem } from 'web3-utils';
3
- import { NumberAsString, OptimalSwapExchange } from '@paraswap/core';
4
- import { assert, AsyncOrSync } from 'ts-essentials';
5
- import { Interface, JsonFragment } from '@ethersproject/abi';
6
- import {
7
- Token,
8
- Address,
9
- ExchangePrices,
10
- PoolPrices,
11
- AdapterExchangeParam,
12
- SimpleExchangeParam,
13
- PoolLiquidity,
14
- Logger,
15
- TransferFeeParams,
16
- TxInfo,
17
- PreprocessTransactionOptions,
18
- ExchangeTxInfo,
19
- DexExchangeParam,
20
- } from '../../types';
21
- import {
22
- SwapSide,
23
- Network,
24
- SRC_TOKEN_PARASWAP_TRANSFERS,
25
- NULL_ADDRESS,
26
- } from '../../constants';
27
- import * as CALLDATA_GAS_COST from '../../calldata-gas-cost';
28
- import {
29
- getBigIntPow,
30
- getDexKeysWithNetwork,
31
- isETHAddress,
32
- isSrcTokenTransferFeeToBeExchanged,
33
- uuidToBytes16,
34
- } from '../../utils';
35
- import { IDex } from '../idex';
36
- import { IDexHelper } from '../../dex-helper/idex-helper';
37
- import {
38
- CurveSwapFunctions,
39
- CurveV1FactoryData,
40
- CurveV1FactoryIfaces,
41
- CurveV1SwapType,
42
- CustomImplementationNames,
43
- ImplementationNames,
44
- PoolConstants,
45
- DirectCurveV1Param,
46
- CurveV1FactoryDirectSwap,
47
- DirectCurveV1FactoryParamV6,
48
- } from './types';
49
- import {
50
- getLocalDeadlineAsFriendlyPlaceholder,
51
- SimpleExchange,
52
- } from '../simple-exchange';
53
- import { CurveV1FactoryConfig, Adapters } from './config';
54
- import {
55
- DIRECT_METHOD_NAME,
56
- FACTORY_MAX_PLAIN_COINS,
57
- FACTORY_MAX_PLAIN_IMPLEMENTATIONS_FOR_COIN,
58
- MIN_AMOUNT_TO_RECEIVE,
59
- POOL_EXCHANGE_GAS_COST,
60
- } from './constants';
61
- import { CurveV1FactoryPoolManager } from './curve-v1-pool-manager';
62
- import CurveABI from '../../abi/Curve.json';
63
- import DirectSwapABI from '../../abi/DirectSwap.json';
64
- import FactoryCurveV1ABI from '../../abi/curve-v1-factory/FactoryCurveV1.json';
65
- import ThreePoolABI from '../../abi/curve-v1-factory/ThreePool.json';
66
- import ERC20ABI from '../../abi/erc20.json';
67
- import {
68
- addressDecode,
69
- generalDecoder,
70
- uint256DecodeToNumber,
71
- uint8ToNumber,
72
- } from '../../lib/decoders';
73
- import { MultiCallParams, MultiResult } from '../../lib/multi-wrapper';
74
- import { BigNumber, BytesLike } from 'ethers';
75
- import { FactoryStateHandler } from './state-polling-pools/factory-pool-polling';
76
- import { PoolPollingBase } from './state-polling-pools/pool-polling-base';
77
- import { CustomBasePoolForFactory } from './state-polling-pools/custom-pool-polling';
78
- import ImplementationConstants from './price-handlers/functions/constants';
79
- import { applyTransferFee } from '../../lib/token-transfer-fee';
80
- import { PriceHandler } from './price-handlers/price-handler';
81
- import { hexConcat, hexZeroPad, hexlify } from 'ethers/lib/utils';
82
- import { packCurveData } from '../../lib/curve/encoder';
83
- import { encodeCurveAssets } from '../curve-v1/packer';
84
-
85
- import { DIRECT_METHOD_NAME_V6 } from './constants';
86
-
87
- const DefaultCoinsABI: AbiItem = {
88
- type: 'function',
89
- name: 'coins',
90
- inputs: [
91
- {
92
- name: '',
93
- type: 'uint256',
94
- },
95
- ],
96
- outputs: [
97
- {
98
- name: '',
99
- type: 'address',
100
- },
101
- ],
102
- };
103
-
104
- export class CurveV1Factory
105
- extends SimpleExchange
106
- implements
107
- IDex<CurveV1FactoryData, DirectCurveV1Param | CurveV1FactoryDirectSwap>
108
- {
109
- readonly hasConstantPriceLargeAmounts = false;
110
- readonly needWrapNative = false;
111
- readonly isFeeOnTransferSupported = true;
112
- readonly isStatePollingDex = true;
113
-
114
- private factoryAddresses: string[];
115
-
116
- readonly poolManager: CurveV1FactoryPoolManager;
117
- readonly ifaces: CurveV1FactoryIfaces;
118
-
119
- private areFactoryPoolsFetched: boolean = false;
120
- private areCustomPoolsFetched: boolean = false;
121
-
122
- readonly SRC_TOKEN_DEX_TRANSFERS = 1;
123
- readonly DEST_TOKEN_DEX_TRANSFERS = 1;
124
-
125
- readonly directSwapIface = new Interface(DirectSwapABI);
126
-
127
- public static dexKeysWithNetwork: { key: string; networks: Network[] }[] =
128
- getDexKeysWithNetwork(CurveV1FactoryConfig);
129
-
130
- logger: Logger;
131
-
132
- constructor(
133
- readonly network: Network,
134
- readonly dexKey: string,
135
- readonly dexHelper: IDexHelper,
136
- protected adapters = Adapters[network] || {},
137
- protected config = CurveV1FactoryConfig[dexKey][network],
138
- // This type is used to support different encoding for uint128 and uint256 args
139
- private coinsTypeTemplate: AbiItem = DefaultCoinsABI,
140
- ) {
141
- super(dexHelper, dexKey);
142
- this.logger = dexHelper.getLogger(`${this.dexKey}-${this.network}`);
143
- this.factoryAddresses =
144
- this.config.factories?.map(e => e.address.toLowerCase()) || [];
145
- this.ifaces = {
146
- exchangeRouter: new Interface(CurveABI),
147
- factory: new Interface(FactoryCurveV1ABI as JsonFragment[]),
148
- erc20: new Interface(ERC20ABI as JsonFragment[]),
149
- threePool: new Interface(ThreePoolABI as JsonFragment[]),
150
- };
151
-
152
- // I had to put this initialization into CurveV1 because I have to have access to mapping
153
- // from address to custom pool
154
- const allPriceHandlers = Object.values(
155
- this.config.factoryPoolImplementations,
156
- ).reduce<Record<string, PriceHandler>>((acc, curr) => {
157
- let baseImplementationName: ImplementationNames | undefined;
158
- if (curr.basePoolAddress) {
159
- const customBasePool = this.config.customPools[curr.basePoolAddress];
160
- if (customBasePool === undefined) {
161
- this.logger.error(
162
- `${this.dexKey} on ${this.dexHelper.config.data.network}: can not get customBasePool from basePoolAddress`,
163
- );
164
- return acc;
165
- } else {
166
- baseImplementationName = customBasePool.name;
167
- }
168
- }
169
- acc[curr.address] = new PriceHandler(
170
- this.logger,
171
- curr.name,
172
- baseImplementationName,
173
- );
174
- return acc;
175
- }, {});
176
-
177
- Object.values(this.config.customPools).reduce((acc, curr) => {
178
- if (curr.useForPricing) {
179
- acc[curr.address] = new PriceHandler(this.logger, curr.name);
180
- }
181
-
182
- return acc;
183
- }, allPriceHandlers);
184
-
185
- this.poolManager = new CurveV1FactoryPoolManager(
186
- this.dexKey,
187
- dexHelper.getLogger(`${this.dexKey}-state-manager`),
188
- dexHelper,
189
- allPriceHandlers,
190
- this.config.stateUpdatePeriodMs,
191
- );
192
- }
193
-
194
- async initializePricing(blockNumber: number) {
195
- // This is only to start timer, each pool is initialized with updated state
196
- this.poolManager.initializePollingPools();
197
- await this.fetchFactoryPools(blockNumber);
198
- await this.poolManager.fetchLiquiditiesFromApi();
199
- await this.poolManager.updatePollingPoolsInBatch();
200
- this.logger.info(`${this.dexKey}: successfully initialized`);
201
- }
202
-
203
- async initializeCustomPollingPools(
204
- factoryAddresses: string[],
205
- blockNumber?: number,
206
- // We don't want to initialize state for PoolTracker. It doesn't make any sense
207
- initializeInitialState: boolean = true,
208
- ) {
209
- if (this.areCustomPoolsFetched) {
210
- return;
211
- }
212
-
213
- await Promise.all(
214
- factoryAddresses.map(async factoryAddress => {
215
- try {
216
- await Promise.all(
217
- Object.values(this.config.customPools).map(async customPool => {
218
- const poolIdentifier = this.getPoolIdentifier(
219
- customPool.address,
220
- false,
221
- );
222
-
223
- const poolContextConstants =
224
- ImplementationConstants[customPool.name];
225
- const {
226
- N_COINS: nCoins,
227
- USE_LENDING: useLending,
228
- isLending,
229
- } = poolContextConstants;
230
-
231
- const coinsAndImplementations =
232
- await this.dexHelper.multiWrapper.aggregate(
233
- _.range(0, nCoins)
234
- .map(i => ({
235
- target: customPool.address,
236
- callData: this.abiCoder.encodeFunctionCall(
237
- this._getCoinsABI(customPool.coinsInputType),
238
- [i.toString()],
239
- ),
240
- decodeFunction: addressDecode,
241
- }))
242
- .concat([
243
- {
244
- target: factoryAddress,
245
- callData: this.ifaces.factory.encodeFunctionData(
246
- 'get_implementation_address',
247
- [customPool.address],
248
- ),
249
- decodeFunction: addressDecode,
250
- },
251
- ]),
252
- );
253
- const COINS = coinsAndImplementations.slice(0, -1);
254
- const implementationAddress =
255
- coinsAndImplementations.slice(-1)[0];
256
-
257
- const coins_decimals = (
258
- await this.dexHelper.multiWrapper.tryAggregate(
259
- true,
260
- COINS.map(c => ({
261
- target: c,
262
- callData: this.ifaces.erc20.encodeFunctionData(
263
- 'decimals',
264
- [],
265
- ),
266
- decodeFunction: uint8ToNumber,
267
- })),
268
- )
269
- ).map(r => r.returnData);
270
-
271
- const poolConstants: PoolConstants = {
272
- COINS,
273
- coins_decimals,
274
- rate_multipliers: this._calcRateMultipliers(coins_decimals),
275
- lpTokenAddress: customPool.lpTokenAddress,
276
- };
277
-
278
- let newPool: PoolPollingBase;
279
- if (
280
- Object.values<ImplementationNames>(
281
- CustomImplementationNames,
282
- ).includes(customPool.name)
283
- ) {
284
- // We don't want custom pools to be used for pricing, unless explicitly specified
285
- newPool = new CustomBasePoolForFactory(
286
- this.logger,
287
- this.dexKey,
288
- this.dexHelper.config.data.network,
289
- this.cacheStateKey,
290
- customPool.name,
291
- implementationAddress,
292
- customPool.address,
293
- this.config.stateUpdatePeriodMs,
294
- poolIdentifier,
295
- poolConstants,
296
- poolContextConstants,
297
- customPool.liquidityApiSlug,
298
- customPool.lpTokenAddress,
299
- isLending,
300
- customPool.balancesInputType,
301
- useLending,
302
- customPool.useForPricing,
303
- );
304
- } else {
305
- // Use for pricing pools from factory
306
- newPool = new CustomBasePoolForFactory(
307
- this.logger,
308
- this.dexKey,
309
- this.dexHelper.config.data.network,
310
- this.cacheStateKey,
311
- customPool.name,
312
- implementationAddress,
313
- customPool.address,
314
- this.config.stateUpdatePeriodMs,
315
- poolIdentifier,
316
- poolConstants,
317
- poolContextConstants,
318
- customPool.liquidityApiSlug,
319
- customPool.lpTokenAddress,
320
- isLending,
321
- customPool.balancesInputType,
322
- useLending,
323
- true,
324
- );
325
- }
326
-
327
- this.poolManager.initializeNewPoolForState(
328
- poolIdentifier,
329
- newPool,
330
- );
331
-
332
- if (initializeInitialState) {
333
- await this.poolManager.initializeIndividualPollingPoolState(
334
- poolIdentifier,
335
- CustomBasePoolForFactory.IS_SRC_FEE_ON_TRANSFER_SUPPORTED,
336
- blockNumber,
337
- );
338
- }
339
- }),
340
- );
341
- } catch (e) {
342
- this.logger.error(
343
- `Error initializing custom polling pools for factory ${factoryAddress}: `,
344
- e,
345
- );
346
- throw e;
347
- }
348
- }),
349
- );
350
-
351
- this.areCustomPoolsFetched = true;
352
- }
353
-
354
- async fetchFactoryPools(
355
- blockNumber?: number,
356
- // Variable initializeInitialState is only for poolTracker. We don't want to keep state updated with scheduler
357
- // We just want to initialize factory pools and send request to CurveAPI
358
- // Other values are not used
359
- initializeInitialState: boolean = false,
360
- ) {
361
- if (this.areFactoryPoolsFetched) {
362
- return;
363
- }
364
-
365
- if (!this.factoryAddresses || this.factoryAddresses.length == 0) {
366
- this.logger.warn(`No factory address specified in configs`);
367
- return;
368
- }
369
-
370
- // There is no scenario when we need to call initialize custom pools without factory pools
371
- // So I put it here to not forget call, because custom pools must be initialised before factory pools
372
- // This function may be called multiple times, but will execute only once
373
- await this.initializeCustomPollingPools(
374
- this.factoryAddresses,
375
- blockNumber,
376
- initializeInitialState,
377
- );
378
-
379
- await Promise.all(
380
- this.factoryAddresses.map(async factoryAddress => {
381
- try {
382
- const poolCountResult =
383
- await this.dexHelper.multiWrapper!.tryAggregate(true, [
384
- {
385
- target: factoryAddress,
386
- callData: this.ifaces.factory.encodeFunctionData('pool_count'),
387
- decodeFunction: uint256DecodeToNumber,
388
- },
389
- // This is used later to request all available implementations. In particular meta implementations
390
- {
391
- target: factoryAddress,
392
- callData:
393
- this.ifaces.factory.encodeFunctionData('base_pool_count'),
394
- decodeFunction: uint256DecodeToNumber,
395
- },
396
- ]);
397
-
398
- const poolCount = poolCountResult[0].returnData;
399
- const basePoolCount = poolCountResult[1].returnData;
400
-
401
- const calldataGetPoolAddresses = _.range(0, poolCount).map(i => ({
402
- target: factoryAddress,
403
- callData: this.ifaces.factory.encodeFunctionData('pool_list', [i]),
404
- decodeFunction: addressDecode,
405
- }));
406
-
407
- const calldataGetBasePoolAddresses = _.range(0, basePoolCount).map(
408
- i => ({
409
- target: factoryAddress,
410
- callData: this.ifaces.factory.encodeFunctionData(
411
- 'base_pool_list',
412
- [i],
413
- ),
414
- decodeFunction: addressDecode,
415
- }),
416
- );
417
-
418
- const allPoolAddresses = (
419
- await this.dexHelper.multiWrapper.tryAggregate(
420
- true,
421
- calldataGetPoolAddresses.concat(calldataGetBasePoolAddresses),
422
- )
423
- ).map(e => e.returnData);
424
-
425
- const poolAddresses = allPoolAddresses.slice(0, poolCount);
426
- const basePoolAddresses = allPoolAddresses.slice(poolCount);
427
-
428
- const customPoolAddresses = Object.values(
429
- this.config.customPools,
430
- ).map(customPool => customPool.address);
431
- basePoolAddresses.forEach(basePool => {
432
- if (
433
- !customPoolAddresses.includes(basePool) &&
434
- !this.config.disabledPools.has(basePool)
435
- ) {
436
- this._reportForUnspecifiedCustomPool(basePool);
437
- }
438
- });
439
-
440
- const factoryConfig = this.config.factories?.find(
441
- ({ address }) =>
442
- factoryAddress.toLowerCase() === address.toLowerCase(),
443
- );
444
-
445
- let callDataFromFactoryPools: MultiCallParams<
446
- string[] | number[] | string
447
- >[] = poolAddresses
448
- .map(p => [
449
- {
450
- target: factoryAddress,
451
- callData: this.ifaces.factory.encodeFunctionData(
452
- 'get_implementation_address',
453
- [p],
454
- ),
455
- decodeFunction: addressDecode,
456
- },
457
- {
458
- target: factoryAddress,
459
- callData: this.ifaces.factory.encodeFunctionData('get_coins', [
460
- p,
461
- ]),
462
- decodeFunction: (
463
- result: MultiResult<BytesLike> | BytesLike,
464
- ): string[] =>
465
- generalDecoder<string[]>(
466
- result,
467
- [
468
- `address[${
469
- factoryConfig?.maxPlainCoins?.toString() || ''
470
- }]`,
471
- ],
472
- new Array(4).fill(NULL_ADDRESS),
473
- parsed => parsed[0].map((p: string) => p.toLowerCase()),
474
- ),
475
- },
476
- {
477
- target: factoryAddress,
478
- callData: this.ifaces.factory.encodeFunctionData(
479
- 'get_decimals',
480
- [p],
481
- ),
482
- decodeFunction: (
483
- result: MultiResult<BytesLike> | BytesLike,
484
- ): number[] =>
485
- generalDecoder<number[]>(
486
- result,
487
- [
488
- `uint256[${
489
- factoryConfig?.maxPlainCoins?.toString() || ''
490
- }]`,
491
- ],
492
- [0, 0, 0, 0],
493
- parsed =>
494
- parsed[0].map((p: BigNumber) => Number(p.toString())),
495
- ),
496
- },
497
- ])
498
- .flat();
499
-
500
- // This is divider between pools related results and implementations
501
- const factoryResultsDivider = callDataFromFactoryPools.length;
502
-
503
- const metaPoolImplementations = factoryConfig?.isStableNg
504
- ? []
505
- : basePoolAddresses.map(basePoolAddress => ({
506
- target: factoryAddress,
507
- callData: this.ifaces.factory.encodeFunctionData(
508
- 'metapool_implementations',
509
- [basePoolAddress],
510
- ),
511
- decodeFunction: (
512
- result: MultiResult<BytesLike> | BytesLike,
513
- ): string[] =>
514
- generalDecoder<string[]>(
515
- result,
516
- ['address[10]'],
517
- new Array(10).fill(NULL_ADDRESS),
518
- parsed => parsed[0].map((p: string) => p.toLowerCase()),
519
- ),
520
- }));
521
-
522
- const plainImplementations = factoryConfig?.isStableNg
523
- ? []
524
- : _.flattenDeep(
525
- _.range(2, FACTORY_MAX_PLAIN_COINS + 1).map(coinNumber =>
526
- _.range(FACTORY_MAX_PLAIN_IMPLEMENTATIONS_FOR_COIN).map(
527
- implInd => ({
528
- target: factoryAddress,
529
- callData: this.ifaces.factory.encodeFunctionData(
530
- 'plain_implementations',
531
- [coinNumber, implInd],
532
- ),
533
- decodeFunction: addressDecode,
534
- }),
535
- ),
536
- ),
537
- );
538
-
539
- // Implementations must be requested from factory, but it accepts as arg basePool address
540
- // for metaPools
541
- callDataFromFactoryPools = callDataFromFactoryPools.concat(
542
- ...metaPoolImplementations,
543
- // To receive plain pool implementation address, you have to call plain_implementations
544
- // with two variables: N_COINS and implementations_index
545
- // N_COINS is between 2-4. Currently more than 4 coins is not supported
546
- // as for implementation index, there are only 0-9 indexes
547
- ...plainImplementations,
548
- );
549
-
550
- const allResultsFromFactory = (
551
- await this.dexHelper.multiWrapper.tryAggregate<
552
- string[] | number[] | string
553
- >(true, callDataFromFactoryPools)
554
- ).map(r => r.returnData);
555
-
556
- const resultsFromFactory = allResultsFromFactory.slice(
557
- 0,
558
- factoryResultsDivider,
559
- );
560
-
561
- const allAvailableImplementations = _.flattenDeep(
562
- allResultsFromFactory.slice(factoryResultsDivider) as string[],
563
- ).filter(
564
- implementation =>
565
- implementation !== NULL_ADDRESS &&
566
- !this.config.disabledImplementations.has(implementation),
567
- );
568
-
569
- allAvailableImplementations.forEach(implementation => {
570
- const currentImplementation =
571
- this.config.factoryPoolImplementations[implementation];
572
- if (currentImplementation === undefined) {
573
- this._reportForUnspecifiedImplementation(implementation);
574
- }
575
- });
576
-
577
- const stateInitializePromises: Promise<void>[] = [];
578
- _.chunk(resultsFromFactory, 3).forEach((result, i) => {
579
- if (this.config.disabledPools.has(poolAddresses[i])) {
580
- this.logger.trace(`Filtering disabled pool ${poolAddresses[i]}`);
581
- return;
582
- }
583
-
584
- let [implementationAddress, coins, coins_decimals] = result as [
585
- string,
586
- string[],
587
- number[],
588
- ];
589
-
590
- implementationAddress = implementationAddress.toLowerCase();
591
- coins = coins
592
- .map(c => c.toLowerCase())
593
- .filter(c => c !== NULL_ADDRESS);
594
- coins_decimals = coins_decimals.filter(cd => cd !== 0);
595
-
596
- const factoryImplementationFromConfig =
597
- this.config.factoryPoolImplementations[implementationAddress];
598
-
599
- if (
600
- factoryImplementationFromConfig === undefined &&
601
- !this.config.disabledImplementations.has(implementationAddress)
602
- ) {
603
- this._reportForUnspecifiedImplementation(
604
- implementationAddress,
605
- poolAddresses[i],
606
- );
607
- return;
608
- }
609
-
610
- const factoryImplementationConstants =
611
- ImplementationConstants[factoryImplementationFromConfig.name];
612
-
613
- let isMeta: boolean = false;
614
- let basePoolStateFetcher: PoolPollingBase | undefined;
615
- if (factoryImplementationFromConfig.basePoolAddress !== undefined) {
616
- isMeta = true;
617
- const basePoolIdentifier = this.getPoolIdentifier(
618
- factoryImplementationFromConfig.basePoolAddress,
619
- false,
620
- );
621
- const basePool = this.poolManager.getPool(
622
- basePoolIdentifier,
623
- false,
624
- );
625
- if (basePool === null) {
626
- this.logger.error(
627
- `${this.dexKey}_${this.dexHelper.config.data.network}: custom base pool ${basePoolIdentifier} was not initialized properly. ` +
628
- `You must call initializeCustomPollingPools before fetching factory`,
629
- );
630
- return;
631
- }
632
- basePoolStateFetcher = basePool;
633
- }
634
-
635
- const poolConstants: PoolConstants = {
636
- COINS: coins,
637
- coins_decimals,
638
- rate_multipliers: this._calcRateMultipliers(coins_decimals),
639
- };
640
-
641
- const poolIdentifier = this.getPoolIdentifier(
642
- poolAddresses[i],
643
- isMeta,
644
- );
645
-
646
- const newPool = new FactoryStateHandler(
647
- this.logger,
648
- this.dexKey,
649
- this.dexHelper.config.data.network,
650
- this.cacheStateKey,
651
- factoryImplementationFromConfig.name,
652
- implementationAddress.toLowerCase(),
653
- poolAddresses[i],
654
- this.config,
655
- factoryAddress,
656
- poolIdentifier,
657
- poolConstants,
658
- factoryImplementationConstants,
659
- factoryImplementationConstants.isFeeOnTransferSupported,
660
- factoryImplementationFromConfig.liquidityApiSlug ?? '/factory',
661
- basePoolStateFetcher,
662
- factoryImplementationFromConfig.customGasCost,
663
- factoryImplementationFromConfig.isStoreRateSupported,
664
- );
665
-
666
- this.poolManager.initializeNewPool(poolIdentifier, newPool);
667
-
668
- if (initializeInitialState) {
669
- stateInitializePromises.push(
670
- this.poolManager.initializeIndividualPollingPoolState(
671
- poolIdentifier,
672
- factoryImplementationConstants.isFeeOnTransferSupported,
673
- ),
674
- );
675
- }
676
- });
677
-
678
- await Promise.all(stateInitializePromises);
679
- } catch (e) {
680
- this.logger.error(
681
- `Error fetching factory pools for ${factoryAddress}: `,
682
- e,
683
- );
684
- throw e;
685
- }
686
- }),
687
- );
688
-
689
- this.areFactoryPoolsFetched = true;
690
- }
691
-
692
- getAdapters(side: SwapSide): { name: string; index: number }[] | null {
693
- return this.adapters[side] ? this.adapters[side] : null;
694
- }
695
-
696
- getPoolIdentifier(poolAddress: string, isMeta: boolean): string {
697
- return `${this.dexKey}_${poolAddress}_${isMeta}`;
698
- }
699
-
700
- async getPoolIdentifiers(
701
- srcToken: Token,
702
- destToken: Token,
703
- side: SwapSide,
704
- blockNumber: number,
705
- ): Promise<string[]> {
706
- if (side === SwapSide.BUY) {
707
- return [];
708
- }
709
-
710
- const srcTokenAddress = srcToken.address.toLowerCase();
711
- const destTokenAddress = destToken.address.toLowerCase();
712
-
713
- if (srcTokenAddress === destTokenAddress) {
714
- return [];
715
- }
716
-
717
- const pools = this.poolManager.getPoolsForPair(
718
- srcTokenAddress,
719
- destTokenAddress,
720
- );
721
-
722
- return pools.map(pool =>
723
- this.getPoolIdentifier(pool.address, pool.isMetaPool),
724
- );
725
- }
726
-
727
- async getPricesVolume(
728
- srcToken: Token,
729
- destToken: Token,
730
- amounts: bigint[],
731
- side: SwapSide,
732
- blockNumber: number,
733
- limitPools?: string[],
734
- transferFees: TransferFeeParams = {
735
- srcFee: 0,
736
- destFee: 0,
737
- srcDexFee: 0,
738
- destDexFee: 0,
739
- },
740
- ): Promise<null | ExchangePrices<CurveV1FactoryData>> {
741
- try {
742
- if (side === SwapSide.BUY) {
743
- return null;
744
- }
745
-
746
- const _isSrcTokenTransferFeeToBeExchanged =
747
- isSrcTokenTransferFeeToBeExchanged(transferFees);
748
-
749
- const srcTokenAddress = srcToken.address.toLowerCase();
750
- const destTokenAddress = destToken.address.toLowerCase();
751
-
752
- if (srcTokenAddress === destTokenAddress) {
753
- return null;
754
- }
755
-
756
- let pools: PoolPollingBase[] = [];
757
- if (limitPools !== undefined) {
758
- pools = limitPools
759
- .map(poolIdentifier =>
760
- this.poolManager.getPool(
761
- poolIdentifier,
762
- _isSrcTokenTransferFeeToBeExchanged,
763
- ),
764
- )
765
- .filter(
766
- (pool): pool is PoolPollingBase =>
767
- pool !== null &&
768
- pool.getPoolData(srcTokenAddress, destTokenAddress) !== null,
769
- );
770
- } else {
771
- pools = this.poolManager.getPoolsForPair(
772
- srcTokenAddress,
773
- destTokenAddress,
774
- _isSrcTokenTransferFeeToBeExchanged,
775
- );
776
- }
777
-
778
- if (pools.length <= 0) {
779
- return null;
780
- }
781
-
782
- if (pools.length > 10) {
783
- // I expect this to not happen
784
- this.logger.warn(
785
- `${this.dexKey}: There are more than 10 pools found for pair: ${srcTokenAddress}-${destTokenAddress}`,
786
- );
787
- }
788
-
789
- const amountsWithUnit = [
790
- getBigIntPow(srcToken.decimals),
791
- ...amounts.slice(1),
792
- ];
793
- const amountsWithUnitAndFee = _isSrcTokenTransferFeeToBeExchanged
794
- ? applyTransferFee(
795
- applyTransferFee(
796
- amountsWithUnit,
797
- side,
798
- transferFees.srcFee,
799
- SRC_TOKEN_PARASWAP_TRANSFERS,
800
- ),
801
- side,
802
- transferFees.srcDexFee,
803
- this.SRC_TOKEN_DEX_TRANSFERS,
804
- )
805
- : amountsWithUnit;
806
-
807
- const results = await Promise.all(
808
- pools.map(
809
- async (pool): Promise<PoolPrices<CurveV1FactoryData> | null> => {
810
- let state = pool.getState();
811
- if (!state) {
812
- await this.poolManager.updateManuallyPollingPools(
813
- pool.baseStatePoolPolling
814
- ? [pool.baseStatePoolPolling, pool]
815
- : [pool],
816
- );
817
- state = pool.getState();
818
- if (!state) {
819
- return null;
820
- }
821
- }
822
-
823
- if (state.balances.every(b => b === 0n)) {
824
- this.logger.trace(
825
- `${this.dexKey} on ${this.dexHelper.config.data.network}: State balances equal to 0 in pool ${pool.address}`,
826
- );
827
- return null;
828
- }
829
-
830
- const poolData = pool.getPoolData(
831
- srcTokenAddress,
832
- destTokenAddress,
833
- );
834
-
835
- if (poolData === null) {
836
- this.logger.error(
837
- `${pool.fullName}: one or both tokens can not be exchanged in pool ${pool.address}: ${srcTokenAddress} -> ${destTokenAddress}`,
838
- );
839
- return null;
840
- }
841
-
842
- let outputs: bigint[] = this.poolManager
843
- .getPriceHandler(pool.implementationAddress)
844
- .getOutputs(
845
- state,
846
- amountsWithUnitAndFee,
847
- poolData.i,
848
- poolData.j,
849
- poolData.underlyingSwap,
850
- );
851
-
852
- outputs = applyTransferFee(
853
- outputs,
854
- side,
855
- transferFees.destDexFee,
856
- this.DEST_TOKEN_DEX_TRANSFERS,
857
- );
858
-
859
- return {
860
- prices: [0n, ...outputs.slice(1)],
861
- unit: outputs[0],
862
- data: poolData,
863
- exchange: this.dexKey,
864
- poolIdentifier: pool.poolIdentifier,
865
- gasCost: POOL_EXCHANGE_GAS_COST,
866
- poolAddresses: [pool.address],
867
- };
868
- },
869
- ),
870
- );
871
-
872
- return results.filter(
873
- (
874
- r: PoolPrices<CurveV1FactoryData> | null,
875
- ): r is PoolPrices<CurveV1FactoryData> => r !== null,
876
- );
877
- } catch (e) {
878
- if (blockNumber === 0)
879
- this.logger.error(
880
- `Error_${this.dexKey}_${this.dexHelper.config.data.network}_getPrices: Aurelius block manager not yet instantiated`,
881
- );
882
- this.logger.error(
883
- `Error_${this.dexKey}_${this.dexHelper.config.data.network}_getPrices`,
884
- e,
885
- );
886
- return null;
887
- }
888
- }
889
-
890
- getCalldataGasCost(
891
- poolPrices: PoolPrices<CurveV1FactoryData>,
892
- ): number | number[] {
893
- return (
894
- CALLDATA_GAS_COST.DEX_OVERHEAD +
895
- CALLDATA_GAS_COST.LENGTH_SMALL +
896
- CALLDATA_GAS_COST.INDEX +
897
- CALLDATA_GAS_COST.INDEX +
898
- CALLDATA_GAS_COST.TIMESTAMP +
899
- CALLDATA_GAS_COST.BOOL
900
- );
901
- }
902
-
903
- getAdapterParam(
904
- srcToken: string,
905
- destToken: string,
906
- srcAmount: string,
907
- destAmount: string,
908
- data: CurveV1FactoryData,
909
- side: SwapSide,
910
- ): AdapterExchangeParam {
911
- if (side === SwapSide.BUY) throw new Error(`Buy not supported`);
912
-
913
- const { i, j, underlyingSwap } = data;
914
- const payload = this.abiCoder.encodeParameter(
915
- {
916
- ParentStruct: {
917
- i: 'int128',
918
- j: 'int128',
919
- deadline: 'uint256',
920
- underlyingSwap: 'bool',
921
- },
922
- },
923
- { i, j, deadline: 0, underlyingSwap },
924
- );
925
-
926
- return {
927
- targetExchange: data.exchange,
928
- payload,
929
- networkFee: '0',
930
- };
931
- }
932
-
933
- getTokenFromAddress(address: Address): Token {
934
- // In this Dex decimals are not used
935
- return { address, decimals: 0 };
936
- }
937
-
938
- async preProcessTransaction(
939
- optimalSwapExchange: OptimalSwapExchange<CurveV1FactoryData>,
940
- srcToken: Token,
941
- _0: Token,
942
- _1: SwapSide,
943
- options: PreprocessTransactionOptions,
944
- ): Promise<[OptimalSwapExchange<CurveV1FactoryData>, ExchangeTxInfo]> {
945
- if (!options.isDirectMethod) {
946
- return [
947
- optimalSwapExchange,
948
- {
949
- deadline: BigInt(getLocalDeadlineAsFriendlyPlaceholder()),
950
- },
951
- ];
952
- }
953
-
954
- assert(
955
- optimalSwapExchange.data !== undefined,
956
- `preProcessTransaction: data field is missing`,
957
- );
958
-
959
- let isApproved: boolean | undefined;
960
-
961
- try {
962
- isApproved = await this.dexHelper.augustusApprovals.hasApproval(
963
- options.executionContractAddress,
964
- this.dexHelper.config.wrapETH(srcToken).address,
965
- optimalSwapExchange.data.exchange,
966
- );
967
- } catch (e) {
968
- this.logger.error(
969
- `preProcessTransaction failed to retrieve allowance info: `,
970
- e,
971
- );
972
- }
973
-
974
- return [
975
- {
976
- ...optimalSwapExchange,
977
- data: {
978
- ...optimalSwapExchange.data,
979
- isApproved,
980
- },
981
- },
982
- {
983
- deadline: BigInt(getLocalDeadlineAsFriendlyPlaceholder()),
984
- },
985
- ];
986
- }
987
-
988
- getDirectParam(
989
- srcToken: Address,
990
- destToken: Address,
991
- srcAmount: NumberAsString,
992
- destAmount: NumberAsString,
993
- expectedAmount: NumberAsString,
994
- data: CurveV1FactoryData,
995
- side: SwapSide,
996
- permit: string,
997
- uuid: string,
998
- feePercent: NumberAsString,
999
- deadline: NumberAsString,
1000
- partner: string,
1001
- beneficiary: string,
1002
- contractMethod?: string,
1003
- ): TxInfo<DirectCurveV1Param> {
1004
- if (contractMethod !== DIRECT_METHOD_NAME) {
1005
- throw new Error(`Invalid contract method ${contractMethod}`);
1006
- }
1007
- assert(side === SwapSide.SELL, 'Buy not supported');
1008
-
1009
- let isApproved: boolean = !!data.isApproved;
1010
- if (data.isApproved === undefined) {
1011
- this.logger.warn(`isApproved is undefined, defaulting to false`);
1012
- }
1013
-
1014
- const swapParams: DirectCurveV1Param = [
1015
- srcToken,
1016
- destToken,
1017
- data.exchange,
1018
- srcAmount,
1019
- destAmount,
1020
- expectedAmount,
1021
- feePercent,
1022
- data.i.toString(),
1023
- data.j.toString(),
1024
- partner,
1025
- isApproved,
1026
- data.underlyingSwap
1027
- ? CurveV1SwapType.EXCHANGE_UNDERLYING
1028
- : CurveV1SwapType.EXCHANGE,
1029
- beneficiary,
1030
- // For CurveV1 we work as it is, without wrapping and unwrapping
1031
- false,
1032
- permit,
1033
- uuidToBytes16(uuid),
1034
- ];
1035
-
1036
- const encoder = (...params: DirectCurveV1Param) => {
1037
- return this.directSwapIface.encodeFunctionData(DIRECT_METHOD_NAME, [
1038
- params,
1039
- ]);
1040
- };
1041
-
1042
- return {
1043
- params: swapParams,
1044
- encoder,
1045
- networkFee: '0',
1046
- };
1047
- }
1048
-
1049
- getDirectParamV6(
1050
- srcToken: Address,
1051
- destToken: Address,
1052
- fromAmount: NumberAsString,
1053
- toAmount: NumberAsString,
1054
- quotedAmount: NumberAsString,
1055
- data: CurveV1FactoryData,
1056
- side: SwapSide,
1057
- permit: string,
1058
- uuid: string,
1059
- partnerAndFee: string,
1060
- beneficiary: string,
1061
- blockNumber: number,
1062
- contractMethod?: string,
1063
- ) {
1064
- if (contractMethod !== DIRECT_METHOD_NAME_V6) {
1065
- throw new Error(`Invalid contract method ${contractMethod}`);
1066
- }
1067
- assert(side === SwapSide.SELL, 'Buy not supported');
1068
-
1069
- const metadata = hexConcat([
1070
- hexZeroPad(uuidToBytes16(uuid), 16),
1071
- hexZeroPad(hexlify(blockNumber), 16),
1072
- ]);
1073
-
1074
- const swapParams: DirectCurveV1FactoryParamV6 = [
1075
- packCurveData(
1076
- data.exchange,
1077
- !data.isApproved, // approve flag, if not approved then set to true
1078
- isETHAddress(destToken) ? 0 : isETHAddress(srcToken) ? 3 : 0,
1079
- data.underlyingSwap
1080
- ? CurveV1SwapType.EXCHANGE_UNDERLYING
1081
- : CurveV1SwapType.EXCHANGE,
1082
- ).toString(),
1083
- encodeCurveAssets(data.i, data.j).toString(),
1084
- srcToken,
1085
- destToken,
1086
- fromAmount,
1087
- toAmount,
1088
- quotedAmount,
1089
- metadata,
1090
- beneficiary,
1091
- ];
1092
-
1093
- const encodeParams: CurveV1FactoryDirectSwap = [
1094
- swapParams,
1095
- partnerAndFee,
1096
- permit,
1097
- ];
1098
-
1099
- const encoder = (...params: CurveV1FactoryDirectSwap) => {
1100
- return this.augustusV6Interface.encodeFunctionData(
1101
- DIRECT_METHOD_NAME_V6,
1102
- [...params],
1103
- );
1104
- };
1105
-
1106
- return {
1107
- encoder,
1108
- params: encodeParams,
1109
- networkFee: '0',
1110
- };
1111
- }
1112
-
1113
- static getDirectFunctionName(): string[] {
1114
- return [DIRECT_METHOD_NAME];
1115
- }
1116
-
1117
- async getSimpleParam(
1118
- srcToken: string,
1119
- destToken: string,
1120
- srcAmount: string,
1121
- destAmount: string,
1122
- data: CurveV1FactoryData,
1123
- side: SwapSide,
1124
- ): Promise<SimpleExchangeParam> {
1125
- if (side === SwapSide.BUY) throw new Error(`Buy not supported`);
1126
-
1127
- const { exchange, i, j, underlyingSwap } = data;
1128
- const defaultArgs = [i, j, srcAmount, MIN_AMOUNT_TO_RECEIVE];
1129
- const swapMethod = underlyingSwap
1130
- ? CurveSwapFunctions.exchange_underlying
1131
- : CurveSwapFunctions.exchange;
1132
-
1133
- const swapData = this.ifaces.exchangeRouter.encodeFunctionData(
1134
- swapMethod,
1135
- defaultArgs,
1136
- );
1137
-
1138
- return this.buildSimpleParamWithoutWETHConversion(
1139
- srcToken,
1140
- srcAmount,
1141
- destToken,
1142
- destAmount,
1143
- swapData,
1144
- exchange,
1145
- );
1146
- }
1147
-
1148
- static getDirectFunctionNameV6(): string[] {
1149
- return [DIRECT_METHOD_NAME_V6];
1150
- }
1151
-
1152
- getDexParam(
1153
- srcToken: Address,
1154
- destToken: Address,
1155
- srcAmount: NumberAsString,
1156
- destAmount: NumberAsString,
1157
- recipient: Address,
1158
- data: CurveV1FactoryData,
1159
- side: SwapSide,
1160
- ): DexExchangeParam {
1161
- if (side === SwapSide.BUY) throw new Error(`Buy not supported`);
1162
-
1163
- const { exchange, i, j, underlyingSwap } = data;
1164
- const defaultArgs = [i, j, srcAmount, MIN_AMOUNT_TO_RECEIVE];
1165
- const swapMethod = underlyingSwap
1166
- ? CurveSwapFunctions.exchange_underlying
1167
- : CurveSwapFunctions.exchange;
1168
- const exchangeData = this.ifaces.exchangeRouter.encodeFunctionData(
1169
- swapMethod,
1170
- defaultArgs,
1171
- );
1172
-
1173
- return {
1174
- exchangeData,
1175
- needWrapNative: this.needWrapNative,
1176
- sendEthButSupportsInsertFromAmount: true,
1177
- dexFuncHasRecipient: false,
1178
- targetExchange: exchange,
1179
- returnAmountPos: undefined,
1180
- };
1181
- }
1182
-
1183
- async updatePoolState(): Promise<void> {
1184
- if (!this.areFactoryPoolsFetched) {
1185
- await this.fetchFactoryPools(undefined, false);
1186
- }
1187
-
1188
- await this.poolManager.fetchLiquiditiesFromApi();
1189
- }
1190
-
1191
- async getTopPoolsForToken(
1192
- tokenAddress: Address,
1193
- limit: number,
1194
- ): Promise<PoolLiquidity[]> {
1195
- if (!this.areFactoryPoolsFetched) {
1196
- this.logger.error(
1197
- `${this.dexKey}: received getTopPools while factory pools are not fetched`,
1198
- );
1199
- return [];
1200
- }
1201
- const _tokenAddress = tokenAddress.toLowerCase();
1202
- const poolsWithToken = this.poolManager.getPoolsWithToken(_tokenAddress);
1203
-
1204
- if (poolsWithToken.length === 0) {
1205
- return [];
1206
- }
1207
-
1208
- return poolsWithToken
1209
- .reduce<PoolLiquidity[]>((acc, pool) => {
1210
- let inCoins = false;
1211
- let inUnderlying = false;
1212
- const inCoinConnectors: Token[] = [];
1213
- for (const [i, coin] of pool.poolConstants.COINS.entries()) {
1214
- if (coin === _tokenAddress) {
1215
- inCoins = true;
1216
- } else {
1217
- inCoinConnectors.push({
1218
- address: coin,
1219
- decimals: pool.poolConstants.coins_decimals[i],
1220
- });
1221
- }
1222
- }
1223
-
1224
- const underlyingConnectors: Token[] = [];
1225
- for (const [underlying, i] of Object.entries(
1226
- pool.underlyingCoinsToIndices,
1227
- )) {
1228
- if (underlying === _tokenAddress) {
1229
- inUnderlying = true;
1230
- } else {
1231
- underlyingConnectors.push({
1232
- address: underlying,
1233
- decimals: pool.underlyingDecimals[i],
1234
- });
1235
- }
1236
- }
1237
- let connectorTokens: Token[] = [];
1238
- if (inCoins) {
1239
- connectorTokens = connectorTokens.concat(inCoinConnectors);
1240
- }
1241
- if (inUnderlying) {
1242
- connectorTokens = connectorTokens.concat(underlyingConnectors);
1243
- }
1244
- if (connectorTokens.length) {
1245
- acc.push({
1246
- exchange: this.dexKey,
1247
- address: pool.address,
1248
- liquidityUSD: pool.liquidityUSD,
1249
- connectorTokens: _.uniqBy(connectorTokens, 'address'),
1250
- });
1251
- }
1252
- return acc;
1253
- }, [])
1254
- .sort((a, b) => b.liquidityUSD - a.liquidityUSD)
1255
- .slice(0, limit);
1256
- }
1257
-
1258
- releaseResources(): AsyncOrSync<void> {
1259
- this.poolManager.releaseResources();
1260
- }
1261
-
1262
- private _calcRateMultipliers(coins_decimals: number[]): bigint[] {
1263
- return coins_decimals.map(coinDecimal => getBigIntPow(36 - coinDecimal));
1264
- }
1265
-
1266
- private _reportForUnspecifiedImplementation(
1267
- implementation: Address,
1268
- pool?: Address,
1269
- ) {
1270
- this.logger.warn(
1271
- `${this.dexKey}: on network ${this.dexHelper.config.data.network} ` +
1272
- `found unspecified implementation: ${implementation} for ${pool} pool. Skipping pool`,
1273
- );
1274
- }
1275
-
1276
- private _reportForUnspecifiedCustomPool(pool: Address) {
1277
- this.logger.error(
1278
- `${this.dexKey}: on network ${this.dexHelper.config.data.network} ` +
1279
- `found unspecified custom pool: ${pool}`,
1280
- );
1281
- }
1282
-
1283
- private _getCoinsABI(type: string): AbiItem {
1284
- const newCoinsType = _.cloneDeep(this.coinsTypeTemplate);
1285
- newCoinsType.inputs![0].type = type;
1286
- return newCoinsType;
1287
- }
1288
- }
1
+ import _ from 'lodash';
2
+ import { AbiItem } from 'web3-utils';
3
+ import { NumberAsString, OptimalSwapExchange } from '@paraswap/core';
4
+ import { assert, AsyncOrSync } from 'ts-essentials';
5
+ import { Interface, JsonFragment } from '@ethersproject/abi';
6
+ import {
7
+ Token,
8
+ Address,
9
+ ExchangePrices,
10
+ PoolPrices,
11
+ AdapterExchangeParam,
12
+ SimpleExchangeParam,
13
+ PoolLiquidity,
14
+ Logger,
15
+ TransferFeeParams,
16
+ TxInfo,
17
+ PreprocessTransactionOptions,
18
+ ExchangeTxInfo,
19
+ DexExchangeParam,
20
+ } from '../../types';
21
+ import {
22
+ SwapSide,
23
+ Network,
24
+ SRC_TOKEN_PARASWAP_TRANSFERS,
25
+ NULL_ADDRESS,
26
+ } from '../../constants';
27
+ import * as CALLDATA_GAS_COST from '../../calldata-gas-cost';
28
+ import {
29
+ getBigIntPow,
30
+ getDexKeysWithNetwork,
31
+ isETHAddress,
32
+ isSrcTokenTransferFeeToBeExchanged,
33
+ uuidToBytes16,
34
+ } from '../../utils';
35
+ import { IDex } from '../idex';
36
+ import { IDexHelper } from '../../dex-helper/idex-helper';
37
+ import {
38
+ CurveSwapFunctions,
39
+ CurveV1FactoryData,
40
+ CurveV1FactoryIfaces,
41
+ CurveV1SwapType,
42
+ CustomImplementationNames,
43
+ ImplementationNames,
44
+ PoolConstants,
45
+ DirectCurveV1Param,
46
+ CurveV1FactoryDirectSwap,
47
+ DirectCurveV1FactoryParamV6,
48
+ } from './types';
49
+ import {
50
+ getLocalDeadlineAsFriendlyPlaceholder,
51
+ SimpleExchange,
52
+ } from '../simple-exchange';
53
+ import { CurveV1FactoryConfig, Adapters } from './config';
54
+ import {
55
+ DIRECT_METHOD_NAME,
56
+ FACTORY_MAX_PLAIN_COINS,
57
+ FACTORY_MAX_PLAIN_IMPLEMENTATIONS_FOR_COIN,
58
+ MIN_AMOUNT_TO_RECEIVE,
59
+ POOL_EXCHANGE_GAS_COST,
60
+ } from './constants';
61
+ import { CurveV1FactoryPoolManager } from './curve-v1-pool-manager';
62
+ import CurveABI from '../../abi/Curve.json';
63
+ import DirectSwapABI from '../../abi/DirectSwap.json';
64
+ import FactoryCurveV1ABI from '../../abi/curve-v1-factory/FactoryCurveV1.json';
65
+ import ThreePoolABI from '../../abi/curve-v1-factory/ThreePool.json';
66
+ import ERC20ABI from '../../abi/erc20.json';
67
+ import {
68
+ addressDecode,
69
+ generalDecoder,
70
+ uint256DecodeToNumber,
71
+ uint8ToNumber,
72
+ } from '../../lib/decoders';
73
+ import { MultiCallParams, MultiResult } from '../../lib/multi-wrapper';
74
+ import { BigNumber, BytesLike } from 'ethers';
75
+ import { FactoryStateHandler } from './state-polling-pools/factory-pool-polling';
76
+ import { PoolPollingBase } from './state-polling-pools/pool-polling-base';
77
+ import { CustomBasePoolForFactory } from './state-polling-pools/custom-pool-polling';
78
+ import ImplementationConstants from './price-handlers/functions/constants';
79
+ import { applyTransferFee } from '../../lib/token-transfer-fee';
80
+ import { PriceHandler } from './price-handlers/price-handler';
81
+ import { hexConcat, hexZeroPad, hexlify } from 'ethers/lib/utils';
82
+ import { packCurveData } from '../../lib/curve/encoder';
83
+ import { encodeCurveAssets } from '../curve-v1/packer';
84
+
85
+ import { DIRECT_METHOD_NAME_V6 } from './constants';
86
+
87
+ const DefaultCoinsABI: AbiItem = {
88
+ type: 'function',
89
+ name: 'coins',
90
+ inputs: [
91
+ {
92
+ name: '',
93
+ type: 'uint256',
94
+ },
95
+ ],
96
+ outputs: [
97
+ {
98
+ name: '',
99
+ type: 'address',
100
+ },
101
+ ],
102
+ };
103
+
104
+ export class CurveV1Factory
105
+ extends SimpleExchange
106
+ implements
107
+ IDex<CurveV1FactoryData, DirectCurveV1Param | CurveV1FactoryDirectSwap>
108
+ {
109
+ readonly hasConstantPriceLargeAmounts = false;
110
+ readonly needWrapNative = false;
111
+ readonly isFeeOnTransferSupported = true;
112
+ readonly isStatePollingDex = true;
113
+
114
+ private factoryAddresses: string[];
115
+
116
+ readonly poolManager: CurveV1FactoryPoolManager;
117
+ readonly ifaces: CurveV1FactoryIfaces;
118
+
119
+ private areFactoryPoolsFetched: boolean = false;
120
+ private areCustomPoolsFetched: boolean = false;
121
+
122
+ readonly SRC_TOKEN_DEX_TRANSFERS = 1;
123
+ readonly DEST_TOKEN_DEX_TRANSFERS = 1;
124
+
125
+ readonly directSwapIface = new Interface(DirectSwapABI);
126
+
127
+ public static dexKeysWithNetwork: { key: string; networks: Network[] }[] =
128
+ getDexKeysWithNetwork(CurveV1FactoryConfig);
129
+
130
+ logger: Logger;
131
+
132
+ constructor(
133
+ readonly network: Network,
134
+ readonly dexKey: string,
135
+ readonly dexHelper: IDexHelper,
136
+ protected adapters = Adapters[network] || {},
137
+ protected config = CurveV1FactoryConfig[dexKey][network],
138
+ // This type is used to support different encoding for uint128 and uint256 args
139
+ private coinsTypeTemplate: AbiItem = DefaultCoinsABI,
140
+ ) {
141
+ super(dexHelper, dexKey);
142
+ this.logger = dexHelper.getLogger(`${this.dexKey}-${this.network}`);
143
+ this.factoryAddresses =
144
+ this.config.factories?.map(e => e.address.toLowerCase()) || [];
145
+ this.ifaces = {
146
+ exchangeRouter: new Interface(CurveABI),
147
+ factory: new Interface(FactoryCurveV1ABI as JsonFragment[]),
148
+ erc20: new Interface(ERC20ABI as JsonFragment[]),
149
+ threePool: new Interface(ThreePoolABI as JsonFragment[]),
150
+ };
151
+
152
+ // I had to put this initialization into CurveV1 because I have to have access to mapping
153
+ // from address to custom pool
154
+ const allPriceHandlers = Object.values(
155
+ this.config.factoryPoolImplementations,
156
+ ).reduce<Record<string, PriceHandler>>((acc, curr) => {
157
+ let baseImplementationName: ImplementationNames | undefined;
158
+ if (curr.basePoolAddress) {
159
+ const customBasePool = this.config.customPools[curr.basePoolAddress];
160
+ if (customBasePool === undefined) {
161
+ this.logger.error(
162
+ `${this.dexKey} on ${this.dexHelper.config.data.network}: can not get customBasePool from basePoolAddress`,
163
+ );
164
+ return acc;
165
+ } else {
166
+ baseImplementationName = customBasePool.name;
167
+ }
168
+ }
169
+ acc[curr.address] = new PriceHandler(
170
+ this.logger,
171
+ curr.name,
172
+ baseImplementationName,
173
+ );
174
+ return acc;
175
+ }, {});
176
+
177
+ Object.values(this.config.customPools).reduce((acc, curr) => {
178
+ if (curr.useForPricing) {
179
+ acc[curr.address] = new PriceHandler(this.logger, curr.name);
180
+ }
181
+
182
+ return acc;
183
+ }, allPriceHandlers);
184
+
185
+ this.poolManager = new CurveV1FactoryPoolManager(
186
+ this.dexKey,
187
+ dexHelper.getLogger(`${this.dexKey}-state-manager`),
188
+ dexHelper,
189
+ allPriceHandlers,
190
+ this.config.stateUpdatePeriodMs,
191
+ );
192
+ }
193
+
194
+ async initializePricing(blockNumber: number) {
195
+ // This is only to start timer, each pool is initialized with updated state
196
+ this.poolManager.initializePollingPools();
197
+ await this.fetchFactoryPools(blockNumber);
198
+ await this.poolManager.fetchLiquiditiesFromApi();
199
+ await this.poolManager.updatePollingPoolsInBatch();
200
+ this.logger.info(`${this.dexKey}: successfully initialized`);
201
+ }
202
+
203
+ async initializeCustomPollingPools(
204
+ factoryAddresses: string[],
205
+ blockNumber?: number,
206
+ // We don't want to initialize state for PoolTracker. It doesn't make any sense
207
+ initializeInitialState: boolean = true,
208
+ ) {
209
+ if (this.areCustomPoolsFetched) {
210
+ return;
211
+ }
212
+
213
+ await Promise.all(
214
+ factoryAddresses.map(async factoryAddress => {
215
+ try {
216
+ await Promise.all(
217
+ Object.values(this.config.customPools).map(async customPool => {
218
+ const poolIdentifier = this.getPoolIdentifier(
219
+ customPool.address,
220
+ false,
221
+ );
222
+
223
+ const poolContextConstants =
224
+ ImplementationConstants[customPool.name];
225
+ const {
226
+ N_COINS: nCoins,
227
+ USE_LENDING: useLending,
228
+ isLending,
229
+ } = poolContextConstants;
230
+
231
+ const coinsAndImplementations =
232
+ await this.dexHelper.multiWrapper.aggregate(
233
+ _.range(0, nCoins)
234
+ .map(i => ({
235
+ target: customPool.address,
236
+ callData: this.abiCoder.encodeFunctionCall(
237
+ this._getCoinsABI(customPool.coinsInputType),
238
+ [i.toString()],
239
+ ),
240
+ decodeFunction: addressDecode,
241
+ }))
242
+ .concat([
243
+ {
244
+ target: factoryAddress,
245
+ callData: this.ifaces.factory.encodeFunctionData(
246
+ 'get_implementation_address',
247
+ [customPool.address],
248
+ ),
249
+ decodeFunction: addressDecode,
250
+ },
251
+ ]),
252
+ );
253
+ const COINS = coinsAndImplementations.slice(0, -1);
254
+ const implementationAddress =
255
+ coinsAndImplementations.slice(-1)[0];
256
+
257
+ const coins_decimals = (
258
+ await this.dexHelper.multiWrapper.tryAggregate(
259
+ true,
260
+ COINS.map(c => ({
261
+ target: c,
262
+ callData: this.ifaces.erc20.encodeFunctionData(
263
+ 'decimals',
264
+ [],
265
+ ),
266
+ decodeFunction: uint8ToNumber,
267
+ })),
268
+ )
269
+ ).map(r => r.returnData);
270
+
271
+ const poolConstants: PoolConstants = {
272
+ COINS,
273
+ coins_decimals,
274
+ rate_multipliers: this._calcRateMultipliers(coins_decimals),
275
+ lpTokenAddress: customPool.lpTokenAddress,
276
+ };
277
+
278
+ let newPool: PoolPollingBase;
279
+ if (
280
+ Object.values<ImplementationNames>(
281
+ CustomImplementationNames,
282
+ ).includes(customPool.name)
283
+ ) {
284
+ // We don't want custom pools to be used for pricing, unless explicitly specified
285
+ newPool = new CustomBasePoolForFactory(
286
+ this.logger,
287
+ this.dexKey,
288
+ this.dexHelper.config.data.network,
289
+ this.cacheStateKey,
290
+ customPool.name,
291
+ implementationAddress,
292
+ customPool.address,
293
+ this.config.stateUpdatePeriodMs,
294
+ poolIdentifier,
295
+ poolConstants,
296
+ poolContextConstants,
297
+ customPool.liquidityApiSlug,
298
+ customPool.lpTokenAddress,
299
+ isLending,
300
+ customPool.balancesInputType,
301
+ useLending,
302
+ customPool.useForPricing,
303
+ );
304
+ } else {
305
+ // Use for pricing pools from factory
306
+ newPool = new CustomBasePoolForFactory(
307
+ this.logger,
308
+ this.dexKey,
309
+ this.dexHelper.config.data.network,
310
+ this.cacheStateKey,
311
+ customPool.name,
312
+ implementationAddress,
313
+ customPool.address,
314
+ this.config.stateUpdatePeriodMs,
315
+ poolIdentifier,
316
+ poolConstants,
317
+ poolContextConstants,
318
+ customPool.liquidityApiSlug,
319
+ customPool.lpTokenAddress,
320
+ isLending,
321
+ customPool.balancesInputType,
322
+ useLending,
323
+ true,
324
+ );
325
+ }
326
+
327
+ this.poolManager.initializeNewPoolForState(
328
+ poolIdentifier,
329
+ newPool,
330
+ );
331
+
332
+ if (initializeInitialState) {
333
+ await this.poolManager.initializeIndividualPollingPoolState(
334
+ poolIdentifier,
335
+ CustomBasePoolForFactory.IS_SRC_FEE_ON_TRANSFER_SUPPORTED,
336
+ blockNumber,
337
+ );
338
+ }
339
+ }),
340
+ );
341
+ } catch (e) {
342
+ this.logger.error(
343
+ `Error initializing custom polling pools for factory ${factoryAddress}: `,
344
+ e,
345
+ );
346
+ throw e;
347
+ }
348
+ }),
349
+ );
350
+
351
+ this.areCustomPoolsFetched = true;
352
+ }
353
+
354
+ async fetchFactoryPools(
355
+ blockNumber?: number,
356
+ // Variable initializeInitialState is only for poolTracker. We don't want to keep state updated with scheduler
357
+ // We just want to initialize factory pools and send request to CurveAPI
358
+ // Other values are not used
359
+ initializeInitialState: boolean = false,
360
+ ) {
361
+ if (this.areFactoryPoolsFetched) {
362
+ return;
363
+ }
364
+
365
+ if (!this.factoryAddresses || this.factoryAddresses.length == 0) {
366
+ this.logger.warn(`No factory address specified in configs`);
367
+ return;
368
+ }
369
+
370
+ // There is no scenario when we need to call initialize custom pools without factory pools
371
+ // So I put it here to not forget call, because custom pools must be initialised before factory pools
372
+ // This function may be called multiple times, but will execute only once
373
+ await this.initializeCustomPollingPools(
374
+ this.factoryAddresses,
375
+ blockNumber,
376
+ initializeInitialState,
377
+ );
378
+
379
+ await Promise.all(
380
+ this.factoryAddresses.map(async factoryAddress => {
381
+ try {
382
+ const poolCountResult =
383
+ await this.dexHelper.multiWrapper!.tryAggregate(true, [
384
+ {
385
+ target: factoryAddress,
386
+ callData: this.ifaces.factory.encodeFunctionData('pool_count'),
387
+ decodeFunction: uint256DecodeToNumber,
388
+ },
389
+ // This is used later to request all available implementations. In particular meta implementations
390
+ {
391
+ target: factoryAddress,
392
+ callData:
393
+ this.ifaces.factory.encodeFunctionData('base_pool_count'),
394
+ decodeFunction: uint256DecodeToNumber,
395
+ },
396
+ ]);
397
+
398
+ const poolCount = poolCountResult[0].returnData;
399
+ const basePoolCount = poolCountResult[1].returnData;
400
+
401
+ const calldataGetPoolAddresses = _.range(0, poolCount).map(i => ({
402
+ target: factoryAddress,
403
+ callData: this.ifaces.factory.encodeFunctionData('pool_list', [i]),
404
+ decodeFunction: addressDecode,
405
+ }));
406
+
407
+ const calldataGetBasePoolAddresses = _.range(0, basePoolCount).map(
408
+ i => ({
409
+ target: factoryAddress,
410
+ callData: this.ifaces.factory.encodeFunctionData(
411
+ 'base_pool_list',
412
+ [i],
413
+ ),
414
+ decodeFunction: addressDecode,
415
+ }),
416
+ );
417
+
418
+ const allPoolAddresses = (
419
+ await this.dexHelper.multiWrapper.tryAggregate(
420
+ true,
421
+ calldataGetPoolAddresses.concat(calldataGetBasePoolAddresses),
422
+ )
423
+ ).map(e => e.returnData);
424
+
425
+ const poolAddresses = allPoolAddresses.slice(0, poolCount);
426
+ const basePoolAddresses = allPoolAddresses.slice(poolCount);
427
+
428
+ const customPoolAddresses = Object.values(
429
+ this.config.customPools,
430
+ ).map(customPool => customPool.address);
431
+ basePoolAddresses.forEach(basePool => {
432
+ if (
433
+ !customPoolAddresses.includes(basePool) &&
434
+ !this.config.disabledPools.has(basePool)
435
+ ) {
436
+ this._reportForUnspecifiedCustomPool(basePool);
437
+ }
438
+ });
439
+
440
+ const factoryConfig = this.config.factories?.find(
441
+ ({ address }) =>
442
+ factoryAddress.toLowerCase() === address.toLowerCase(),
443
+ );
444
+
445
+ let callDataFromFactoryPools: MultiCallParams<
446
+ string[] | number[] | string
447
+ >[] = poolAddresses
448
+ .map(p => [
449
+ {
450
+ target: factoryAddress,
451
+ callData: this.ifaces.factory.encodeFunctionData(
452
+ 'get_implementation_address',
453
+ [p],
454
+ ),
455
+ decodeFunction: addressDecode,
456
+ },
457
+ {
458
+ target: factoryAddress,
459
+ callData: this.ifaces.factory.encodeFunctionData('get_coins', [
460
+ p,
461
+ ]),
462
+ decodeFunction: (
463
+ result: MultiResult<BytesLike> | BytesLike,
464
+ ): string[] =>
465
+ generalDecoder<string[]>(
466
+ result,
467
+ [
468
+ `address[${
469
+ factoryConfig?.maxPlainCoins?.toString() || ''
470
+ }]`,
471
+ ],
472
+ new Array(4).fill(NULL_ADDRESS),
473
+ parsed => parsed[0].map((p: string) => p.toLowerCase()),
474
+ ),
475
+ },
476
+ {
477
+ target: factoryAddress,
478
+ callData: this.ifaces.factory.encodeFunctionData(
479
+ 'get_decimals',
480
+ [p],
481
+ ),
482
+ decodeFunction: (
483
+ result: MultiResult<BytesLike> | BytesLike,
484
+ ): number[] =>
485
+ generalDecoder<number[]>(
486
+ result,
487
+ [
488
+ `uint256[${
489
+ factoryConfig?.maxPlainCoins?.toString() || ''
490
+ }]`,
491
+ ],
492
+ [0, 0, 0, 0],
493
+ parsed =>
494
+ parsed[0].map((p: BigNumber) => Number(p.toString())),
495
+ ),
496
+ },
497
+ ])
498
+ .flat();
499
+
500
+ // This is divider between pools related results and implementations
501
+ const factoryResultsDivider = callDataFromFactoryPools.length;
502
+
503
+ const metaPoolImplementations = factoryConfig?.isStableNg
504
+ ? []
505
+ : basePoolAddresses.map(basePoolAddress => ({
506
+ target: factoryAddress,
507
+ callData: this.ifaces.factory.encodeFunctionData(
508
+ 'metapool_implementations',
509
+ [basePoolAddress],
510
+ ),
511
+ decodeFunction: (
512
+ result: MultiResult<BytesLike> | BytesLike,
513
+ ): string[] =>
514
+ generalDecoder<string[]>(
515
+ result,
516
+ ['address[10]'],
517
+ new Array(10).fill(NULL_ADDRESS),
518
+ parsed => parsed[0].map((p: string) => p.toLowerCase()),
519
+ ),
520
+ }));
521
+
522
+ const plainImplementations = factoryConfig?.isStableNg
523
+ ? []
524
+ : _.flattenDeep(
525
+ _.range(2, FACTORY_MAX_PLAIN_COINS + 1).map(coinNumber =>
526
+ _.range(FACTORY_MAX_PLAIN_IMPLEMENTATIONS_FOR_COIN).map(
527
+ implInd => ({
528
+ target: factoryAddress,
529
+ callData: this.ifaces.factory.encodeFunctionData(
530
+ 'plain_implementations',
531
+ [coinNumber, implInd],
532
+ ),
533
+ decodeFunction: addressDecode,
534
+ }),
535
+ ),
536
+ ),
537
+ );
538
+
539
+ // Implementations must be requested from factory, but it accepts as arg basePool address
540
+ // for metaPools
541
+ callDataFromFactoryPools = callDataFromFactoryPools.concat(
542
+ ...metaPoolImplementations,
543
+ // To receive plain pool implementation address, you have to call plain_implementations
544
+ // with two variables: N_COINS and implementations_index
545
+ // N_COINS is between 2-4. Currently more than 4 coins is not supported
546
+ // as for implementation index, there are only 0-9 indexes
547
+ ...plainImplementations,
548
+ );
549
+
550
+ const allResultsFromFactory = (
551
+ await this.dexHelper.multiWrapper.tryAggregate<
552
+ string[] | number[] | string
553
+ >(true, callDataFromFactoryPools)
554
+ ).map(r => r.returnData);
555
+
556
+ const resultsFromFactory = allResultsFromFactory.slice(
557
+ 0,
558
+ factoryResultsDivider,
559
+ );
560
+
561
+ const allAvailableImplementations = _.flattenDeep(
562
+ allResultsFromFactory.slice(factoryResultsDivider) as string[],
563
+ ).filter(
564
+ implementation =>
565
+ implementation !== NULL_ADDRESS &&
566
+ !this.config.disabledImplementations.has(implementation),
567
+ );
568
+
569
+ allAvailableImplementations.forEach(implementation => {
570
+ const currentImplementation =
571
+ this.config.factoryPoolImplementations[implementation];
572
+ if (currentImplementation === undefined) {
573
+ this._reportForUnspecifiedImplementation(implementation);
574
+ }
575
+ });
576
+
577
+ const stateInitializePromises: Promise<void>[] = [];
578
+ _.chunk(resultsFromFactory, 3).forEach((result, i) => {
579
+ if (this.config.disabledPools.has(poolAddresses[i])) {
580
+ this.logger.trace(`Filtering disabled pool ${poolAddresses[i]}`);
581
+ return;
582
+ }
583
+
584
+ let [implementationAddress, coins, coins_decimals] = result as [
585
+ string,
586
+ string[],
587
+ number[],
588
+ ];
589
+
590
+ implementationAddress = implementationAddress.toLowerCase();
591
+ coins = coins
592
+ .map(c => c.toLowerCase())
593
+ .filter(c => c !== NULL_ADDRESS);
594
+ coins_decimals = coins_decimals.filter(cd => cd !== 0);
595
+
596
+ const factoryImplementationFromConfig =
597
+ this.config.factoryPoolImplementations[implementationAddress];
598
+
599
+ if (
600
+ factoryImplementationFromConfig === undefined &&
601
+ !this.config.disabledImplementations.has(implementationAddress)
602
+ ) {
603
+ this._reportForUnspecifiedImplementation(
604
+ implementationAddress,
605
+ poolAddresses[i],
606
+ );
607
+ return;
608
+ }
609
+
610
+ const factoryImplementationConstants =
611
+ ImplementationConstants[factoryImplementationFromConfig.name];
612
+
613
+ let isMeta: boolean = false;
614
+ let basePoolStateFetcher: PoolPollingBase | undefined;
615
+ if (factoryImplementationFromConfig.basePoolAddress !== undefined) {
616
+ isMeta = true;
617
+ const basePoolIdentifier = this.getPoolIdentifier(
618
+ factoryImplementationFromConfig.basePoolAddress,
619
+ false,
620
+ );
621
+ const basePool = this.poolManager.getPool(
622
+ basePoolIdentifier,
623
+ false,
624
+ );
625
+ if (basePool === null) {
626
+ this.logger.error(
627
+ `${this.dexKey}_${this.dexHelper.config.data.network}: custom base pool ${basePoolIdentifier} was not initialized properly. ` +
628
+ `You must call initializeCustomPollingPools before fetching factory`,
629
+ );
630
+ return;
631
+ }
632
+ basePoolStateFetcher = basePool;
633
+ }
634
+
635
+ const poolConstants: PoolConstants = {
636
+ COINS: coins,
637
+ coins_decimals,
638
+ rate_multipliers: this._calcRateMultipliers(coins_decimals),
639
+ };
640
+
641
+ const poolIdentifier = this.getPoolIdentifier(
642
+ poolAddresses[i],
643
+ isMeta,
644
+ );
645
+
646
+ const newPool = new FactoryStateHandler(
647
+ this.logger,
648
+ this.dexKey,
649
+ this.dexHelper.config.data.network,
650
+ this.cacheStateKey,
651
+ factoryImplementationFromConfig.name,
652
+ implementationAddress.toLowerCase(),
653
+ poolAddresses[i],
654
+ this.config,
655
+ factoryAddress,
656
+ poolIdentifier,
657
+ poolConstants,
658
+ factoryImplementationConstants,
659
+ factoryImplementationConstants.isFeeOnTransferSupported,
660
+ factoryImplementationFromConfig.liquidityApiSlug ?? '/factory',
661
+ basePoolStateFetcher,
662
+ factoryImplementationFromConfig.customGasCost,
663
+ factoryImplementationFromConfig.isStoreRateSupported,
664
+ );
665
+
666
+ this.poolManager.initializeNewPool(poolIdentifier, newPool);
667
+
668
+ if (initializeInitialState) {
669
+ stateInitializePromises.push(
670
+ this.poolManager.initializeIndividualPollingPoolState(
671
+ poolIdentifier,
672
+ factoryImplementationConstants.isFeeOnTransferSupported,
673
+ ),
674
+ );
675
+ }
676
+ });
677
+
678
+ await Promise.all(stateInitializePromises);
679
+ } catch (e) {
680
+ this.logger.error(
681
+ `Error fetching factory pools for ${factoryAddress}: `,
682
+ e,
683
+ );
684
+ throw e;
685
+ }
686
+ }),
687
+ );
688
+
689
+ this.areFactoryPoolsFetched = true;
690
+ }
691
+
692
+ getAdapters(side: SwapSide): { name: string; index: number }[] | null {
693
+ return this.adapters[side] ? this.adapters[side] : null;
694
+ }
695
+
696
+ getPoolIdentifier(poolAddress: string, isMeta: boolean): string {
697
+ return `${this.dexKey}_${poolAddress}_${isMeta}`;
698
+ }
699
+
700
+ async getPoolIdentifiers(
701
+ srcToken: Token,
702
+ destToken: Token,
703
+ side: SwapSide,
704
+ blockNumber: number,
705
+ ): Promise<string[]> {
706
+ if (side === SwapSide.BUY) {
707
+ return [];
708
+ }
709
+
710
+ const srcTokenAddress = srcToken.address.toLowerCase();
711
+ const destTokenAddress = destToken.address.toLowerCase();
712
+
713
+ if (srcTokenAddress === destTokenAddress) {
714
+ return [];
715
+ }
716
+
717
+ const pools = this.poolManager.getPoolsForPair(
718
+ srcTokenAddress,
719
+ destTokenAddress,
720
+ );
721
+
722
+ return pools.map(pool =>
723
+ this.getPoolIdentifier(pool.address, pool.isMetaPool),
724
+ );
725
+ }
726
+
727
+ async getPricesVolume(
728
+ srcToken: Token,
729
+ destToken: Token,
730
+ amounts: bigint[],
731
+ side: SwapSide,
732
+ blockNumber: number,
733
+ limitPools?: string[],
734
+ transferFees: TransferFeeParams = {
735
+ srcFee: 0,
736
+ destFee: 0,
737
+ srcDexFee: 0,
738
+ destDexFee: 0,
739
+ },
740
+ ): Promise<null | ExchangePrices<CurveV1FactoryData>> {
741
+ try {
742
+ if (side === SwapSide.BUY) {
743
+ return null;
744
+ }
745
+
746
+ const _isSrcTokenTransferFeeToBeExchanged =
747
+ isSrcTokenTransferFeeToBeExchanged(transferFees);
748
+
749
+ const srcTokenAddress = srcToken.address.toLowerCase();
750
+ const destTokenAddress = destToken.address.toLowerCase();
751
+
752
+ if (srcTokenAddress === destTokenAddress) {
753
+ return null;
754
+ }
755
+
756
+ let pools: PoolPollingBase[] = [];
757
+ if (limitPools !== undefined) {
758
+ pools = limitPools
759
+ .map(poolIdentifier =>
760
+ this.poolManager.getPool(
761
+ poolIdentifier,
762
+ _isSrcTokenTransferFeeToBeExchanged,
763
+ ),
764
+ )
765
+ .filter(
766
+ (pool): pool is PoolPollingBase =>
767
+ pool !== null &&
768
+ pool.getPoolData(srcTokenAddress, destTokenAddress) !== null,
769
+ );
770
+ } else {
771
+ pools = this.poolManager.getPoolsForPair(
772
+ srcTokenAddress,
773
+ destTokenAddress,
774
+ _isSrcTokenTransferFeeToBeExchanged,
775
+ );
776
+ }
777
+
778
+ if (pools.length <= 0) {
779
+ return null;
780
+ }
781
+
782
+ if (pools.length > 10) {
783
+ // I expect this to not happen
784
+ this.logger.warn(
785
+ `${this.dexKey}: There are more than 10 pools found for pair: ${srcTokenAddress}-${destTokenAddress}`,
786
+ );
787
+ }
788
+
789
+ const amountsWithUnit = [
790
+ getBigIntPow(srcToken.decimals),
791
+ ...amounts.slice(1),
792
+ ];
793
+ const amountsWithUnitAndFee = _isSrcTokenTransferFeeToBeExchanged
794
+ ? applyTransferFee(
795
+ applyTransferFee(
796
+ amountsWithUnit,
797
+ side,
798
+ transferFees.srcFee,
799
+ SRC_TOKEN_PARASWAP_TRANSFERS,
800
+ ),
801
+ side,
802
+ transferFees.srcDexFee,
803
+ this.SRC_TOKEN_DEX_TRANSFERS,
804
+ )
805
+ : amountsWithUnit;
806
+
807
+ const results = await Promise.all(
808
+ pools.map(
809
+ async (pool): Promise<PoolPrices<CurveV1FactoryData> | null> => {
810
+ let state = pool.getState();
811
+ if (!state) {
812
+ await this.poolManager.updateManuallyPollingPools(
813
+ pool.baseStatePoolPolling
814
+ ? [pool.baseStatePoolPolling, pool]
815
+ : [pool],
816
+ );
817
+ state = pool.getState();
818
+ if (!state) {
819
+ return null;
820
+ }
821
+ }
822
+
823
+ if (state.balances.every(b => b === 0n)) {
824
+ this.logger.trace(
825
+ `${this.dexKey} on ${this.dexHelper.config.data.network}: State balances equal to 0 in pool ${pool.address}`,
826
+ );
827
+ return null;
828
+ }
829
+
830
+ const poolData = pool.getPoolData(
831
+ srcTokenAddress,
832
+ destTokenAddress,
833
+ );
834
+
835
+ if (poolData === null) {
836
+ this.logger.error(
837
+ `${pool.fullName}: one or both tokens can not be exchanged in pool ${pool.address}: ${srcTokenAddress} -> ${destTokenAddress}`,
838
+ );
839
+ return null;
840
+ }
841
+
842
+ let outputs: bigint[] = this.poolManager
843
+ .getPriceHandler(pool.implementationAddress)
844
+ .getOutputs(
845
+ state,
846
+ amountsWithUnitAndFee,
847
+ poolData.i,
848
+ poolData.j,
849
+ poolData.underlyingSwap,
850
+ );
851
+
852
+ outputs = applyTransferFee(
853
+ outputs,
854
+ side,
855
+ transferFees.destDexFee,
856
+ this.DEST_TOKEN_DEX_TRANSFERS,
857
+ );
858
+
859
+ return {
860
+ prices: [0n, ...outputs.slice(1)],
861
+ unit: outputs[0],
862
+ data: poolData,
863
+ exchange: this.dexKey,
864
+ poolIdentifier: pool.poolIdentifier,
865
+ gasCost: POOL_EXCHANGE_GAS_COST,
866
+ poolAddresses: [pool.address],
867
+ };
868
+ },
869
+ ),
870
+ );
871
+
872
+ return results.filter(
873
+ (
874
+ r: PoolPrices<CurveV1FactoryData> | null,
875
+ ): r is PoolPrices<CurveV1FactoryData> => r !== null,
876
+ );
877
+ } catch (e) {
878
+ if (blockNumber === 0)
879
+ this.logger.error(
880
+ `Error_${this.dexKey}_${this.dexHelper.config.data.network}_getPrices: Aurelius block manager not yet instantiated`,
881
+ );
882
+ this.logger.error(
883
+ `Error_${this.dexKey}_${this.dexHelper.config.data.network}_getPrices`,
884
+ e,
885
+ );
886
+ return null;
887
+ }
888
+ }
889
+
890
+ getCalldataGasCost(
891
+ poolPrices: PoolPrices<CurveV1FactoryData>,
892
+ ): number | number[] {
893
+ return (
894
+ CALLDATA_GAS_COST.DEX_OVERHEAD +
895
+ CALLDATA_GAS_COST.LENGTH_SMALL +
896
+ CALLDATA_GAS_COST.INDEX +
897
+ CALLDATA_GAS_COST.INDEX +
898
+ CALLDATA_GAS_COST.TIMESTAMP +
899
+ CALLDATA_GAS_COST.BOOL
900
+ );
901
+ }
902
+
903
+ getAdapterParam(
904
+ srcToken: string,
905
+ destToken: string,
906
+ srcAmount: string,
907
+ destAmount: string,
908
+ data: CurveV1FactoryData,
909
+ side: SwapSide,
910
+ ): AdapterExchangeParam {
911
+ if (side === SwapSide.BUY) throw new Error(`Buy not supported`);
912
+
913
+ const { i, j, underlyingSwap } = data;
914
+ const payload = this.abiCoder.encodeParameter(
915
+ {
916
+ ParentStruct: {
917
+ i: 'int128',
918
+ j: 'int128',
919
+ deadline: 'uint256',
920
+ underlyingSwap: 'bool',
921
+ },
922
+ },
923
+ { i, j, deadline: 0, underlyingSwap },
924
+ );
925
+
926
+ return {
927
+ targetExchange: data.exchange,
928
+ payload,
929
+ networkFee: '0',
930
+ };
931
+ }
932
+
933
+ getTokenFromAddress(address: Address): Token {
934
+ // In this Dex decimals are not used
935
+ return { address, decimals: 0 };
936
+ }
937
+
938
+ async preProcessTransaction(
939
+ optimalSwapExchange: OptimalSwapExchange<CurveV1FactoryData>,
940
+ srcToken: Token,
941
+ _0: Token,
942
+ _1: SwapSide,
943
+ options: PreprocessTransactionOptions,
944
+ ): Promise<[OptimalSwapExchange<CurveV1FactoryData>, ExchangeTxInfo]> {
945
+ if (!options.isDirectMethod) {
946
+ return [
947
+ optimalSwapExchange,
948
+ {
949
+ deadline: BigInt(getLocalDeadlineAsFriendlyPlaceholder()),
950
+ },
951
+ ];
952
+ }
953
+
954
+ assert(
955
+ optimalSwapExchange.data !== undefined,
956
+ `preProcessTransaction: data field is missing`,
957
+ );
958
+
959
+ let isApproved: boolean | undefined;
960
+
961
+ try {
962
+ isApproved = await this.dexHelper.augustusApprovals.hasApproval(
963
+ options.executionContractAddress,
964
+ this.dexHelper.config.wrapETH(srcToken).address,
965
+ optimalSwapExchange.data.exchange,
966
+ );
967
+ } catch (e) {
968
+ this.logger.error(
969
+ `preProcessTransaction failed to retrieve allowance info: `,
970
+ e,
971
+ );
972
+ }
973
+
974
+ return [
975
+ {
976
+ ...optimalSwapExchange,
977
+ data: {
978
+ ...optimalSwapExchange.data,
979
+ isApproved,
980
+ },
981
+ },
982
+ {
983
+ deadline: BigInt(getLocalDeadlineAsFriendlyPlaceholder()),
984
+ },
985
+ ];
986
+ }
987
+
988
+ getDirectParam(
989
+ srcToken: Address,
990
+ destToken: Address,
991
+ srcAmount: NumberAsString,
992
+ destAmount: NumberAsString,
993
+ expectedAmount: NumberAsString,
994
+ data: CurveV1FactoryData,
995
+ side: SwapSide,
996
+ permit: string,
997
+ uuid: string,
998
+ feePercent: NumberAsString,
999
+ deadline: NumberAsString,
1000
+ partner: string,
1001
+ beneficiary: string,
1002
+ contractMethod?: string,
1003
+ ): TxInfo<DirectCurveV1Param> {
1004
+ if (contractMethod !== DIRECT_METHOD_NAME) {
1005
+ throw new Error(`Invalid contract method ${contractMethod}`);
1006
+ }
1007
+ assert(side === SwapSide.SELL, 'Buy not supported');
1008
+
1009
+ let isApproved: boolean = !!data.isApproved;
1010
+ if (data.isApproved === undefined) {
1011
+ this.logger.warn(`isApproved is undefined, defaulting to false`);
1012
+ }
1013
+
1014
+ const swapParams: DirectCurveV1Param = [
1015
+ srcToken,
1016
+ destToken,
1017
+ data.exchange,
1018
+ srcAmount,
1019
+ destAmount,
1020
+ expectedAmount,
1021
+ feePercent,
1022
+ data.i.toString(),
1023
+ data.j.toString(),
1024
+ partner,
1025
+ isApproved,
1026
+ data.underlyingSwap
1027
+ ? CurveV1SwapType.EXCHANGE_UNDERLYING
1028
+ : CurveV1SwapType.EXCHANGE,
1029
+ beneficiary,
1030
+ // For CurveV1 we work as it is, without wrapping and unwrapping
1031
+ false,
1032
+ permit,
1033
+ uuidToBytes16(uuid),
1034
+ ];
1035
+
1036
+ const encoder = (...params: DirectCurveV1Param) => {
1037
+ return this.directSwapIface.encodeFunctionData(DIRECT_METHOD_NAME, [
1038
+ params,
1039
+ ]);
1040
+ };
1041
+
1042
+ return {
1043
+ params: swapParams,
1044
+ encoder,
1045
+ networkFee: '0',
1046
+ };
1047
+ }
1048
+
1049
+ getDirectParamV6(
1050
+ srcToken: Address,
1051
+ destToken: Address,
1052
+ fromAmount: NumberAsString,
1053
+ toAmount: NumberAsString,
1054
+ quotedAmount: NumberAsString,
1055
+ data: CurveV1FactoryData,
1056
+ side: SwapSide,
1057
+ permit: string,
1058
+ uuid: string,
1059
+ partnerAndFee: string,
1060
+ beneficiary: string,
1061
+ blockNumber: number,
1062
+ contractMethod?: string,
1063
+ ) {
1064
+ if (contractMethod !== DIRECT_METHOD_NAME_V6) {
1065
+ throw new Error(`Invalid contract method ${contractMethod}`);
1066
+ }
1067
+ assert(side === SwapSide.SELL, 'Buy not supported');
1068
+
1069
+ const metadata = hexConcat([
1070
+ hexZeroPad(uuidToBytes16(uuid), 16),
1071
+ hexZeroPad(hexlify(blockNumber), 16),
1072
+ ]);
1073
+
1074
+ const swapParams: DirectCurveV1FactoryParamV6 = [
1075
+ packCurveData(
1076
+ data.exchange,
1077
+ !data.isApproved, // approve flag, if not approved then set to true
1078
+ isETHAddress(destToken) ? 0 : isETHAddress(srcToken) ? 3 : 0,
1079
+ data.underlyingSwap
1080
+ ? CurveV1SwapType.EXCHANGE_UNDERLYING
1081
+ : CurveV1SwapType.EXCHANGE,
1082
+ ).toString(),
1083
+ encodeCurveAssets(data.i, data.j).toString(),
1084
+ srcToken,
1085
+ destToken,
1086
+ fromAmount,
1087
+ toAmount,
1088
+ quotedAmount,
1089
+ metadata,
1090
+ beneficiary,
1091
+ ];
1092
+
1093
+ const encodeParams: CurveV1FactoryDirectSwap = [
1094
+ swapParams,
1095
+ partnerAndFee,
1096
+ permit,
1097
+ ];
1098
+
1099
+ const encoder = (...params: CurveV1FactoryDirectSwap) => {
1100
+ return this.augustusV6Interface.encodeFunctionData(
1101
+ DIRECT_METHOD_NAME_V6,
1102
+ [...params],
1103
+ );
1104
+ };
1105
+
1106
+ return {
1107
+ encoder,
1108
+ params: encodeParams,
1109
+ networkFee: '0',
1110
+ };
1111
+ }
1112
+
1113
+ static getDirectFunctionName(): string[] {
1114
+ return [DIRECT_METHOD_NAME];
1115
+ }
1116
+
1117
+ async getSimpleParam(
1118
+ srcToken: string,
1119
+ destToken: string,
1120
+ srcAmount: string,
1121
+ destAmount: string,
1122
+ data: CurveV1FactoryData,
1123
+ side: SwapSide,
1124
+ ): Promise<SimpleExchangeParam> {
1125
+ if (side === SwapSide.BUY) throw new Error(`Buy not supported`);
1126
+
1127
+ const { exchange, i, j, underlyingSwap } = data;
1128
+ const defaultArgs = [i, j, srcAmount, MIN_AMOUNT_TO_RECEIVE];
1129
+ const swapMethod = underlyingSwap
1130
+ ? CurveSwapFunctions.exchange_underlying
1131
+ : CurveSwapFunctions.exchange;
1132
+
1133
+ const swapData = this.ifaces.exchangeRouter.encodeFunctionData(
1134
+ swapMethod,
1135
+ defaultArgs,
1136
+ );
1137
+
1138
+ return this.buildSimpleParamWithoutWETHConversion(
1139
+ srcToken,
1140
+ srcAmount,
1141
+ destToken,
1142
+ destAmount,
1143
+ swapData,
1144
+ exchange,
1145
+ );
1146
+ }
1147
+
1148
+ static getDirectFunctionNameV6(): string[] {
1149
+ return [DIRECT_METHOD_NAME_V6];
1150
+ }
1151
+
1152
+ getDexParam(
1153
+ srcToken: Address,
1154
+ destToken: Address,
1155
+ srcAmount: NumberAsString,
1156
+ destAmount: NumberAsString,
1157
+ recipient: Address,
1158
+ data: CurveV1FactoryData,
1159
+ side: SwapSide,
1160
+ ): DexExchangeParam {
1161
+ if (side === SwapSide.BUY) throw new Error(`Buy not supported`);
1162
+
1163
+ const { exchange, i, j, underlyingSwap } = data;
1164
+ const defaultArgs = [i, j, srcAmount, MIN_AMOUNT_TO_RECEIVE];
1165
+ const swapMethod = underlyingSwap
1166
+ ? CurveSwapFunctions.exchange_underlying
1167
+ : CurveSwapFunctions.exchange;
1168
+ const exchangeData = this.ifaces.exchangeRouter.encodeFunctionData(
1169
+ swapMethod,
1170
+ defaultArgs,
1171
+ );
1172
+
1173
+ return {
1174
+ exchangeData,
1175
+ needWrapNative: this.needWrapNative,
1176
+ sendEthButSupportsInsertFromAmount: true,
1177
+ dexFuncHasRecipient: false,
1178
+ targetExchange: exchange,
1179
+ returnAmountPos: undefined,
1180
+ };
1181
+ }
1182
+
1183
+ async updatePoolState(): Promise<void> {
1184
+ if (!this.areFactoryPoolsFetched) {
1185
+ await this.fetchFactoryPools(undefined, false);
1186
+ }
1187
+
1188
+ await this.poolManager.fetchLiquiditiesFromApi();
1189
+ }
1190
+
1191
+ async getTopPoolsForToken(
1192
+ tokenAddress: Address,
1193
+ limit: number,
1194
+ ): Promise<PoolLiquidity[]> {
1195
+ if (!this.areFactoryPoolsFetched) {
1196
+ this.logger.error(
1197
+ `${this.dexKey}: received getTopPools while factory pools are not fetched`,
1198
+ );
1199
+ return [];
1200
+ }
1201
+ const _tokenAddress = tokenAddress.toLowerCase();
1202
+ const poolsWithToken = this.poolManager.getPoolsWithToken(_tokenAddress);
1203
+
1204
+ if (poolsWithToken.length === 0) {
1205
+ return [];
1206
+ }
1207
+
1208
+ return poolsWithToken
1209
+ .reduce<PoolLiquidity[]>((acc, pool) => {
1210
+ let inCoins = false;
1211
+ let inUnderlying = false;
1212
+ const inCoinConnectors: Token[] = [];
1213
+ for (const [i, coin] of pool.poolConstants.COINS.entries()) {
1214
+ if (coin === _tokenAddress) {
1215
+ inCoins = true;
1216
+ } else {
1217
+ inCoinConnectors.push({
1218
+ address: coin,
1219
+ decimals: pool.poolConstants.coins_decimals[i],
1220
+ });
1221
+ }
1222
+ }
1223
+
1224
+ const underlyingConnectors: Token[] = [];
1225
+ for (const [underlying, i] of Object.entries(
1226
+ pool.underlyingCoinsToIndices,
1227
+ )) {
1228
+ if (underlying === _tokenAddress) {
1229
+ inUnderlying = true;
1230
+ } else {
1231
+ underlyingConnectors.push({
1232
+ address: underlying,
1233
+ decimals: pool.underlyingDecimals[i],
1234
+ });
1235
+ }
1236
+ }
1237
+ let connectorTokens: Token[] = [];
1238
+ if (inCoins) {
1239
+ connectorTokens = connectorTokens.concat(inCoinConnectors);
1240
+ }
1241
+ if (inUnderlying) {
1242
+ connectorTokens = connectorTokens.concat(underlyingConnectors);
1243
+ }
1244
+ if (connectorTokens.length) {
1245
+ acc.push({
1246
+ exchange: this.dexKey,
1247
+ address: pool.address,
1248
+ liquidityUSD: pool.liquidityUSD,
1249
+ connectorTokens: _.uniqBy(connectorTokens, 'address'),
1250
+ });
1251
+ }
1252
+ return acc;
1253
+ }, [])
1254
+ .sort((a, b) => b.liquidityUSD - a.liquidityUSD)
1255
+ .slice(0, limit);
1256
+ }
1257
+
1258
+ releaseResources(): AsyncOrSync<void> {
1259
+ this.poolManager.releaseResources();
1260
+ }
1261
+
1262
+ private _calcRateMultipliers(coins_decimals: number[]): bigint[] {
1263
+ return coins_decimals.map(coinDecimal => getBigIntPow(36 - coinDecimal));
1264
+ }
1265
+
1266
+ private _reportForUnspecifiedImplementation(
1267
+ implementation: Address,
1268
+ pool?: Address,
1269
+ ) {
1270
+ this.logger.warn(
1271
+ `${this.dexKey}: on network ${this.dexHelper.config.data.network} ` +
1272
+ `found unspecified implementation: ${implementation} for ${pool} pool. Skipping pool`,
1273
+ );
1274
+ }
1275
+
1276
+ private _reportForUnspecifiedCustomPool(pool: Address) {
1277
+ this.logger.error(
1278
+ `${this.dexKey}: on network ${this.dexHelper.config.data.network} ` +
1279
+ `found unspecified custom pool: ${pool}`,
1280
+ );
1281
+ }
1282
+
1283
+ private _getCoinsABI(type: string): AbiItem {
1284
+ const newCoinsType = _.cloneDeep(this.coinsTypeTemplate);
1285
+ newCoinsType.inputs![0].type = type;
1286
+ return newCoinsType;
1287
+ }
1288
+ }