@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,873 +1,873 @@
1
- import {
2
- DEST_TOKEN_PARASWAP_TRANSFERS,
3
- Network,
4
- NULL_ADDRESS,
5
- SRC_TOKEN_PARASWAP_TRANSFERS,
6
- SUBGRAPH_TIMEOUT,
7
- } from '../../constants';
8
- import {
9
- AdapterExchangeParam,
10
- Address,
11
- DexExchangeParam,
12
- ExchangePrices,
13
- Log,
14
- Logger,
15
- PoolLiquidity,
16
- PoolPrices,
17
- SimpleExchangeParam,
18
- Token,
19
- TransferFeeParams,
20
- } from '../../types';
21
- import { IDexHelper } from '../../dex-helper';
22
- import {
23
- UniswapParam,
24
- UniswapV2Functions,
25
- UniswapV2Data,
26
- UniswapPool,
27
- } from '../uniswap-v2/types';
28
- import { getBigIntPow, getDexKeysWithNetwork, isETHAddress } from '../../utils';
29
- import camelotFactoryABI from '../../abi/camelot/CamelotFactory.json';
30
- import camelotPairABI from '../../abi/camelot/CamelotPair.json';
31
- import _ from 'lodash';
32
- import { AsyncOrSync, DeepReadonly } from 'ts-essentials';
33
- import { NumberAsString, SwapSide } from '@paraswap/core';
34
- import { Interface, AbiCoder } from '@ethersproject/abi';
35
- import { SolidlyStablePool } from '../solidly/solidly-stable-pool';
36
- import { Uniswapv2ConstantProductPool } from '../uniswap-v2/uniswap-v2-constant-product-pool';
37
- import { CamelotPoolState, CamelotPoolOrderedParams } from './types';
38
- import { CamelotConfig, Adapters } from './config';
39
- import { Contract } from 'web3-eth-contract';
40
- import { IDex } from '../idex';
41
- import { StatefulEventSubscriber } from '../../stateful-event-subscriber';
42
- import ParaSwapABI from '../../abi/IParaswap.json';
43
- import UniswapV2ExchangeRouterABI from '../../abi/UniswapV2ExchangeRouter.json';
44
- import { SimpleExchange } from '../simple-exchange';
45
- import { applyTransferFee } from '../../lib/token-transfer-fee';
46
- import * as CALLDATA_GAS_COST from '../../calldata-gas-cost';
47
- import { SolidlyData } from '../solidly/types';
48
- import {
49
- OnPoolCreatedCallback,
50
- UniswapV2Factory,
51
- } from '../uniswap-v2/uniswap-v2-factory';
52
- import {
53
- hexDataLength,
54
- hexlify,
55
- hexZeroPad,
56
- id,
57
- solidityPack,
58
- } from 'ethers/lib/utils';
59
- import { BigNumber } from 'ethers';
60
- import { Flag, SpecialDex } from '../../executor/types';
61
-
62
- const DefaultCamelotPoolGasCost = 90 * 1000;
63
-
64
- const camelotPairIface = new Interface(camelotPairABI);
65
- const coder = new AbiCoder();
66
-
67
- const LogCallTopics = [
68
- '0x1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1', // event Sync(uint112 reserve0, uint112 reserve1) // uni-V2 and most forks
69
- 'a4877b8ecb5a00ba277e4bceeeb187a669e7113649774dfbea05c259ce27f17b', // event FeePercentUpdated(uint16 token0FeePercent, uint16 token1FeePercent)
70
- 'b6a86710bde53aa7fb1b3856279e2af5b476d53e2dd0902cf17a0911b5a43a8b', // event SetStableSwap(bool prevStableSwap, bool stableSwap)
71
- ];
72
-
73
- function encodePools(
74
- pools: UniswapPool[],
75
- feeFactor: number,
76
- ): NumberAsString[] {
77
- return pools.map(({ fee, direction, address }) => {
78
- return (
79
- (BigInt(Math.ceil((feeFactor - fee) / 10)) << 161n) +
80
- ((direction ? 0n : 1n) << 160n) +
81
- BigInt(address)
82
- ).toString();
83
- });
84
- }
85
-
86
- export interface CamelotPair {
87
- token0: Token;
88
- token1: Token;
89
- exchange?: Address;
90
- pool?: CamelotEventPool;
91
- }
92
-
93
- export class CamelotEventPool extends StatefulEventSubscriber<CamelotPoolState> {
94
- decoder = (log: Log) => this.iface.parseLog(log);
95
-
96
- constructor(
97
- parentName: string,
98
- protected dexHelper: IDexHelper,
99
- private poolAddress: Address,
100
- private token0: Token,
101
- private token1: Token,
102
- logger: Logger,
103
- private iface: Interface = camelotPairIface,
104
- private callEntries: any[],
105
- private callDecoder: (data: any[]) => {
106
- reserve0: string;
107
- reserve1: string;
108
- token0FeeCode: number;
109
- token1FeeCode: number;
110
- stable: boolean;
111
- },
112
- ) {
113
- super(
114
- parentName,
115
- (token0.symbol || token0.address) +
116
- '-' +
117
- (token1.symbol || token1.address) +
118
- ' pool',
119
- dexHelper,
120
- logger,
121
- );
122
- }
123
-
124
- protected processLog(
125
- state: DeepReadonly<CamelotPoolState>,
126
- log: Readonly<Log>,
127
- ): AsyncOrSync<DeepReadonly<CamelotPoolState> | null> {
128
- if (!LogCallTopics.includes(log.topics[0])) return null;
129
-
130
- const event = this.decoder(log);
131
- switch (event.name) {
132
- case 'SetStableSwap':
133
- return {
134
- reserve0: state.reserve0,
135
- reserve1: state.reserve1,
136
- token0FeeCode: state.token0FeeCode,
137
- token1FeeCode: state.token1FeeCode,
138
- stable: event.args.stableSwap,
139
- };
140
- case 'FeePercentUpdated':
141
- return {
142
- reserve0: state.reserve0,
143
- reserve1: state.reserve1,
144
- token0FeeCode: parseInt(event.args.token0FeePercent.toString()),
145
- token1FeeCode: parseInt(event.args.token1FeePercent.toString()),
146
- stable: state.stable,
147
- };
148
- case 'Sync':
149
- return {
150
- reserve0: event.args.reserve0.toString(),
151
- reserve1: event.args.reserve1.toString(),
152
- token0FeeCode: state.token0FeeCode,
153
- token1FeeCode: state.token1FeeCode,
154
- stable: state.stable,
155
- };
156
- }
157
- return null;
158
- }
159
-
160
- async generateState(
161
- blockNumber: number | 'latest' = 'latest',
162
- ): Promise<DeepReadonly<CamelotPoolState>> {
163
- const data: { returnData: any[] } =
164
- await this.dexHelper.multiContract.methods
165
- .aggregate(this.callEntries)
166
- .call({}, blockNumber);
167
-
168
- return this.callDecoder(data.returnData);
169
- }
170
- }
171
-
172
- export class Camelot
173
- extends SimpleExchange
174
- implements IDex<SolidlyData, UniswapParam>
175
- {
176
- pairs: { [key: string]: CamelotPair } = {};
177
- feeFactor = 100000;
178
- factory: Contract;
179
-
180
- needWrapNative = true;
181
- routerInterface: Interface;
182
- exchangeRouterInterface: Interface;
183
-
184
- readonly hasConstantPriceLargeAmounts: boolean = false;
185
- readonly isFeeOnTransferSupported: boolean = true;
186
- readonly DEST_TOKEN_DEX_TRANSFERS = 1;
187
- logger: Logger;
188
-
189
- private readonly factoryInst: UniswapV2Factory;
190
-
191
- private newlyCreatedPoolKeys: Set<string> = new Set();
192
-
193
- public static dexKeysWithNetwork: { key: string; networks: Network[] }[] =
194
- getDexKeysWithNetwork(CamelotConfig);
195
-
196
- constructor(
197
- protected network: Network,
198
- dexKey: string,
199
- protected dexHelper: IDexHelper,
200
- protected factoryAddress: Address = CamelotConfig[dexKey][network]
201
- .factoryAddress,
202
- protected subgraphURL: string | undefined = CamelotConfig[dexKey] &&
203
- CamelotConfig[dexKey][network].subgraphURL,
204
- protected initCode: string = CamelotConfig[dexKey][network].initCode,
205
- protected poolGasCost: number = (CamelotConfig[dexKey] &&
206
- CamelotConfig[dexKey][network].poolGasCost) ??
207
- DefaultCamelotPoolGasCost,
208
- protected decoderIface: Interface = camelotPairIface,
209
- protected adapters = (CamelotConfig[dexKey] &&
210
- CamelotConfig[dexKey][network].adapters) ??
211
- Adapters[network],
212
- protected routerAddress = (CamelotConfig[dexKey] &&
213
- CamelotConfig[dexKey][network].router) ??
214
- dexHelper.config.data.uniswapV2ExchangeRouterAddress,
215
- ) {
216
- super(dexHelper, dexKey);
217
- this.logger = dexHelper.getLogger(dexKey);
218
-
219
- this.factory = new dexHelper.web3Provider.eth.Contract(
220
- camelotFactoryABI as any,
221
- factoryAddress,
222
- );
223
- this.routerAddress = routerAddress;
224
-
225
- this.routerInterface = new Interface(ParaSwapABI);
226
- this.exchangeRouterInterface = new Interface(UniswapV2ExchangeRouterABI);
227
-
228
- this.factoryInst = new UniswapV2Factory(
229
- dexHelper,
230
- dexKey,
231
- factoryAddress,
232
- this.logger,
233
- this.onPoolCreatedDeleteFromNonExistingSet,
234
- );
235
- }
236
-
237
- async initializePricing(blockNumber: number) {
238
- // Init listening to new pools creation
239
- await this.factoryInst.initialize(blockNumber);
240
- }
241
-
242
- private getPoolIdentifier(token0: string, token1: string) {
243
- const [_token0, _token1] =
244
- token0.toLowerCase() < token1.toLowerCase()
245
- ? [token0, token1]
246
- : [token1, token0];
247
-
248
- const poolKey = `${this.dexKey}_${_token0}_${_token1}`.toLowerCase();
249
-
250
- return poolKey;
251
- }
252
-
253
- /*
254
- * When a non existing pool is queried, it's blacklisted for an arbitrary long period in order to prevent issuing too many rpc calls
255
- * Once the pool is created, it gets immediately flagged
256
- */
257
- onPoolCreatedDeleteFromNonExistingSet: OnPoolCreatedCallback = async ({
258
- token0,
259
- token1,
260
- }) => {
261
- const logPrefix = '[onPoolCreatedDeleteFromNonExistingSet]';
262
-
263
- try {
264
- const poolKey = this.getPoolIdentifier(token0, token1);
265
-
266
- this.newlyCreatedPoolKeys.add(poolKey);
267
-
268
- // delete entry locally to let local instance discover the pool
269
- delete this.pairs[poolKey];
270
-
271
- this.logger.info(`${logPrefix} discovered new pool ${poolKey}`);
272
- } catch (e) {
273
- this.logger.error(
274
- `${logPrefix} LOGIC ERROR on ack new pool (token0=${token0},token1=${token1})`,
275
- e,
276
- );
277
- }
278
- };
279
-
280
- getPoolStatesMultiCallData(pair: CamelotPair): {
281
- callEntries: any[];
282
- callDecoder: (data: any[]) => {
283
- reserve0: string;
284
- reserve1: string;
285
- token0FeeCode: number;
286
- token1FeeCode: number;
287
- stable: boolean;
288
- };
289
- } {
290
- const callEntries = [
291
- {
292
- target: pair.exchange,
293
- callData: camelotPairIface.encodeFunctionData('getReserves', []),
294
- },
295
- {
296
- target: pair.exchange,
297
- callData: camelotPairIface.encodeFunctionData('stableSwap', []),
298
- },
299
- ];
300
-
301
- const callDecoder = (data: any[]) => {
302
- const info = coder.decode(
303
- ['uint112', 'uint112', 'uint16', 'uint16'],
304
- data[0],
305
- );
306
- const reserve0: string = info[0].toString();
307
- const reserve1: string = info[1].toString();
308
- const token0FeeCode: number = parseInt(info[2].toString());
309
- const token1FeeCode: number = parseInt(info[3].toString());
310
- const stable: boolean = coder.decode(['bool'], data[1])[0];
311
-
312
- return { reserve0, reserve1, token0FeeCode, token1FeeCode, stable };
313
- };
314
- return { callEntries, callDecoder };
315
- }
316
-
317
- protected async addPool(
318
- pair: CamelotPair,
319
- reserve0: string,
320
- reserve1: string,
321
- token0FeeCode: number,
322
- token1FeeCode: number,
323
- stable: boolean,
324
- blockNumber: number,
325
- ) {
326
- const { callEntries, callDecoder } =
327
- this.getPoolStatesMultiCallData(pair) || {};
328
- pair.pool = new CamelotEventPool(
329
- this.dexKey,
330
- this.dexHelper,
331
- pair.exchange!,
332
- pair.token0,
333
- pair.token1,
334
- this.logger,
335
- this.decoderIface,
336
- callEntries,
337
- callDecoder,
338
- );
339
- pair.pool.addressesSubscribed.push(pair.exchange!);
340
-
341
- await pair.pool.initialize(blockNumber, {
342
- state: { reserve0, reserve1, token0FeeCode, token1FeeCode, stable },
343
- });
344
- }
345
-
346
- async getBuyPrice(
347
- priceParams: CamelotPoolOrderedParams,
348
- srcAmount: bigint,
349
- ): Promise<bigint> {
350
- if (priceParams.stable) throw new Error('Buy not supported');
351
-
352
- return Uniswapv2ConstantProductPool.getBuyPrice(
353
- priceParams,
354
- srcAmount,
355
- this.feeFactor,
356
- );
357
- }
358
-
359
- async getSellPrice(
360
- priceParams: CamelotPoolOrderedParams,
361
- srcAmount: bigint,
362
- ): Promise<bigint> {
363
- return priceParams.stable
364
- ? SolidlyStablePool.getSellPrice(priceParams, srcAmount, this.feeFactor)
365
- : Uniswapv2ConstantProductPool.getSellPrice(
366
- priceParams,
367
- srcAmount,
368
- this.feeFactor,
369
- );
370
- }
371
-
372
- async getBuyPricePath(
373
- amount: bigint,
374
- params: CamelotPoolOrderedParams[],
375
- ): Promise<bigint> {
376
- let price = amount;
377
- for (const param of params.reverse()) {
378
- price = await this.getBuyPrice(param, price);
379
- }
380
- return price;
381
- }
382
-
383
- async getSellPricePath(
384
- amount: bigint,
385
- params: CamelotPoolOrderedParams[],
386
- ): Promise<bigint> {
387
- let price = amount;
388
- for (const param of params) {
389
- price = await this.getSellPrice(param, price);
390
- }
391
- return price;
392
- }
393
-
394
- async findPair(from: Token, to: Token) {
395
- if (from.address.toLowerCase() === to.address.toLowerCase()) return null;
396
- const [token0, token1] =
397
- from.address.toLowerCase() < to.address.toLowerCase()
398
- ? [from, to]
399
- : [to, from];
400
-
401
- const key = this.getPoolIdentifier(token0.address, token1.address);
402
- let pair = this.pairs[key];
403
- if (pair) return pair;
404
- const exchange = await this.factory.methods
405
- .getPair(token0.address, token1.address)
406
- .call();
407
- if (exchange === NULL_ADDRESS) {
408
- // if the pool has been newly created to not allow this op as we can run into race condition between pool discovery and concurrent pricing request touching this pool
409
- if (!this.newlyCreatedPoolKeys.has(key)) {
410
- pair = { token0, token1 };
411
- }
412
- } else {
413
- pair = { token0, token1, exchange };
414
- }
415
- this.pairs[key] = pair;
416
- return pair;
417
- }
418
-
419
- async getManyPoolStates(
420
- pairs: CamelotPair[],
421
- blockNumber: number,
422
- ): Promise<CamelotPoolState[]> {
423
- try {
424
- const callData = pairs
425
- .map((pair, i) => {
426
- return this.getPoolStatesMultiCallData(pair).callEntries;
427
- })
428
- .flat();
429
-
430
- const data: { returnData: any[] } =
431
- await this.dexHelper.multiContract.methods
432
- .aggregate(callData)
433
- .call({}, blockNumber);
434
-
435
- const returnData = _.chunk(data.returnData, 2);
436
-
437
- return pairs.map((pair, i) => {
438
- return this.getPoolStatesMultiCallData(pair).callDecoder(returnData[i]);
439
- });
440
- } catch (e) {
441
- this.logger.error(
442
- `Error_getManyPoolReserves could not get reserves with error:`,
443
- e,
444
- );
445
- return [];
446
- }
447
- }
448
-
449
- async batchCatchUpPairs(pairs: [Token, Token][], blockNumber: number) {
450
- if (!blockNumber) return;
451
- const pairsToFetch: CamelotPair[] = [];
452
- for (const _pair of pairs) {
453
- const pair = await this.findPair(_pair[0], _pair[1]);
454
- if (!(pair && pair.exchange)) continue;
455
- if (!pair.pool) {
456
- pairsToFetch.push(pair);
457
- } else if (!pair.pool.getState(blockNumber)) {
458
- pairsToFetch.push(pair);
459
- }
460
- }
461
-
462
- if (!pairsToFetch.length) return;
463
-
464
- const pairsStates = await this.getManyPoolStates(pairsToFetch, blockNumber);
465
-
466
- if (pairsStates.length !== pairsToFetch.length) {
467
- this.logger.error(
468
- `Error_getManyPoolReserves didn't get any pool reserves`,
469
- );
470
- }
471
-
472
- for (let i = 0; i < pairsToFetch.length; i++) {
473
- const pairState = pairsStates[i];
474
- const pair = pairsToFetch[i];
475
- if (!pair.pool) {
476
- await this.addPool(
477
- pair,
478
- pairState.reserve0,
479
- pairState.reserve1,
480
- pairState.token0FeeCode,
481
- pairState.token1FeeCode,
482
- pairState.stable,
483
- blockNumber,
484
- );
485
- } else pair.pool.setState(pairState, blockNumber);
486
- }
487
- }
488
-
489
- async getPairOrderedParams(
490
- from: Token,
491
- to: Token,
492
- blockNumber: number,
493
- tokenDexTransferFee: number,
494
- ): Promise<CamelotPoolOrderedParams | null> {
495
- const pair = await this.findPair(from, to);
496
- if (!(pair && pair.pool && pair.exchange)) return null;
497
- const pairState = pair.pool.getState(blockNumber);
498
- if (!pairState) {
499
- this.logger.error(
500
- `Error_orderPairParams expected reserves, got none (maybe the pool doesn't exist) ${
501
- from.symbol || from.address
502
- } ${to.symbol || to.address}`,
503
- );
504
- return null;
505
- }
506
-
507
- const pairReversed =
508
- pair.token1.address.toLowerCase() === from.address.toLowerCase();
509
- if (pairReversed) {
510
- return {
511
- tokenIn: from.address,
512
- tokenOut: to.address,
513
- reservesIn: pairState.reserve1,
514
- reservesOut: pairState.reserve0,
515
- decimalsIn: from.decimals,
516
- decimalsOut: to.decimals,
517
- fee: (pairState.token1FeeCode + tokenDexTransferFee).toString(),
518
- direction: false,
519
- exchange: pair.exchange,
520
- stable: pairState.stable,
521
- };
522
- }
523
- return {
524
- tokenIn: from.address,
525
- tokenOut: to.address,
526
- decimalsIn: from.decimals,
527
- decimalsOut: to.decimals,
528
- reservesIn: pairState.reserve0,
529
- reservesOut: pairState.reserve1,
530
- fee: (pairState.token0FeeCode + tokenDexTransferFee).toString(),
531
- direction: true,
532
- exchange: pair.exchange,
533
- stable: pairState.stable,
534
- };
535
- }
536
-
537
- async getPoolIdentifiers(
538
- _from: Token,
539
- _to: Token,
540
- side: SwapSide,
541
- blockNumber: number,
542
- ): Promise<string[]> {
543
- if (side === SwapSide.BUY) return [];
544
-
545
- const from = this.dexHelper.config.wrapETH(_from);
546
- const to = this.dexHelper.config.wrapETH(_to);
547
-
548
- if (from.address.toLowerCase() === to.address.toLowerCase()) {
549
- return [];
550
- }
551
-
552
- const poolKey = this.getPoolIdentifier(from.address, to.address);
553
-
554
- return [poolKey];
555
- }
556
-
557
- async getPricesVolume(
558
- _from: Token,
559
- _to: Token,
560
- amounts: bigint[],
561
- side: SwapSide,
562
- blockNumber: number,
563
- // list of pool identifiers to use for pricing, if undefined use all pools
564
- limitPools?: string[],
565
- transferFees: TransferFeeParams = {
566
- srcFee: 0,
567
- destFee: 0,
568
- srcDexFee: 0,
569
- destDexFee: 0,
570
- },
571
- ): Promise<ExchangePrices<SolidlyData> | null> {
572
- try {
573
- const from = this.dexHelper.config.wrapETH(_from);
574
- const to = this.dexHelper.config.wrapETH(_to);
575
-
576
- if (from.address.toLowerCase() === to.address.toLowerCase()) {
577
- return null;
578
- }
579
-
580
- const poolIdentifier = this.getPoolIdentifier(from.address, to.address);
581
-
582
- if (limitPools && limitPools.every(p => p !== poolIdentifier))
583
- return null;
584
-
585
- await this.batchCatchUpPairs([[from, to]], blockNumber);
586
- const isSell = side === SwapSide.SELL;
587
- const pairParam = await this.getPairOrderedParams(
588
- from,
589
- to,
590
- blockNumber,
591
- transferFees.srcDexFee,
592
- );
593
-
594
- if (!pairParam || (side === SwapSide.BUY && pairParam.stable))
595
- return null;
596
-
597
- const unitAmount = getBigIntPow(isSell ? from.decimals : to.decimals);
598
-
599
- const [unitVolumeWithFee, ...amountsWithFee] = applyTransferFee(
600
- [unitAmount, ...amounts],
601
- side,
602
- isSell ? transferFees.srcFee : transferFees.destFee,
603
- isSell ? SRC_TOKEN_PARASWAP_TRANSFERS : DEST_TOKEN_PARASWAP_TRANSFERS,
604
- );
605
-
606
- const unit = isSell
607
- ? await this.getSellPricePath(unitVolumeWithFee, [pairParam])
608
- : await this.getBuyPricePath(unitVolumeWithFee, [pairParam]);
609
-
610
- const prices = isSell
611
- ? await Promise.all(
612
- amountsWithFee.map(amount =>
613
- this.getSellPricePath(amount, [pairParam]),
614
- ),
615
- )
616
- : await Promise.all(
617
- amountsWithFee.map(amount =>
618
- this.getBuyPricePath(amount, [pairParam]),
619
- ),
620
- );
621
-
622
- const [unitOutWithFee, ...outputsWithFee] = applyTransferFee(
623
- [unit, ...prices],
624
- side,
625
- // This part is confusing, because we treat differently SELL and BUY fees
626
- // If Buy, we should apply transfer fee on srcToken on top of dexFee applied earlier
627
- // But for Sell we should apply only one dexFee
628
- isSell ? transferFees.destDexFee : transferFees.srcFee,
629
- isSell ? this.DEST_TOKEN_DEX_TRANSFERS : SRC_TOKEN_PARASWAP_TRANSFERS,
630
- );
631
-
632
- // As camelot just has one pool per token pair
633
- return [
634
- {
635
- prices: outputsWithFee,
636
- unit: unitOutWithFee,
637
- data: {
638
- router:
639
- side === SwapSide.SELL
640
- ? this.routerAddress
641
- : this.dexHelper.config.data.uniswapV2ExchangeRouterAddress,
642
- path: [from.address.toLowerCase(), to.address.toLowerCase()],
643
- factory: this.factoryAddress,
644
- initCode: this.initCode,
645
- feeFactor: this.feeFactor,
646
- isFeeTokenInRoute: Object.values(transferFees).some(f => f !== 0),
647
- pools: [
648
- {
649
- address: pairParam.exchange,
650
- fee: parseInt(pairParam.fee),
651
- direction: pairParam.direction,
652
- },
653
- ],
654
- },
655
- exchange: this.dexKey,
656
- poolIdentifier,
657
- gasCost: this.poolGasCost,
658
- poolAddresses: [pairParam.exchange],
659
- },
660
- ];
661
- } catch (e) {
662
- if (blockNumber === 0)
663
- this.logger.error(
664
- `Error_getPricesVolume: Aurelius block manager not yet instantiated`,
665
- );
666
- this.logger.error(`Error_getPrices:`, e);
667
- return null;
668
- }
669
- }
670
-
671
- // Returns estimated gas cost of calldata for this DEX in multiSwap
672
- getCalldataGasCost(poolPrices: PoolPrices<UniswapV2Data>): number | number[] {
673
- return (
674
- CALLDATA_GAS_COST.DEX_OVERHEAD +
675
- CALLDATA_GAS_COST.LENGTH_SMALL +
676
- // ParentStruct header
677
- CALLDATA_GAS_COST.OFFSET_SMALL +
678
- // ParentStruct -> weth
679
- CALLDATA_GAS_COST.ADDRESS +
680
- // ParentStruct -> pools[] header
681
- CALLDATA_GAS_COST.OFFSET_SMALL +
682
- // ParentStruct -> pools[]
683
- CALLDATA_GAS_COST.LENGTH_SMALL +
684
- // ParentStruct -> pools[0]
685
- CALLDATA_GAS_COST.wordNonZeroBytes(22)
686
- );
687
- }
688
-
689
- getAdapters(side: SwapSide): { name: string; index: number }[] | null {
690
- return this.adapters[side];
691
- }
692
-
693
- async getTopPoolsForToken(
694
- tokenAddress: Address,
695
- count: number,
696
- ): Promise<PoolLiquidity[]> {
697
- if (!this.subgraphURL) return [];
698
-
699
- const query = `query ($token: Bytes!, $count: Int) {
700
- pools0: pairs(first: $count, orderBy: reserveUSD, orderDirection: desc, where: {token0: $token, reserve0_gt: 1, reserve1_gt: 1}) {
701
- id
702
- isStable
703
- token0 {
704
- id
705
- decimals
706
- }
707
- token1 {
708
- id
709
- decimals
710
- }
711
- reserveUSD
712
- }
713
- pools1: pairs(first: $count, orderBy: reserveUSD, orderDirection: desc, where: {token1: $token, reserve0_gt: 1, reserve1_gt: 1}) {
714
- id
715
- isStable
716
- token0 {
717
- id
718
- decimals
719
- }
720
- token1 {
721
- id
722
- decimals
723
- }
724
- reserveUSD
725
- }
726
- }`;
727
-
728
- const { data } = await this.dexHelper.httpRequest.post(
729
- this.subgraphURL,
730
- {
731
- query,
732
- variables: { token: tokenAddress.toLowerCase(), count },
733
- },
734
- SUBGRAPH_TIMEOUT,
735
- );
736
-
737
- if (!(data && data.pools0 && data.pools1))
738
- throw new Error("Couldn't fetch the pools from the subgraph");
739
- const pools0 = _.map(data.pools0, pool => ({
740
- exchange: this.dexKey,
741
- stable: pool.isStable,
742
- address: pool.id.toLowerCase(),
743
- connectorTokens: [
744
- {
745
- address: pool.token1.id.toLowerCase(),
746
- decimals: parseInt(pool.token1.decimals),
747
- },
748
- ],
749
- liquidityUSD: parseFloat(pool.reserveUSD),
750
- }));
751
-
752
- const pools1 = _.map(data.pools1, pool => ({
753
- exchange: this.dexKey,
754
- stable: pool.isStable,
755
- address: pool.id.toLowerCase(),
756
- connectorTokens: [
757
- {
758
- address: pool.token0.id.toLowerCase(),
759
- decimals: parseInt(pool.token0.decimals),
760
- },
761
- ],
762
- liquidityUSD: parseFloat(pool.reserveUSD),
763
- }));
764
-
765
- return _.slice(
766
- _.sortBy(_.concat(pools0, pools1), [pool => -1 * pool.liquidityUSD]),
767
- 0,
768
- count,
769
- );
770
- }
771
-
772
- getWETHAddress(srcToken: Address, destToken: Address, weth?: Address) {
773
- if (!isETHAddress(srcToken) && !isETHAddress(destToken))
774
- return NULL_ADDRESS;
775
- return weth || this.dexHelper.config.data.wrappedNativeTokenAddress;
776
- }
777
-
778
- getAdapterParam(
779
- srcToken: Address,
780
- destToken: Address,
781
- srcAmount: NumberAsString,
782
- toAmount: NumberAsString, // required for buy case
783
- data: SolidlyData,
784
- side: SwapSide,
785
- ): AdapterExchangeParam {
786
- if (side === SwapSide.BUY) throw new Error('Buy not supported');
787
-
788
- const pools = encodePools(data.pools, this.feeFactor);
789
- const weth = this.getWETHAddress(srcToken, destToken, data.wethAddress);
790
- const payload = this.abiCoder.encodeParameter(
791
- {
792
- ParentStruct: {
793
- weth: 'address',
794
- pools: 'uint256[]',
795
- isFeeTokenInRoute: 'bool',
796
- },
797
- },
798
- { pools, weth, isFeeTokenInRoute: data.isFeeTokenInRoute },
799
- );
800
- return {
801
- targetExchange: data.router,
802
- payload,
803
- networkFee: '0',
804
- };
805
- }
806
-
807
- async getSimpleParam(
808
- src: Address,
809
- dest: Address,
810
- srcAmount: NumberAsString,
811
- destAmount: NumberAsString,
812
- data: SolidlyData,
813
- side: SwapSide,
814
- ): Promise<SimpleExchangeParam> {
815
- if (side === SwapSide.BUY) throw new Error('Buy not supported');
816
-
817
- const pools = encodePools(data.pools, this.feeFactor);
818
- const weth = this.getWETHAddress(src, dest, data.wethAddress);
819
- const swapData = this.exchangeRouterInterface.encodeFunctionData(
820
- side === SwapSide.SELL ? UniswapV2Functions.swap : UniswapV2Functions.buy,
821
- [src, srcAmount, destAmount, weth, pools],
822
- );
823
-
824
- return this.buildSimpleParamWithoutWETHConversion(
825
- src,
826
- srcAmount,
827
- dest,
828
- destAmount,
829
- swapData,
830
- data.router,
831
- );
832
- }
833
-
834
- getDexParam(
835
- srcToken: Address,
836
- destToken: Address,
837
- srcAmount: NumberAsString,
838
- destAmount: NumberAsString,
839
- recipient: Address,
840
- data: SolidlyData,
841
- side: SwapSide,
842
- ): DexExchangeParam {
843
- if (side === SwapSide.BUY) throw new Error('Buy not supported');
844
- let exchangeDataTypes = ['bytes4', 'bytes32'];
845
-
846
- let exchangeDataToPack = [
847
- hexZeroPad(hexlify(0), 4),
848
- hexZeroPad(hexlify(data.pools.length), 32),
849
- ];
850
-
851
- const pools = encodePools(data.pools, this.feeFactor);
852
- pools.forEach(pool => {
853
- exchangeDataTypes.push('bytes32');
854
- exchangeDataToPack.push(hexZeroPad(hexlify(BigNumber.from(pool)), 32));
855
- });
856
-
857
- const exchangeData = solidityPack(exchangeDataTypes, exchangeDataToPack);
858
-
859
- return {
860
- needWrapNative: this.needWrapNative,
861
- dexFuncHasRecipient: true,
862
- exchangeData,
863
- targetExchange: recipient,
864
- specialDexFlag: data.isFeeTokenInRoute
865
- ? SpecialDex.SWAP_ON_DYSTOPIA_UNISWAP_V2_FORK_WITH_FEE
866
- : SpecialDex.SWAP_ON_DYSTOPIA_UNISWAP_V2_FORK,
867
- transferSrcTokenBeforeSwap: data.isFeeTokenInRoute
868
- ? undefined
869
- : data.pools[0].address,
870
- returnAmountPos: undefined,
871
- };
872
- }
873
- }
1
+ import {
2
+ DEST_TOKEN_PARASWAP_TRANSFERS,
3
+ Network,
4
+ NULL_ADDRESS,
5
+ SRC_TOKEN_PARASWAP_TRANSFERS,
6
+ SUBGRAPH_TIMEOUT,
7
+ } from '../../constants';
8
+ import {
9
+ AdapterExchangeParam,
10
+ Address,
11
+ DexExchangeParam,
12
+ ExchangePrices,
13
+ Log,
14
+ Logger,
15
+ PoolLiquidity,
16
+ PoolPrices,
17
+ SimpleExchangeParam,
18
+ Token,
19
+ TransferFeeParams,
20
+ } from '../../types';
21
+ import { IDexHelper } from '../../dex-helper';
22
+ import {
23
+ UniswapParam,
24
+ UniswapV2Functions,
25
+ UniswapV2Data,
26
+ UniswapPool,
27
+ } from '../uniswap-v2/types';
28
+ import { getBigIntPow, getDexKeysWithNetwork, isETHAddress } from '../../utils';
29
+ import camelotFactoryABI from '../../abi/camelot/CamelotFactory.json';
30
+ import camelotPairABI from '../../abi/camelot/CamelotPair.json';
31
+ import _ from 'lodash';
32
+ import { AsyncOrSync, DeepReadonly } from 'ts-essentials';
33
+ import { NumberAsString, SwapSide } from '@paraswap/core';
34
+ import { Interface, AbiCoder } from '@ethersproject/abi';
35
+ import { SolidlyStablePool } from '../solidly/solidly-stable-pool';
36
+ import { Uniswapv2ConstantProductPool } from '../uniswap-v2/uniswap-v2-constant-product-pool';
37
+ import { CamelotPoolState, CamelotPoolOrderedParams } from './types';
38
+ import { CamelotConfig, Adapters } from './config';
39
+ import { Contract } from 'web3-eth-contract';
40
+ import { IDex } from '../idex';
41
+ import { StatefulEventSubscriber } from '../../stateful-event-subscriber';
42
+ import ParaSwapABI from '../../abi/IParaswap.json';
43
+ import UniswapV2ExchangeRouterABI from '../../abi/UniswapV2ExchangeRouter.json';
44
+ import { SimpleExchange } from '../simple-exchange';
45
+ import { applyTransferFee } from '../../lib/token-transfer-fee';
46
+ import * as CALLDATA_GAS_COST from '../../calldata-gas-cost';
47
+ import { SolidlyData } from '../solidly/types';
48
+ import {
49
+ OnPoolCreatedCallback,
50
+ UniswapV2Factory,
51
+ } from '../uniswap-v2/uniswap-v2-factory';
52
+ import {
53
+ hexDataLength,
54
+ hexlify,
55
+ hexZeroPad,
56
+ id,
57
+ solidityPack,
58
+ } from 'ethers/lib/utils';
59
+ import { BigNumber } from 'ethers';
60
+ import { Flag, SpecialDex } from '../../executor/types';
61
+
62
+ const DefaultCamelotPoolGasCost = 90 * 1000;
63
+
64
+ const camelotPairIface = new Interface(camelotPairABI);
65
+ const coder = new AbiCoder();
66
+
67
+ const LogCallTopics = [
68
+ '0x1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1', // event Sync(uint112 reserve0, uint112 reserve1) // uni-V2 and most forks
69
+ 'a4877b8ecb5a00ba277e4bceeeb187a669e7113649774dfbea05c259ce27f17b', // event FeePercentUpdated(uint16 token0FeePercent, uint16 token1FeePercent)
70
+ 'b6a86710bde53aa7fb1b3856279e2af5b476d53e2dd0902cf17a0911b5a43a8b', // event SetStableSwap(bool prevStableSwap, bool stableSwap)
71
+ ];
72
+
73
+ function encodePools(
74
+ pools: UniswapPool[],
75
+ feeFactor: number,
76
+ ): NumberAsString[] {
77
+ return pools.map(({ fee, direction, address }) => {
78
+ return (
79
+ (BigInt(Math.ceil((feeFactor - fee) / 10)) << 161n) +
80
+ ((direction ? 0n : 1n) << 160n) +
81
+ BigInt(address)
82
+ ).toString();
83
+ });
84
+ }
85
+
86
+ export interface CamelotPair {
87
+ token0: Token;
88
+ token1: Token;
89
+ exchange?: Address;
90
+ pool?: CamelotEventPool;
91
+ }
92
+
93
+ export class CamelotEventPool extends StatefulEventSubscriber<CamelotPoolState> {
94
+ decoder = (log: Log) => this.iface.parseLog(log);
95
+
96
+ constructor(
97
+ parentName: string,
98
+ protected dexHelper: IDexHelper,
99
+ private poolAddress: Address,
100
+ private token0: Token,
101
+ private token1: Token,
102
+ logger: Logger,
103
+ private iface: Interface = camelotPairIface,
104
+ private callEntries: any[],
105
+ private callDecoder: (data: any[]) => {
106
+ reserve0: string;
107
+ reserve1: string;
108
+ token0FeeCode: number;
109
+ token1FeeCode: number;
110
+ stable: boolean;
111
+ },
112
+ ) {
113
+ super(
114
+ parentName,
115
+ (token0.symbol || token0.address) +
116
+ '-' +
117
+ (token1.symbol || token1.address) +
118
+ ' pool',
119
+ dexHelper,
120
+ logger,
121
+ );
122
+ }
123
+
124
+ protected processLog(
125
+ state: DeepReadonly<CamelotPoolState>,
126
+ log: Readonly<Log>,
127
+ ): AsyncOrSync<DeepReadonly<CamelotPoolState> | null> {
128
+ if (!LogCallTopics.includes(log.topics[0])) return null;
129
+
130
+ const event = this.decoder(log);
131
+ switch (event.name) {
132
+ case 'SetStableSwap':
133
+ return {
134
+ reserve0: state.reserve0,
135
+ reserve1: state.reserve1,
136
+ token0FeeCode: state.token0FeeCode,
137
+ token1FeeCode: state.token1FeeCode,
138
+ stable: event.args.stableSwap,
139
+ };
140
+ case 'FeePercentUpdated':
141
+ return {
142
+ reserve0: state.reserve0,
143
+ reserve1: state.reserve1,
144
+ token0FeeCode: parseInt(event.args.token0FeePercent.toString()),
145
+ token1FeeCode: parseInt(event.args.token1FeePercent.toString()),
146
+ stable: state.stable,
147
+ };
148
+ case 'Sync':
149
+ return {
150
+ reserve0: event.args.reserve0.toString(),
151
+ reserve1: event.args.reserve1.toString(),
152
+ token0FeeCode: state.token0FeeCode,
153
+ token1FeeCode: state.token1FeeCode,
154
+ stable: state.stable,
155
+ };
156
+ }
157
+ return null;
158
+ }
159
+
160
+ async generateState(
161
+ blockNumber: number | 'latest' = 'latest',
162
+ ): Promise<DeepReadonly<CamelotPoolState>> {
163
+ const data: { returnData: any[] } =
164
+ await this.dexHelper.multiContract.methods
165
+ .aggregate(this.callEntries)
166
+ .call({}, blockNumber);
167
+
168
+ return this.callDecoder(data.returnData);
169
+ }
170
+ }
171
+
172
+ export class Camelot
173
+ extends SimpleExchange
174
+ implements IDex<SolidlyData, UniswapParam>
175
+ {
176
+ pairs: { [key: string]: CamelotPair } = {};
177
+ feeFactor = 100000;
178
+ factory: Contract;
179
+
180
+ needWrapNative = true;
181
+ routerInterface: Interface;
182
+ exchangeRouterInterface: Interface;
183
+
184
+ readonly hasConstantPriceLargeAmounts: boolean = false;
185
+ readonly isFeeOnTransferSupported: boolean = true;
186
+ readonly DEST_TOKEN_DEX_TRANSFERS = 1;
187
+ logger: Logger;
188
+
189
+ private readonly factoryInst: UniswapV2Factory;
190
+
191
+ private newlyCreatedPoolKeys: Set<string> = new Set();
192
+
193
+ public static dexKeysWithNetwork: { key: string; networks: Network[] }[] =
194
+ getDexKeysWithNetwork(CamelotConfig);
195
+
196
+ constructor(
197
+ protected network: Network,
198
+ dexKey: string,
199
+ protected dexHelper: IDexHelper,
200
+ protected factoryAddress: Address = CamelotConfig[dexKey][network]
201
+ .factoryAddress,
202
+ protected subgraphURL: string | undefined = CamelotConfig[dexKey] &&
203
+ CamelotConfig[dexKey][network].subgraphURL,
204
+ protected initCode: string = CamelotConfig[dexKey][network].initCode,
205
+ protected poolGasCost: number = (CamelotConfig[dexKey] &&
206
+ CamelotConfig[dexKey][network].poolGasCost) ??
207
+ DefaultCamelotPoolGasCost,
208
+ protected decoderIface: Interface = camelotPairIface,
209
+ protected adapters = (CamelotConfig[dexKey] &&
210
+ CamelotConfig[dexKey][network].adapters) ??
211
+ Adapters[network],
212
+ protected routerAddress = (CamelotConfig[dexKey] &&
213
+ CamelotConfig[dexKey][network].router) ??
214
+ dexHelper.config.data.uniswapV2ExchangeRouterAddress,
215
+ ) {
216
+ super(dexHelper, dexKey);
217
+ this.logger = dexHelper.getLogger(dexKey);
218
+
219
+ this.factory = new dexHelper.web3Provider.eth.Contract(
220
+ camelotFactoryABI as any,
221
+ factoryAddress,
222
+ );
223
+ this.routerAddress = routerAddress;
224
+
225
+ this.routerInterface = new Interface(ParaSwapABI);
226
+ this.exchangeRouterInterface = new Interface(UniswapV2ExchangeRouterABI);
227
+
228
+ this.factoryInst = new UniswapV2Factory(
229
+ dexHelper,
230
+ dexKey,
231
+ factoryAddress,
232
+ this.logger,
233
+ this.onPoolCreatedDeleteFromNonExistingSet,
234
+ );
235
+ }
236
+
237
+ async initializePricing(blockNumber: number) {
238
+ // Init listening to new pools creation
239
+ await this.factoryInst.initialize(blockNumber);
240
+ }
241
+
242
+ private getPoolIdentifier(token0: string, token1: string) {
243
+ const [_token0, _token1] =
244
+ token0.toLowerCase() < token1.toLowerCase()
245
+ ? [token0, token1]
246
+ : [token1, token0];
247
+
248
+ const poolKey = `${this.dexKey}_${_token0}_${_token1}`.toLowerCase();
249
+
250
+ return poolKey;
251
+ }
252
+
253
+ /*
254
+ * When a non existing pool is queried, it's blacklisted for an arbitrary long period in order to prevent issuing too many rpc calls
255
+ * Once the pool is created, it gets immediately flagged
256
+ */
257
+ onPoolCreatedDeleteFromNonExistingSet: OnPoolCreatedCallback = async ({
258
+ token0,
259
+ token1,
260
+ }) => {
261
+ const logPrefix = '[onPoolCreatedDeleteFromNonExistingSet]';
262
+
263
+ try {
264
+ const poolKey = this.getPoolIdentifier(token0, token1);
265
+
266
+ this.newlyCreatedPoolKeys.add(poolKey);
267
+
268
+ // delete entry locally to let local instance discover the pool
269
+ delete this.pairs[poolKey];
270
+
271
+ this.logger.info(`${logPrefix} discovered new pool ${poolKey}`);
272
+ } catch (e) {
273
+ this.logger.error(
274
+ `${logPrefix} LOGIC ERROR on ack new pool (token0=${token0},token1=${token1})`,
275
+ e,
276
+ );
277
+ }
278
+ };
279
+
280
+ getPoolStatesMultiCallData(pair: CamelotPair): {
281
+ callEntries: any[];
282
+ callDecoder: (data: any[]) => {
283
+ reserve0: string;
284
+ reserve1: string;
285
+ token0FeeCode: number;
286
+ token1FeeCode: number;
287
+ stable: boolean;
288
+ };
289
+ } {
290
+ const callEntries = [
291
+ {
292
+ target: pair.exchange,
293
+ callData: camelotPairIface.encodeFunctionData('getReserves', []),
294
+ },
295
+ {
296
+ target: pair.exchange,
297
+ callData: camelotPairIface.encodeFunctionData('stableSwap', []),
298
+ },
299
+ ];
300
+
301
+ const callDecoder = (data: any[]) => {
302
+ const info = coder.decode(
303
+ ['uint112', 'uint112', 'uint16', 'uint16'],
304
+ data[0],
305
+ );
306
+ const reserve0: string = info[0].toString();
307
+ const reserve1: string = info[1].toString();
308
+ const token0FeeCode: number = parseInt(info[2].toString());
309
+ const token1FeeCode: number = parseInt(info[3].toString());
310
+ const stable: boolean = coder.decode(['bool'], data[1])[0];
311
+
312
+ return { reserve0, reserve1, token0FeeCode, token1FeeCode, stable };
313
+ };
314
+ return { callEntries, callDecoder };
315
+ }
316
+
317
+ protected async addPool(
318
+ pair: CamelotPair,
319
+ reserve0: string,
320
+ reserve1: string,
321
+ token0FeeCode: number,
322
+ token1FeeCode: number,
323
+ stable: boolean,
324
+ blockNumber: number,
325
+ ) {
326
+ const { callEntries, callDecoder } =
327
+ this.getPoolStatesMultiCallData(pair) || {};
328
+ pair.pool = new CamelotEventPool(
329
+ this.dexKey,
330
+ this.dexHelper,
331
+ pair.exchange!,
332
+ pair.token0,
333
+ pair.token1,
334
+ this.logger,
335
+ this.decoderIface,
336
+ callEntries,
337
+ callDecoder,
338
+ );
339
+ pair.pool.addressesSubscribed.push(pair.exchange!);
340
+
341
+ await pair.pool.initialize(blockNumber, {
342
+ state: { reserve0, reserve1, token0FeeCode, token1FeeCode, stable },
343
+ });
344
+ }
345
+
346
+ async getBuyPrice(
347
+ priceParams: CamelotPoolOrderedParams,
348
+ srcAmount: bigint,
349
+ ): Promise<bigint> {
350
+ if (priceParams.stable) throw new Error('Buy not supported');
351
+
352
+ return Uniswapv2ConstantProductPool.getBuyPrice(
353
+ priceParams,
354
+ srcAmount,
355
+ this.feeFactor,
356
+ );
357
+ }
358
+
359
+ async getSellPrice(
360
+ priceParams: CamelotPoolOrderedParams,
361
+ srcAmount: bigint,
362
+ ): Promise<bigint> {
363
+ return priceParams.stable
364
+ ? SolidlyStablePool.getSellPrice(priceParams, srcAmount, this.feeFactor)
365
+ : Uniswapv2ConstantProductPool.getSellPrice(
366
+ priceParams,
367
+ srcAmount,
368
+ this.feeFactor,
369
+ );
370
+ }
371
+
372
+ async getBuyPricePath(
373
+ amount: bigint,
374
+ params: CamelotPoolOrderedParams[],
375
+ ): Promise<bigint> {
376
+ let price = amount;
377
+ for (const param of params.reverse()) {
378
+ price = await this.getBuyPrice(param, price);
379
+ }
380
+ return price;
381
+ }
382
+
383
+ async getSellPricePath(
384
+ amount: bigint,
385
+ params: CamelotPoolOrderedParams[],
386
+ ): Promise<bigint> {
387
+ let price = amount;
388
+ for (const param of params) {
389
+ price = await this.getSellPrice(param, price);
390
+ }
391
+ return price;
392
+ }
393
+
394
+ async findPair(from: Token, to: Token) {
395
+ if (from.address.toLowerCase() === to.address.toLowerCase()) return null;
396
+ const [token0, token1] =
397
+ from.address.toLowerCase() < to.address.toLowerCase()
398
+ ? [from, to]
399
+ : [to, from];
400
+
401
+ const key = this.getPoolIdentifier(token0.address, token1.address);
402
+ let pair = this.pairs[key];
403
+ if (pair) return pair;
404
+ const exchange = await this.factory.methods
405
+ .getPair(token0.address, token1.address)
406
+ .call();
407
+ if (exchange === NULL_ADDRESS) {
408
+ // if the pool has been newly created to not allow this op as we can run into race condition between pool discovery and concurrent pricing request touching this pool
409
+ if (!this.newlyCreatedPoolKeys.has(key)) {
410
+ pair = { token0, token1 };
411
+ }
412
+ } else {
413
+ pair = { token0, token1, exchange };
414
+ }
415
+ this.pairs[key] = pair;
416
+ return pair;
417
+ }
418
+
419
+ async getManyPoolStates(
420
+ pairs: CamelotPair[],
421
+ blockNumber: number,
422
+ ): Promise<CamelotPoolState[]> {
423
+ try {
424
+ const callData = pairs
425
+ .map((pair, i) => {
426
+ return this.getPoolStatesMultiCallData(pair).callEntries;
427
+ })
428
+ .flat();
429
+
430
+ const data: { returnData: any[] } =
431
+ await this.dexHelper.multiContract.methods
432
+ .aggregate(callData)
433
+ .call({}, blockNumber);
434
+
435
+ const returnData = _.chunk(data.returnData, 2);
436
+
437
+ return pairs.map((pair, i) => {
438
+ return this.getPoolStatesMultiCallData(pair).callDecoder(returnData[i]);
439
+ });
440
+ } catch (e) {
441
+ this.logger.error(
442
+ `Error_getManyPoolReserves could not get reserves with error:`,
443
+ e,
444
+ );
445
+ return [];
446
+ }
447
+ }
448
+
449
+ async batchCatchUpPairs(pairs: [Token, Token][], blockNumber: number) {
450
+ if (!blockNumber) return;
451
+ const pairsToFetch: CamelotPair[] = [];
452
+ for (const _pair of pairs) {
453
+ const pair = await this.findPair(_pair[0], _pair[1]);
454
+ if (!(pair && pair.exchange)) continue;
455
+ if (!pair.pool) {
456
+ pairsToFetch.push(pair);
457
+ } else if (!pair.pool.getState(blockNumber)) {
458
+ pairsToFetch.push(pair);
459
+ }
460
+ }
461
+
462
+ if (!pairsToFetch.length) return;
463
+
464
+ const pairsStates = await this.getManyPoolStates(pairsToFetch, blockNumber);
465
+
466
+ if (pairsStates.length !== pairsToFetch.length) {
467
+ this.logger.error(
468
+ `Error_getManyPoolReserves didn't get any pool reserves`,
469
+ );
470
+ }
471
+
472
+ for (let i = 0; i < pairsToFetch.length; i++) {
473
+ const pairState = pairsStates[i];
474
+ const pair = pairsToFetch[i];
475
+ if (!pair.pool) {
476
+ await this.addPool(
477
+ pair,
478
+ pairState.reserve0,
479
+ pairState.reserve1,
480
+ pairState.token0FeeCode,
481
+ pairState.token1FeeCode,
482
+ pairState.stable,
483
+ blockNumber,
484
+ );
485
+ } else pair.pool.setState(pairState, blockNumber);
486
+ }
487
+ }
488
+
489
+ async getPairOrderedParams(
490
+ from: Token,
491
+ to: Token,
492
+ blockNumber: number,
493
+ tokenDexTransferFee: number,
494
+ ): Promise<CamelotPoolOrderedParams | null> {
495
+ const pair = await this.findPair(from, to);
496
+ if (!(pair && pair.pool && pair.exchange)) return null;
497
+ const pairState = pair.pool.getState(blockNumber);
498
+ if (!pairState) {
499
+ this.logger.error(
500
+ `Error_orderPairParams expected reserves, got none (maybe the pool doesn't exist) ${
501
+ from.symbol || from.address
502
+ } ${to.symbol || to.address}`,
503
+ );
504
+ return null;
505
+ }
506
+
507
+ const pairReversed =
508
+ pair.token1.address.toLowerCase() === from.address.toLowerCase();
509
+ if (pairReversed) {
510
+ return {
511
+ tokenIn: from.address,
512
+ tokenOut: to.address,
513
+ reservesIn: pairState.reserve1,
514
+ reservesOut: pairState.reserve0,
515
+ decimalsIn: from.decimals,
516
+ decimalsOut: to.decimals,
517
+ fee: (pairState.token1FeeCode + tokenDexTransferFee).toString(),
518
+ direction: false,
519
+ exchange: pair.exchange,
520
+ stable: pairState.stable,
521
+ };
522
+ }
523
+ return {
524
+ tokenIn: from.address,
525
+ tokenOut: to.address,
526
+ decimalsIn: from.decimals,
527
+ decimalsOut: to.decimals,
528
+ reservesIn: pairState.reserve0,
529
+ reservesOut: pairState.reserve1,
530
+ fee: (pairState.token0FeeCode + tokenDexTransferFee).toString(),
531
+ direction: true,
532
+ exchange: pair.exchange,
533
+ stable: pairState.stable,
534
+ };
535
+ }
536
+
537
+ async getPoolIdentifiers(
538
+ _from: Token,
539
+ _to: Token,
540
+ side: SwapSide,
541
+ blockNumber: number,
542
+ ): Promise<string[]> {
543
+ if (side === SwapSide.BUY) return [];
544
+
545
+ const from = this.dexHelper.config.wrapETH(_from);
546
+ const to = this.dexHelper.config.wrapETH(_to);
547
+
548
+ if (from.address.toLowerCase() === to.address.toLowerCase()) {
549
+ return [];
550
+ }
551
+
552
+ const poolKey = this.getPoolIdentifier(from.address, to.address);
553
+
554
+ return [poolKey];
555
+ }
556
+
557
+ async getPricesVolume(
558
+ _from: Token,
559
+ _to: Token,
560
+ amounts: bigint[],
561
+ side: SwapSide,
562
+ blockNumber: number,
563
+ // list of pool identifiers to use for pricing, if undefined use all pools
564
+ limitPools?: string[],
565
+ transferFees: TransferFeeParams = {
566
+ srcFee: 0,
567
+ destFee: 0,
568
+ srcDexFee: 0,
569
+ destDexFee: 0,
570
+ },
571
+ ): Promise<ExchangePrices<SolidlyData> | null> {
572
+ try {
573
+ const from = this.dexHelper.config.wrapETH(_from);
574
+ const to = this.dexHelper.config.wrapETH(_to);
575
+
576
+ if (from.address.toLowerCase() === to.address.toLowerCase()) {
577
+ return null;
578
+ }
579
+
580
+ const poolIdentifier = this.getPoolIdentifier(from.address, to.address);
581
+
582
+ if (limitPools && limitPools.every(p => p !== poolIdentifier))
583
+ return null;
584
+
585
+ await this.batchCatchUpPairs([[from, to]], blockNumber);
586
+ const isSell = side === SwapSide.SELL;
587
+ const pairParam = await this.getPairOrderedParams(
588
+ from,
589
+ to,
590
+ blockNumber,
591
+ transferFees.srcDexFee,
592
+ );
593
+
594
+ if (!pairParam || (side === SwapSide.BUY && pairParam.stable))
595
+ return null;
596
+
597
+ const unitAmount = getBigIntPow(isSell ? from.decimals : to.decimals);
598
+
599
+ const [unitVolumeWithFee, ...amountsWithFee] = applyTransferFee(
600
+ [unitAmount, ...amounts],
601
+ side,
602
+ isSell ? transferFees.srcFee : transferFees.destFee,
603
+ isSell ? SRC_TOKEN_PARASWAP_TRANSFERS : DEST_TOKEN_PARASWAP_TRANSFERS,
604
+ );
605
+
606
+ const unit = isSell
607
+ ? await this.getSellPricePath(unitVolumeWithFee, [pairParam])
608
+ : await this.getBuyPricePath(unitVolumeWithFee, [pairParam]);
609
+
610
+ const prices = isSell
611
+ ? await Promise.all(
612
+ amountsWithFee.map(amount =>
613
+ this.getSellPricePath(amount, [pairParam]),
614
+ ),
615
+ )
616
+ : await Promise.all(
617
+ amountsWithFee.map(amount =>
618
+ this.getBuyPricePath(amount, [pairParam]),
619
+ ),
620
+ );
621
+
622
+ const [unitOutWithFee, ...outputsWithFee] = applyTransferFee(
623
+ [unit, ...prices],
624
+ side,
625
+ // This part is confusing, because we treat differently SELL and BUY fees
626
+ // If Buy, we should apply transfer fee on srcToken on top of dexFee applied earlier
627
+ // But for Sell we should apply only one dexFee
628
+ isSell ? transferFees.destDexFee : transferFees.srcFee,
629
+ isSell ? this.DEST_TOKEN_DEX_TRANSFERS : SRC_TOKEN_PARASWAP_TRANSFERS,
630
+ );
631
+
632
+ // As camelot just has one pool per token pair
633
+ return [
634
+ {
635
+ prices: outputsWithFee,
636
+ unit: unitOutWithFee,
637
+ data: {
638
+ router:
639
+ side === SwapSide.SELL
640
+ ? this.routerAddress
641
+ : this.dexHelper.config.data.uniswapV2ExchangeRouterAddress,
642
+ path: [from.address.toLowerCase(), to.address.toLowerCase()],
643
+ factory: this.factoryAddress,
644
+ initCode: this.initCode,
645
+ feeFactor: this.feeFactor,
646
+ isFeeTokenInRoute: Object.values(transferFees).some(f => f !== 0),
647
+ pools: [
648
+ {
649
+ address: pairParam.exchange,
650
+ fee: parseInt(pairParam.fee),
651
+ direction: pairParam.direction,
652
+ },
653
+ ],
654
+ },
655
+ exchange: this.dexKey,
656
+ poolIdentifier,
657
+ gasCost: this.poolGasCost,
658
+ poolAddresses: [pairParam.exchange],
659
+ },
660
+ ];
661
+ } catch (e) {
662
+ if (blockNumber === 0)
663
+ this.logger.error(
664
+ `Error_getPricesVolume: Aurelius block manager not yet instantiated`,
665
+ );
666
+ this.logger.error(`Error_getPrices:`, e);
667
+ return null;
668
+ }
669
+ }
670
+
671
+ // Returns estimated gas cost of calldata for this DEX in multiSwap
672
+ getCalldataGasCost(poolPrices: PoolPrices<UniswapV2Data>): number | number[] {
673
+ return (
674
+ CALLDATA_GAS_COST.DEX_OVERHEAD +
675
+ CALLDATA_GAS_COST.LENGTH_SMALL +
676
+ // ParentStruct header
677
+ CALLDATA_GAS_COST.OFFSET_SMALL +
678
+ // ParentStruct -> weth
679
+ CALLDATA_GAS_COST.ADDRESS +
680
+ // ParentStruct -> pools[] header
681
+ CALLDATA_GAS_COST.OFFSET_SMALL +
682
+ // ParentStruct -> pools[]
683
+ CALLDATA_GAS_COST.LENGTH_SMALL +
684
+ // ParentStruct -> pools[0]
685
+ CALLDATA_GAS_COST.wordNonZeroBytes(22)
686
+ );
687
+ }
688
+
689
+ getAdapters(side: SwapSide): { name: string; index: number }[] | null {
690
+ return this.adapters[side];
691
+ }
692
+
693
+ async getTopPoolsForToken(
694
+ tokenAddress: Address,
695
+ count: number,
696
+ ): Promise<PoolLiquidity[]> {
697
+ if (!this.subgraphURL) return [];
698
+
699
+ const query = `query ($token: Bytes!, $count: Int) {
700
+ pools0: pairs(first: $count, orderBy: reserveUSD, orderDirection: desc, where: {token0: $token, reserve0_gt: 1, reserve1_gt: 1}) {
701
+ id
702
+ isStable
703
+ token0 {
704
+ id
705
+ decimals
706
+ }
707
+ token1 {
708
+ id
709
+ decimals
710
+ }
711
+ reserveUSD
712
+ }
713
+ pools1: pairs(first: $count, orderBy: reserveUSD, orderDirection: desc, where: {token1: $token, reserve0_gt: 1, reserve1_gt: 1}) {
714
+ id
715
+ isStable
716
+ token0 {
717
+ id
718
+ decimals
719
+ }
720
+ token1 {
721
+ id
722
+ decimals
723
+ }
724
+ reserveUSD
725
+ }
726
+ }`;
727
+
728
+ const { data } = await this.dexHelper.httpRequest.post(
729
+ this.subgraphURL,
730
+ {
731
+ query,
732
+ variables: { token: tokenAddress.toLowerCase(), count },
733
+ },
734
+ SUBGRAPH_TIMEOUT,
735
+ );
736
+
737
+ if (!(data && data.pools0 && data.pools1))
738
+ throw new Error("Couldn't fetch the pools from the subgraph");
739
+ const pools0 = _.map(data.pools0, pool => ({
740
+ exchange: this.dexKey,
741
+ stable: pool.isStable,
742
+ address: pool.id.toLowerCase(),
743
+ connectorTokens: [
744
+ {
745
+ address: pool.token1.id.toLowerCase(),
746
+ decimals: parseInt(pool.token1.decimals),
747
+ },
748
+ ],
749
+ liquidityUSD: parseFloat(pool.reserveUSD),
750
+ }));
751
+
752
+ const pools1 = _.map(data.pools1, pool => ({
753
+ exchange: this.dexKey,
754
+ stable: pool.isStable,
755
+ address: pool.id.toLowerCase(),
756
+ connectorTokens: [
757
+ {
758
+ address: pool.token0.id.toLowerCase(),
759
+ decimals: parseInt(pool.token0.decimals),
760
+ },
761
+ ],
762
+ liquidityUSD: parseFloat(pool.reserveUSD),
763
+ }));
764
+
765
+ return _.slice(
766
+ _.sortBy(_.concat(pools0, pools1), [pool => -1 * pool.liquidityUSD]),
767
+ 0,
768
+ count,
769
+ );
770
+ }
771
+
772
+ getWETHAddress(srcToken: Address, destToken: Address, weth?: Address) {
773
+ if (!isETHAddress(srcToken) && !isETHAddress(destToken))
774
+ return NULL_ADDRESS;
775
+ return weth || this.dexHelper.config.data.wrappedNativeTokenAddress;
776
+ }
777
+
778
+ getAdapterParam(
779
+ srcToken: Address,
780
+ destToken: Address,
781
+ srcAmount: NumberAsString,
782
+ toAmount: NumberAsString, // required for buy case
783
+ data: SolidlyData,
784
+ side: SwapSide,
785
+ ): AdapterExchangeParam {
786
+ if (side === SwapSide.BUY) throw new Error('Buy not supported');
787
+
788
+ const pools = encodePools(data.pools, this.feeFactor);
789
+ const weth = this.getWETHAddress(srcToken, destToken, data.wethAddress);
790
+ const payload = this.abiCoder.encodeParameter(
791
+ {
792
+ ParentStruct: {
793
+ weth: 'address',
794
+ pools: 'uint256[]',
795
+ isFeeTokenInRoute: 'bool',
796
+ },
797
+ },
798
+ { pools, weth, isFeeTokenInRoute: data.isFeeTokenInRoute },
799
+ );
800
+ return {
801
+ targetExchange: data.router,
802
+ payload,
803
+ networkFee: '0',
804
+ };
805
+ }
806
+
807
+ async getSimpleParam(
808
+ src: Address,
809
+ dest: Address,
810
+ srcAmount: NumberAsString,
811
+ destAmount: NumberAsString,
812
+ data: SolidlyData,
813
+ side: SwapSide,
814
+ ): Promise<SimpleExchangeParam> {
815
+ if (side === SwapSide.BUY) throw new Error('Buy not supported');
816
+
817
+ const pools = encodePools(data.pools, this.feeFactor);
818
+ const weth = this.getWETHAddress(src, dest, data.wethAddress);
819
+ const swapData = this.exchangeRouterInterface.encodeFunctionData(
820
+ side === SwapSide.SELL ? UniswapV2Functions.swap : UniswapV2Functions.buy,
821
+ [src, srcAmount, destAmount, weth, pools],
822
+ );
823
+
824
+ return this.buildSimpleParamWithoutWETHConversion(
825
+ src,
826
+ srcAmount,
827
+ dest,
828
+ destAmount,
829
+ swapData,
830
+ data.router,
831
+ );
832
+ }
833
+
834
+ getDexParam(
835
+ srcToken: Address,
836
+ destToken: Address,
837
+ srcAmount: NumberAsString,
838
+ destAmount: NumberAsString,
839
+ recipient: Address,
840
+ data: SolidlyData,
841
+ side: SwapSide,
842
+ ): DexExchangeParam {
843
+ if (side === SwapSide.BUY) throw new Error('Buy not supported');
844
+ let exchangeDataTypes = ['bytes4', 'bytes32'];
845
+
846
+ let exchangeDataToPack = [
847
+ hexZeroPad(hexlify(0), 4),
848
+ hexZeroPad(hexlify(data.pools.length), 32),
849
+ ];
850
+
851
+ const pools = encodePools(data.pools, this.feeFactor);
852
+ pools.forEach(pool => {
853
+ exchangeDataTypes.push('bytes32');
854
+ exchangeDataToPack.push(hexZeroPad(hexlify(BigNumber.from(pool)), 32));
855
+ });
856
+
857
+ const exchangeData = solidityPack(exchangeDataTypes, exchangeDataToPack);
858
+
859
+ return {
860
+ needWrapNative: this.needWrapNative,
861
+ dexFuncHasRecipient: true,
862
+ exchangeData,
863
+ targetExchange: recipient,
864
+ specialDexFlag: data.isFeeTokenInRoute
865
+ ? SpecialDex.SWAP_ON_DYSTOPIA_UNISWAP_V2_FORK_WITH_FEE
866
+ : SpecialDex.SWAP_ON_DYSTOPIA_UNISWAP_V2_FORK,
867
+ transferSrcTokenBeforeSwap: data.isFeeTokenInRoute
868
+ ? undefined
869
+ : data.pools[0].address,
870
+ returnAmountPos: undefined,
871
+ };
872
+ }
873
+ }