@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,631 +1,631 @@
1
- import { ethers } from 'ethers';
2
- import { Interface } from '@ethersproject/abi';
3
- import {
4
- Token,
5
- Address,
6
- ExchangePrices,
7
- PoolPrices,
8
- AdapterExchangeParam,
9
- SimpleExchangeParam,
10
- PoolLiquidity,
11
- Logger,
12
- BigIntAsString,
13
- OptimalSwapExchange,
14
- ExchangeTxInfo,
15
- PreprocessTransactionOptions,
16
- NumberAsString,
17
- DexExchangeParam,
18
- } from '../../types';
19
- import { SwapSide, Network, LIMIT_ORDER_PROVIDERS } from '../../constants';
20
- import * as CALLDATA_GAS_COST from '../../calldata-gas-cost';
21
- import { getBigIntPow, getDexKeysWithNetwork } from '../../utils';
22
- import { IDex } from '../../dex/idex';
23
- import { IDexHelper } from '../../dex-helper/idex-helper';
24
- import {
25
- ParaSwapLimitOrdersData,
26
- ParaSwapOrderResponse,
27
- ParaSwapOrderBookResponse,
28
- OrderInfo,
29
- ParaSwapOrderBook,
30
- } from './types';
31
- import { Adapters, ParaSwapLimitOrdersConfig } from './config';
32
- import { LimitOrderExchange } from '../limit-order-exchange';
33
- import { BI_MAX_UINT256 } from '../../bigint-constants';
34
- import augustusRFQABI from '../../abi/paraswap-limit-orders/AugustusRFQ.abi.json';
35
- import {
36
- MAX_ORDERS_MULTI_FACTOR,
37
- MAX_ORDERS_USED_FOR_SWAP,
38
- ONE_ORDER_GASCOST,
39
- } from './constant';
40
- import BigNumber from 'bignumber.js';
41
-
42
- const BLACKLIST_CACHE_PREFIX = `lo_blacklist`;
43
-
44
- export class ParaSwapLimitOrders
45
- extends LimitOrderExchange<ParaSwapOrderResponse, ParaSwapOrderBookResponse>
46
- implements IDex<ParaSwapLimitOrdersData>
47
- {
48
- readonly hasConstantPriceLargeAmounts = false;
49
- readonly needWrapNative = true;
50
- readonly isFeeOnTransferSupported = false;
51
-
52
- public static dexKeysWithNetwork: { key: string; networks: Network[] }[] =
53
- getDexKeysWithNetwork(ParaSwapLimitOrdersConfig);
54
-
55
- logger: Logger;
56
-
57
- protected augustusRFQAddress: Address;
58
-
59
- constructor(
60
- protected network: Network,
61
- dexKey: string,
62
- protected dexHelper: IDexHelper,
63
- protected adapters = Adapters[network] ? Adapters[network] : {},
64
- protected rfqIface = new Interface(augustusRFQABI),
65
- ) {
66
- super(dexHelper, dexKey);
67
- this.augustusRFQAddress =
68
- dexHelper.config.data.augustusRFQAddress.toLowerCase();
69
- this.logger = dexHelper.getLogger(dexKey);
70
- }
71
-
72
- get limitOrderProviderName() {
73
- return LIMIT_ORDER_PROVIDERS.PARASWAP;
74
- }
75
-
76
- getAdapters(side: SwapSide): { name: string; index: number }[] | null {
77
- return this.adapters[side] ? this.adapters[side] : null;
78
- }
79
-
80
- getIdentifier(srcToken: Address, destToken: Address) {
81
- // Expected lowered Addresses
82
- return `${this.dexKey.toLowerCase()}_${srcToken}_${destToken}`;
83
- }
84
-
85
- async isBlacklisted(userAddress: string): Promise<boolean> {
86
- const value = await this.dexHelper.cache.rawget(
87
- `${BLACKLIST_CACHE_PREFIX}_${userAddress}`.toLowerCase(),
88
- );
89
- if (value) {
90
- return true;
91
- }
92
-
93
- return false;
94
- }
95
-
96
- async getPoolIdentifiers(
97
- srcToken: Token,
98
- destToken: Token,
99
- side: SwapSide,
100
- blockNumber: number,
101
- ): Promise<string[]> {
102
- const _srcToken = this.dexHelper.config.wrapETH(srcToken);
103
- const _destToken = this.dexHelper.config.wrapETH(destToken);
104
-
105
- const _srcAddress = _srcToken.address.toLowerCase();
106
- const _destAddress = _destToken.address.toLowerCase();
107
-
108
- if (_srcAddress === _destAddress) {
109
- return [];
110
- }
111
-
112
- const orderBook = await this._getLatestOrderBook(_srcAddress, _destAddress);
113
-
114
- if (orderBook === null) return [];
115
-
116
- return [this.getIdentifier(_srcAddress, _destAddress)];
117
- }
118
-
119
- async getPricesVolume(
120
- srcToken: Token,
121
- destToken: Token,
122
- amounts: bigint[],
123
- side: SwapSide,
124
- blockNumber: number,
125
- limitPools?: string[],
126
- ): Promise<null | ExchangePrices<ParaSwapLimitOrdersData>> {
127
- try {
128
- const _srcToken = this.dexHelper.config.wrapETH(srcToken);
129
- const _destToken = this.dexHelper.config.wrapETH(destToken);
130
-
131
- const _srcAddress = _srcToken.address.toLowerCase();
132
- const _destAddress = _destToken.address.toLowerCase();
133
-
134
- if (_srcAddress === _destAddress) return null;
135
-
136
- const expectedIdentifier = this.getIdentifier(_srcAddress, _destAddress);
137
-
138
- if (
139
- limitPools !== undefined &&
140
- !limitPools.some(p => p === expectedIdentifier)
141
- )
142
- return null;
143
-
144
- const isSell = side === SwapSide.SELL;
145
- const unitVolume = getBigIntPow(
146
- isSell ? _srcToken.decimals : _destToken.decimals,
147
- );
148
-
149
- let orderBook = await this._getLatestOrderBook(_srcAddress, _destAddress);
150
-
151
- if (orderBook === null) return null;
152
-
153
- // Unit is volume is not increasing, so better to request separate
154
- let {
155
- prices: [unit],
156
- } = this._getPrices([unitVolume], orderBook, isSell);
157
-
158
- const { prices, gasCosts, maxOrdersCount } = this._getPrices(
159
- amounts,
160
- orderBook,
161
- isSell,
162
- );
163
-
164
- if (unit === 0n) {
165
- // If we didn't fulfill unit amount, scale up latest amount till unit
166
- unit = (unitVolume * prices.slice(-1)[0]) / amounts.slice(-1)[0];
167
- }
168
-
169
- return [
170
- {
171
- unit,
172
- prices,
173
- data: {
174
- orderInfos: null,
175
- maxOrdersCount,
176
- },
177
- poolIdentifier: expectedIdentifier,
178
- exchange: this.dexKey,
179
- gasCost: gasCosts.map(v => Number(v)),
180
- poolAddresses: [this.augustusRFQAddress],
181
- },
182
- ];
183
- } catch (e) {
184
- this.logger.error(
185
- `Error_getPricesVolume ${this.dexKey}: ${
186
- srcToken.symbol || srcToken.address
187
- }, ${destToken.symbol || destToken.address}, ${side}:`,
188
- e,
189
- );
190
- return null;
191
- }
192
- }
193
-
194
- // Returns estimated gas cost of calldata for this DEX in multiSwap
195
- getCalldataGasCost(
196
- poolPrices: PoolPrices<ParaSwapLimitOrdersData>,
197
- ): number | number[] {
198
- const calculateCalldataGasCost = (g: number) => {
199
- if (!g) return 0;
200
- const numOrders = Number(BigInt(g) / ONE_ORDER_GASCOST);
201
- return (
202
- CALLDATA_GAS_COST.DEX_NO_PAYLOAD +
203
- CALLDATA_GAS_COST.LENGTH_LARGE +
204
- // Struct header
205
- CALLDATA_GAS_COST.OFFSET_SMALL +
206
- // Struct -> orderInfos[] header
207
- CALLDATA_GAS_COST.OFFSET_SMALL +
208
- // Struct -> orderInfos[]
209
- CALLDATA_GAS_COST.LENGTH_SMALL +
210
- // Struct -> orderInfos[0:numOrders] headers
211
- CALLDATA_GAS_COST.OFFSET_SMALL +
212
- CALLDATA_GAS_COST.OFFSET_LARGE * (numOrders - 1) +
213
- // Struct -> orderInfos[0:numOrders]
214
- numOrders *
215
- // Struct -> orderInfos[i] -> order
216
- (CALLDATA_GAS_COST.FULL_WORD +
217
- CALLDATA_GAS_COST.TIMESTAMP +
218
- CALLDATA_GAS_COST.ADDRESS +
219
- CALLDATA_GAS_COST.ADDRESS +
220
- CALLDATA_GAS_COST.ADDRESS +
221
- CALLDATA_GAS_COST.ADDRESS +
222
- CALLDATA_GAS_COST.AMOUNT +
223
- CALLDATA_GAS_COST.AMOUNT +
224
- // Struct -> orderInfos[i] -> signature header
225
- CALLDATA_GAS_COST.OFFSET_LARGE +
226
- // Struct -> orderInfos[i] -> takerTokenFillAmount
227
- CALLDATA_GAS_COST.AMOUNT +
228
- // Struct -> orderInfos[i] -> permitTakerAsset header
229
- CALLDATA_GAS_COST.OFFSET_LARGE +
230
- // Struct -> orderInfos[i] -> permitMakerAsset header
231
- CALLDATA_GAS_COST.OFFSET_LARGE +
232
- // Struct -> orderInfos[i] -> signature
233
- CALLDATA_GAS_COST.LENGTH_SMALL +
234
- CALLDATA_GAS_COST.FULL_WORD +
235
- CALLDATA_GAS_COST.FULL_WORD +
236
- CALLDATA_GAS_COST.wordNonZeroBytes(1) +
237
- // Struct -> orderInfos[i] -> permitTakerAsset
238
- CALLDATA_GAS_COST.ZERO +
239
- // Struct -> orderInfos[i] -> permitMakerAsset
240
- CALLDATA_GAS_COST.ZERO)
241
- );
242
- };
243
- return typeof poolPrices.gasCost === 'number'
244
- ? calculateCalldataGasCost(poolPrices.gasCost)
245
- : poolPrices.gasCost.map(calculateCalldataGasCost);
246
- }
247
-
248
- async preProcessTransaction?(
249
- optimalSwapExchange: OptimalSwapExchange<ParaSwapLimitOrdersData>,
250
- srcToken: Token,
251
- destToken: Token,
252
- side: SwapSide,
253
- options: PreprocessTransactionOptions,
254
- ): Promise<[OptimalSwapExchange<ParaSwapLimitOrdersData>, ExchangeTxInfo]> {
255
- const userAddress = options.txOrigin;
256
-
257
- const srcWrapped = this.dexHelper.config
258
- .wrapETH(srcToken)
259
- .address.toLowerCase();
260
- const destWrapped = this.dexHelper.config
261
- .wrapETH(destToken)
262
- .address.toLowerCase();
263
-
264
- const isSell = side === SwapSide.SELL;
265
- const amountWithSlippage = isSell
266
- ? BigInt(
267
- new BigNumber(optimalSwapExchange.destAmount.toString())
268
- .times(options.slippageFactor)
269
- .toFixed(0),
270
- )
271
- : BigInt(
272
- options.slippageFactor
273
- .times(optimalSwapExchange.srcAmount.toString())
274
- .toFixed(0),
275
- );
276
-
277
- const { encodingValues, minDeadline } =
278
- await this._prepareOrdersForTransaction(
279
- srcWrapped,
280
- destWrapped,
281
- isSell ? optimalSwapExchange.srcAmount : amountWithSlippage.toString(),
282
- isSell ? amountWithSlippage.toString() : optimalSwapExchange.destAmount,
283
- side,
284
- userAddress,
285
- );
286
-
287
- return [
288
- {
289
- ...optimalSwapExchange,
290
- data: { orderInfos: encodingValues },
291
- },
292
- { deadline: minDeadline },
293
- ];
294
- }
295
-
296
- getTokenFromAddress?(address: Address): Token {
297
- // We don't have predefined set of tokens with decimals
298
- // Anyway we don't use decimals, so it is fine to do this
299
- return { address, decimals: 0 };
300
- }
301
-
302
- getAdapterParam(
303
- srcToken: string,
304
- destToken: string,
305
- srcAmount: string,
306
- destAmount: string,
307
- data: ParaSwapLimitOrdersData,
308
- side: SwapSide,
309
- ): AdapterExchangeParam {
310
- const { orderInfos } = data;
311
- if (orderInfos === null) {
312
- throw new Error(
313
- `Error_${this.dexKey}_getAdapterParam payload is not received. It may be because of` +
314
- `not calling preProcessTransaction before`,
315
- );
316
- }
317
-
318
- const isSell = side === SwapSide.SELL;
319
- const orderInfoParamType = this.rfqIface.getFunction(
320
- isSell ? 'tryBatchFillOrderTakerAmount' : 'tryBatchFillOrderMakerAmount',
321
- ).inputs[0];
322
-
323
- const orderInfoTypes = orderInfoParamType.format(
324
- ethers.utils.FormatTypes.full,
325
- );
326
-
327
- const payload = this.rfqIface._abiCoder.encode(
328
- [`tuple(${orderInfoTypes})`],
329
- [{ orderInfos }],
330
- );
331
-
332
- return {
333
- targetExchange: this.augustusRFQAddress,
334
- payload,
335
- networkFee: '0',
336
- };
337
- }
338
-
339
- async getSimpleParam(
340
- srcToken: string,
341
- destToken: string,
342
- srcAmount: string,
343
- destAmount: string,
344
- data: ParaSwapLimitOrdersData,
345
- side: SwapSide,
346
- ): Promise<SimpleExchangeParam> {
347
- const { orderInfos } = data;
348
-
349
- if (orderInfos === null) {
350
- throw new Error(
351
- `Error_${this.dexKey}_getAdapterParam payload is not received. It may be because of` +
352
- `not calling preProcessTransaction before`,
353
- );
354
- }
355
-
356
- const isSell = side === SwapSide.SELL;
357
- const swapData = this.rfqIface.encodeFunctionData(
358
- isSell ? 'tryBatchFillOrderTakerAmount' : 'tryBatchFillOrderMakerAmount',
359
- [orderInfos, isSell ? srcAmount : destAmount, this.augustusAddress],
360
- );
361
-
362
- return this.buildSimpleParamWithoutWETHConversion(
363
- srcToken,
364
- srcAmount,
365
- destToken,
366
- destAmount,
367
- swapData,
368
- this.augustusRFQAddress,
369
- );
370
- }
371
-
372
- getDexParam(
373
- srcToken: Address,
374
- destToken: Address,
375
- srcAmount: NumberAsString,
376
- destAmount: NumberAsString,
377
- recipient: Address,
378
- data: ParaSwapLimitOrdersData,
379
- side: SwapSide,
380
- ): DexExchangeParam {
381
- const { orderInfos } = data;
382
-
383
- if (orderInfos === null) {
384
- throw new Error(
385
- `Error_${this.dexKey}_getAdapterParam payload is not received. It may be because of` +
386
- `not calling preProcessTransaction before`,
387
- );
388
- }
389
-
390
- const isSell = side === SwapSide.SELL;
391
- const swapData = this.rfqIface.encodeFunctionData(
392
- isSell ? 'tryBatchFillOrderTakerAmount' : 'tryBatchFillOrderMakerAmount',
393
- [orderInfos, isSell ? srcAmount : destAmount, recipient],
394
- );
395
-
396
- return {
397
- needWrapNative: this.needWrapNative,
398
- dexFuncHasRecipient: true,
399
- exchangeData: swapData,
400
- targetExchange: this.augustusRFQAddress,
401
- returnAmountPos: undefined,
402
- };
403
- }
404
-
405
- async getTopPoolsForToken(
406
- tokenAddress: Address,
407
- limit: number,
408
- ): Promise<PoolLiquidity[]> {
409
- return [];
410
- }
411
-
412
- private async _getLatestOrderBook(
413
- src: Address,
414
- dest: Address,
415
- ): Promise<ParaSwapOrderBook[] | null> {
416
- const orderBookUnparsed = await this._limitOrderProvider!.fetchOrderBook(
417
- this.network,
418
- src,
419
- dest,
420
- );
421
-
422
- if (orderBookUnparsed === null || orderBookUnparsed.length === 0) {
423
- this.logger.trace(
424
- `${this.dexKey}: No orderBook found for ${src} and ${dest} on ${this.network} network`,
425
- );
426
- return null;
427
- }
428
-
429
- return orderBookUnparsed
430
- .map(orderBook => ({
431
- swappableMakerBalance: BigInt(orderBook.swappableMakerBalance),
432
- swappableTakerBalance: BigInt(orderBook.swappableTakerBalance),
433
- makerAmount: BigInt(orderBook.makerAmount),
434
- takerAmount: BigInt(orderBook.takerAmount),
435
- isFillOrKill: orderBook.isFillOrKill,
436
- }))
437
- .filter(
438
- orderBook =>
439
- orderBook.swappableMakerBalance > 0n &&
440
- orderBook.swappableTakerBalance > 0n,
441
- );
442
- }
443
-
444
- private async _prepareOrdersForTransaction(
445
- srcToken: Address,
446
- destToken: Address,
447
- srcAmount: BigIntAsString,
448
- destAmount: BigIntAsString,
449
- side: SwapSide,
450
- userAddress: Address,
451
- ): Promise<{
452
- encodingValues: OrderInfo[];
453
- minDeadline: bigint;
454
- }> {
455
- // I assume that srcToken and destToken are already wrapped
456
- // And received orders are fully match the amount we need and the price
457
- // without further checks and calculations
458
- const orderInfos = await this._limitOrderProvider!.fetchAndReserveOrders(
459
- this.network,
460
- srcToken,
461
- destToken,
462
- srcAmount,
463
- destAmount,
464
- side,
465
- userAddress,
466
- );
467
-
468
- if (orderInfos === null)
469
- throw new Error(
470
- `${
471
- this.dexKey
472
- }: No orders received from _limitOrderProvider fetchAndReserveOrders request with params: ${JSON.stringify(
473
- {
474
- network: this.network,
475
- srcToken,
476
- destToken,
477
- srcAmount,
478
- destAmount,
479
- side,
480
- userAddress,
481
- },
482
- )}`,
483
- );
484
-
485
- const encodingValues: OrderInfo[] = new Array(orderInfos.length);
486
-
487
- let minDeadline = BI_MAX_UINT256;
488
- for (const [i, orderInfo] of orderInfos.entries()) {
489
- // Find minimum deadline value
490
- const { order } = orderInfo;
491
-
492
- const orderExpiryBigInt = BigInt(order.expiry);
493
- minDeadline =
494
- orderExpiryBigInt < minDeadline ? orderExpiryBigInt : minDeadline;
495
-
496
- encodingValues[i] = {
497
- order: {
498
- nonceAndMeta: order.nonceAndMeta,
499
- expiry: order.expiry,
500
- makerAsset: order.makerAsset,
501
- takerAsset: order.takerAsset,
502
- maker: order.maker,
503
- taker: order.taker,
504
- makerAmount: order.makerAmount,
505
- takerAmount: order.takerAmount,
506
- },
507
- signature: orderInfo.signature,
508
- takerTokenFillAmount: orderInfo.takerTokenFillAmount,
509
- permitTakerAsset: orderInfo.permitTakerAsset
510
- ? orderInfo.permitTakerAsset
511
- : '0x',
512
- permitMakerAsset: orderInfo.permitMakerAsset
513
- ? orderInfo.permitMakerAsset
514
- : '0x',
515
- };
516
- }
517
- return { encodingValues, minDeadline };
518
- }
519
-
520
- private _getPrices(
521
- amounts: bigint[],
522
- orderBook: ParaSwapOrderBook[],
523
- isSell: boolean,
524
- ): { prices: bigint[]; gasCosts: bigint[]; maxOrdersCount: number } {
525
- const prices = new Array<bigint>(amounts.length).fill(0n);
526
- const gasCosts = new Array<bigint>(amounts.length).fill(0n);
527
- let maxOrdersCount = 0;
528
-
529
- const calcOutFunc = isSell
530
- ? this._calcMakerFromTakerAmount
531
- : this._calcTakerFromMakerAmount;
532
-
533
- const srcKeyAmount = isSell
534
- ? 'swappableTakerBalance'
535
- : 'swappableMakerBalance';
536
- const destKeyAmount = isSell
537
- ? 'swappableMakerBalance'
538
- : 'swappableTakerBalance';
539
-
540
- const orderThresholdDenominators = [
541
- BigInt(MAX_ORDERS_USED_FOR_SWAP) * BigInt(MAX_ORDERS_MULTI_FACTOR),
542
- BigInt(MAX_ORDERS_USED_FOR_SWAP),
543
- ];
544
-
545
- for (const orderThresholdDenominator of orderThresholdDenominators) {
546
- let latestFilteredOrderBook = orderBook;
547
- for (const [i, amount] of amounts.entries()) {
548
- if (!(prices[i] === 0n && gasCosts[i] === 0n)) {
549
- // We don't want to recalculate prices if previous iterations succeeded
550
- continue;
551
- }
552
-
553
- if (amount === 0n) {
554
- prices[i] = 0n;
555
- gasCosts[i] = 0n;
556
- continue;
557
- }
558
-
559
- const amountThreshold = amount / orderThresholdDenominator;
560
-
561
- latestFilteredOrderBook = latestFilteredOrderBook.filter(
562
- ob => ob[srcKeyAmount] >= amountThreshold,
563
- );
564
-
565
- if (latestFilteredOrderBook.length === 0) {
566
- prices[i] = 0n;
567
- gasCosts[i] = 0n;
568
- continue;
569
- }
570
-
571
- let toFill = amount;
572
- let numberOfOrders = 0n;
573
- let filled = 0n;
574
-
575
- for (const order of latestFilteredOrderBook) {
576
- if (toFill > 0n) {
577
- if (toFill > order[srcKeyAmount]) {
578
- toFill -= order[srcKeyAmount];
579
- filled += order[destKeyAmount];
580
- numberOfOrders++;
581
- } else if (order.isFillOrKill) {
582
- continue;
583
- } else {
584
- filled += calcOutFunc(
585
- toFill,
586
- order.makerAmount,
587
- order.takerAmount,
588
- );
589
- toFill = 0n;
590
- numberOfOrders++;
591
- }
592
- }
593
- if (numberOfOrders >= MAX_ORDERS_USED_FOR_SWAP) {
594
- break;
595
- }
596
- }
597
-
598
- if (numberOfOrders > MAX_ORDERS_USED_FOR_SWAP) {
599
- prices[i] = 0n;
600
- gasCosts[i] = 0n;
601
- } else if (toFill === 0n) {
602
- prices[i] = filled;
603
- gasCosts[i] = numberOfOrders * ONE_ORDER_GASCOST;
604
- maxOrdersCount = Math.max(maxOrdersCount, +numberOfOrders.toString());
605
- } else {
606
- prices[i] = 0n;
607
- gasCosts[i] = 0n;
608
- }
609
- }
610
- }
611
- return { prices, gasCosts, maxOrdersCount };
612
- }
613
-
614
- private _calcTakerFromMakerAmount(
615
- swappableMakerAmount: bigint,
616
- makerAmount: bigint,
617
- takerAmount: bigint,
618
- ): bigint {
619
- return (
620
- (swappableMakerAmount * takerAmount + (makerAmount - 1n)) / makerAmount
621
- );
622
- }
623
-
624
- private _calcMakerFromTakerAmount(
625
- swappableTakerAmount: bigint,
626
- makerAmount: bigint,
627
- takerAmount: bigint,
628
- ): bigint {
629
- return (swappableTakerAmount * makerAmount) / takerAmount;
630
- }
631
- }
1
+ import { ethers } from 'ethers';
2
+ import { Interface } from '@ethersproject/abi';
3
+ import {
4
+ Token,
5
+ Address,
6
+ ExchangePrices,
7
+ PoolPrices,
8
+ AdapterExchangeParam,
9
+ SimpleExchangeParam,
10
+ PoolLiquidity,
11
+ Logger,
12
+ BigIntAsString,
13
+ OptimalSwapExchange,
14
+ ExchangeTxInfo,
15
+ PreprocessTransactionOptions,
16
+ NumberAsString,
17
+ DexExchangeParam,
18
+ } from '../../types';
19
+ import { SwapSide, Network, LIMIT_ORDER_PROVIDERS } from '../../constants';
20
+ import * as CALLDATA_GAS_COST from '../../calldata-gas-cost';
21
+ import { getBigIntPow, getDexKeysWithNetwork } from '../../utils';
22
+ import { IDex } from '../../dex/idex';
23
+ import { IDexHelper } from '../../dex-helper/idex-helper';
24
+ import {
25
+ ParaSwapLimitOrdersData,
26
+ ParaSwapOrderResponse,
27
+ ParaSwapOrderBookResponse,
28
+ OrderInfo,
29
+ ParaSwapOrderBook,
30
+ } from './types';
31
+ import { Adapters, ParaSwapLimitOrdersConfig } from './config';
32
+ import { LimitOrderExchange } from '../limit-order-exchange';
33
+ import { BI_MAX_UINT256 } from '../../bigint-constants';
34
+ import augustusRFQABI from '../../abi/paraswap-limit-orders/AugustusRFQ.abi.json';
35
+ import {
36
+ MAX_ORDERS_MULTI_FACTOR,
37
+ MAX_ORDERS_USED_FOR_SWAP,
38
+ ONE_ORDER_GASCOST,
39
+ } from './constant';
40
+ import BigNumber from 'bignumber.js';
41
+
42
+ const BLACKLIST_CACHE_PREFIX = `lo_blacklist`;
43
+
44
+ export class ParaSwapLimitOrders
45
+ extends LimitOrderExchange<ParaSwapOrderResponse, ParaSwapOrderBookResponse>
46
+ implements IDex<ParaSwapLimitOrdersData>
47
+ {
48
+ readonly hasConstantPriceLargeAmounts = false;
49
+ readonly needWrapNative = true;
50
+ readonly isFeeOnTransferSupported = false;
51
+
52
+ public static dexKeysWithNetwork: { key: string; networks: Network[] }[] =
53
+ getDexKeysWithNetwork(ParaSwapLimitOrdersConfig);
54
+
55
+ logger: Logger;
56
+
57
+ protected augustusRFQAddress: Address;
58
+
59
+ constructor(
60
+ protected network: Network,
61
+ dexKey: string,
62
+ protected dexHelper: IDexHelper,
63
+ protected adapters = Adapters[network] ? Adapters[network] : {},
64
+ protected rfqIface = new Interface(augustusRFQABI),
65
+ ) {
66
+ super(dexHelper, dexKey);
67
+ this.augustusRFQAddress =
68
+ dexHelper.config.data.augustusRFQAddress.toLowerCase();
69
+ this.logger = dexHelper.getLogger(dexKey);
70
+ }
71
+
72
+ get limitOrderProviderName() {
73
+ return LIMIT_ORDER_PROVIDERS.PARASWAP;
74
+ }
75
+
76
+ getAdapters(side: SwapSide): { name: string; index: number }[] | null {
77
+ return this.adapters[side] ? this.adapters[side] : null;
78
+ }
79
+
80
+ getIdentifier(srcToken: Address, destToken: Address) {
81
+ // Expected lowered Addresses
82
+ return `${this.dexKey.toLowerCase()}_${srcToken}_${destToken}`;
83
+ }
84
+
85
+ async isBlacklisted(userAddress: string): Promise<boolean> {
86
+ const value = await this.dexHelper.cache.rawget(
87
+ `${BLACKLIST_CACHE_PREFIX}_${userAddress}`.toLowerCase(),
88
+ );
89
+ if (value) {
90
+ return true;
91
+ }
92
+
93
+ return false;
94
+ }
95
+
96
+ async getPoolIdentifiers(
97
+ srcToken: Token,
98
+ destToken: Token,
99
+ side: SwapSide,
100
+ blockNumber: number,
101
+ ): Promise<string[]> {
102
+ const _srcToken = this.dexHelper.config.wrapETH(srcToken);
103
+ const _destToken = this.dexHelper.config.wrapETH(destToken);
104
+
105
+ const _srcAddress = _srcToken.address.toLowerCase();
106
+ const _destAddress = _destToken.address.toLowerCase();
107
+
108
+ if (_srcAddress === _destAddress) {
109
+ return [];
110
+ }
111
+
112
+ const orderBook = await this._getLatestOrderBook(_srcAddress, _destAddress);
113
+
114
+ if (orderBook === null) return [];
115
+
116
+ return [this.getIdentifier(_srcAddress, _destAddress)];
117
+ }
118
+
119
+ async getPricesVolume(
120
+ srcToken: Token,
121
+ destToken: Token,
122
+ amounts: bigint[],
123
+ side: SwapSide,
124
+ blockNumber: number,
125
+ limitPools?: string[],
126
+ ): Promise<null | ExchangePrices<ParaSwapLimitOrdersData>> {
127
+ try {
128
+ const _srcToken = this.dexHelper.config.wrapETH(srcToken);
129
+ const _destToken = this.dexHelper.config.wrapETH(destToken);
130
+
131
+ const _srcAddress = _srcToken.address.toLowerCase();
132
+ const _destAddress = _destToken.address.toLowerCase();
133
+
134
+ if (_srcAddress === _destAddress) return null;
135
+
136
+ const expectedIdentifier = this.getIdentifier(_srcAddress, _destAddress);
137
+
138
+ if (
139
+ limitPools !== undefined &&
140
+ !limitPools.some(p => p === expectedIdentifier)
141
+ )
142
+ return null;
143
+
144
+ const isSell = side === SwapSide.SELL;
145
+ const unitVolume = getBigIntPow(
146
+ isSell ? _srcToken.decimals : _destToken.decimals,
147
+ );
148
+
149
+ let orderBook = await this._getLatestOrderBook(_srcAddress, _destAddress);
150
+
151
+ if (orderBook === null) return null;
152
+
153
+ // Unit is volume is not increasing, so better to request separate
154
+ let {
155
+ prices: [unit],
156
+ } = this._getPrices([unitVolume], orderBook, isSell);
157
+
158
+ const { prices, gasCosts, maxOrdersCount } = this._getPrices(
159
+ amounts,
160
+ orderBook,
161
+ isSell,
162
+ );
163
+
164
+ if (unit === 0n) {
165
+ // If we didn't fulfill unit amount, scale up latest amount till unit
166
+ unit = (unitVolume * prices.slice(-1)[0]) / amounts.slice(-1)[0];
167
+ }
168
+
169
+ return [
170
+ {
171
+ unit,
172
+ prices,
173
+ data: {
174
+ orderInfos: null,
175
+ maxOrdersCount,
176
+ },
177
+ poolIdentifier: expectedIdentifier,
178
+ exchange: this.dexKey,
179
+ gasCost: gasCosts.map(v => Number(v)),
180
+ poolAddresses: [this.augustusRFQAddress],
181
+ },
182
+ ];
183
+ } catch (e) {
184
+ this.logger.error(
185
+ `Error_getPricesVolume ${this.dexKey}: ${
186
+ srcToken.symbol || srcToken.address
187
+ }, ${destToken.symbol || destToken.address}, ${side}:`,
188
+ e,
189
+ );
190
+ return null;
191
+ }
192
+ }
193
+
194
+ // Returns estimated gas cost of calldata for this DEX in multiSwap
195
+ getCalldataGasCost(
196
+ poolPrices: PoolPrices<ParaSwapLimitOrdersData>,
197
+ ): number | number[] {
198
+ const calculateCalldataGasCost = (g: number) => {
199
+ if (!g) return 0;
200
+ const numOrders = Number(BigInt(g) / ONE_ORDER_GASCOST);
201
+ return (
202
+ CALLDATA_GAS_COST.DEX_NO_PAYLOAD +
203
+ CALLDATA_GAS_COST.LENGTH_LARGE +
204
+ // Struct header
205
+ CALLDATA_GAS_COST.OFFSET_SMALL +
206
+ // Struct -> orderInfos[] header
207
+ CALLDATA_GAS_COST.OFFSET_SMALL +
208
+ // Struct -> orderInfos[]
209
+ CALLDATA_GAS_COST.LENGTH_SMALL +
210
+ // Struct -> orderInfos[0:numOrders] headers
211
+ CALLDATA_GAS_COST.OFFSET_SMALL +
212
+ CALLDATA_GAS_COST.OFFSET_LARGE * (numOrders - 1) +
213
+ // Struct -> orderInfos[0:numOrders]
214
+ numOrders *
215
+ // Struct -> orderInfos[i] -> order
216
+ (CALLDATA_GAS_COST.FULL_WORD +
217
+ CALLDATA_GAS_COST.TIMESTAMP +
218
+ CALLDATA_GAS_COST.ADDRESS +
219
+ CALLDATA_GAS_COST.ADDRESS +
220
+ CALLDATA_GAS_COST.ADDRESS +
221
+ CALLDATA_GAS_COST.ADDRESS +
222
+ CALLDATA_GAS_COST.AMOUNT +
223
+ CALLDATA_GAS_COST.AMOUNT +
224
+ // Struct -> orderInfos[i] -> signature header
225
+ CALLDATA_GAS_COST.OFFSET_LARGE +
226
+ // Struct -> orderInfos[i] -> takerTokenFillAmount
227
+ CALLDATA_GAS_COST.AMOUNT +
228
+ // Struct -> orderInfos[i] -> permitTakerAsset header
229
+ CALLDATA_GAS_COST.OFFSET_LARGE +
230
+ // Struct -> orderInfos[i] -> permitMakerAsset header
231
+ CALLDATA_GAS_COST.OFFSET_LARGE +
232
+ // Struct -> orderInfos[i] -> signature
233
+ CALLDATA_GAS_COST.LENGTH_SMALL +
234
+ CALLDATA_GAS_COST.FULL_WORD +
235
+ CALLDATA_GAS_COST.FULL_WORD +
236
+ CALLDATA_GAS_COST.wordNonZeroBytes(1) +
237
+ // Struct -> orderInfos[i] -> permitTakerAsset
238
+ CALLDATA_GAS_COST.ZERO +
239
+ // Struct -> orderInfos[i] -> permitMakerAsset
240
+ CALLDATA_GAS_COST.ZERO)
241
+ );
242
+ };
243
+ return typeof poolPrices.gasCost === 'number'
244
+ ? calculateCalldataGasCost(poolPrices.gasCost)
245
+ : poolPrices.gasCost.map(calculateCalldataGasCost);
246
+ }
247
+
248
+ async preProcessTransaction?(
249
+ optimalSwapExchange: OptimalSwapExchange<ParaSwapLimitOrdersData>,
250
+ srcToken: Token,
251
+ destToken: Token,
252
+ side: SwapSide,
253
+ options: PreprocessTransactionOptions,
254
+ ): Promise<[OptimalSwapExchange<ParaSwapLimitOrdersData>, ExchangeTxInfo]> {
255
+ const userAddress = options.txOrigin;
256
+
257
+ const srcWrapped = this.dexHelper.config
258
+ .wrapETH(srcToken)
259
+ .address.toLowerCase();
260
+ const destWrapped = this.dexHelper.config
261
+ .wrapETH(destToken)
262
+ .address.toLowerCase();
263
+
264
+ const isSell = side === SwapSide.SELL;
265
+ const amountWithSlippage = isSell
266
+ ? BigInt(
267
+ new BigNumber(optimalSwapExchange.destAmount.toString())
268
+ .times(options.slippageFactor)
269
+ .toFixed(0),
270
+ )
271
+ : BigInt(
272
+ options.slippageFactor
273
+ .times(optimalSwapExchange.srcAmount.toString())
274
+ .toFixed(0),
275
+ );
276
+
277
+ const { encodingValues, minDeadline } =
278
+ await this._prepareOrdersForTransaction(
279
+ srcWrapped,
280
+ destWrapped,
281
+ isSell ? optimalSwapExchange.srcAmount : amountWithSlippage.toString(),
282
+ isSell ? amountWithSlippage.toString() : optimalSwapExchange.destAmount,
283
+ side,
284
+ userAddress,
285
+ );
286
+
287
+ return [
288
+ {
289
+ ...optimalSwapExchange,
290
+ data: { orderInfos: encodingValues },
291
+ },
292
+ { deadline: minDeadline },
293
+ ];
294
+ }
295
+
296
+ getTokenFromAddress?(address: Address): Token {
297
+ // We don't have predefined set of tokens with decimals
298
+ // Anyway we don't use decimals, so it is fine to do this
299
+ return { address, decimals: 0 };
300
+ }
301
+
302
+ getAdapterParam(
303
+ srcToken: string,
304
+ destToken: string,
305
+ srcAmount: string,
306
+ destAmount: string,
307
+ data: ParaSwapLimitOrdersData,
308
+ side: SwapSide,
309
+ ): AdapterExchangeParam {
310
+ const { orderInfos } = data;
311
+ if (orderInfos === null) {
312
+ throw new Error(
313
+ `Error_${this.dexKey}_getAdapterParam payload is not received. It may be because of` +
314
+ `not calling preProcessTransaction before`,
315
+ );
316
+ }
317
+
318
+ const isSell = side === SwapSide.SELL;
319
+ const orderInfoParamType = this.rfqIface.getFunction(
320
+ isSell ? 'tryBatchFillOrderTakerAmount' : 'tryBatchFillOrderMakerAmount',
321
+ ).inputs[0];
322
+
323
+ const orderInfoTypes = orderInfoParamType.format(
324
+ ethers.utils.FormatTypes.full,
325
+ );
326
+
327
+ const payload = this.rfqIface._abiCoder.encode(
328
+ [`tuple(${orderInfoTypes})`],
329
+ [{ orderInfos }],
330
+ );
331
+
332
+ return {
333
+ targetExchange: this.augustusRFQAddress,
334
+ payload,
335
+ networkFee: '0',
336
+ };
337
+ }
338
+
339
+ async getSimpleParam(
340
+ srcToken: string,
341
+ destToken: string,
342
+ srcAmount: string,
343
+ destAmount: string,
344
+ data: ParaSwapLimitOrdersData,
345
+ side: SwapSide,
346
+ ): Promise<SimpleExchangeParam> {
347
+ const { orderInfos } = data;
348
+
349
+ if (orderInfos === null) {
350
+ throw new Error(
351
+ `Error_${this.dexKey}_getAdapterParam payload is not received. It may be because of` +
352
+ `not calling preProcessTransaction before`,
353
+ );
354
+ }
355
+
356
+ const isSell = side === SwapSide.SELL;
357
+ const swapData = this.rfqIface.encodeFunctionData(
358
+ isSell ? 'tryBatchFillOrderTakerAmount' : 'tryBatchFillOrderMakerAmount',
359
+ [orderInfos, isSell ? srcAmount : destAmount, this.augustusAddress],
360
+ );
361
+
362
+ return this.buildSimpleParamWithoutWETHConversion(
363
+ srcToken,
364
+ srcAmount,
365
+ destToken,
366
+ destAmount,
367
+ swapData,
368
+ this.augustusRFQAddress,
369
+ );
370
+ }
371
+
372
+ getDexParam(
373
+ srcToken: Address,
374
+ destToken: Address,
375
+ srcAmount: NumberAsString,
376
+ destAmount: NumberAsString,
377
+ recipient: Address,
378
+ data: ParaSwapLimitOrdersData,
379
+ side: SwapSide,
380
+ ): DexExchangeParam {
381
+ const { orderInfos } = data;
382
+
383
+ if (orderInfos === null) {
384
+ throw new Error(
385
+ `Error_${this.dexKey}_getAdapterParam payload is not received. It may be because of` +
386
+ `not calling preProcessTransaction before`,
387
+ );
388
+ }
389
+
390
+ const isSell = side === SwapSide.SELL;
391
+ const swapData = this.rfqIface.encodeFunctionData(
392
+ isSell ? 'tryBatchFillOrderTakerAmount' : 'tryBatchFillOrderMakerAmount',
393
+ [orderInfos, isSell ? srcAmount : destAmount, recipient],
394
+ );
395
+
396
+ return {
397
+ needWrapNative: this.needWrapNative,
398
+ dexFuncHasRecipient: true,
399
+ exchangeData: swapData,
400
+ targetExchange: this.augustusRFQAddress,
401
+ returnAmountPos: undefined,
402
+ };
403
+ }
404
+
405
+ async getTopPoolsForToken(
406
+ tokenAddress: Address,
407
+ limit: number,
408
+ ): Promise<PoolLiquidity[]> {
409
+ return [];
410
+ }
411
+
412
+ private async _getLatestOrderBook(
413
+ src: Address,
414
+ dest: Address,
415
+ ): Promise<ParaSwapOrderBook[] | null> {
416
+ const orderBookUnparsed = await this._limitOrderProvider!.fetchOrderBook(
417
+ this.network,
418
+ src,
419
+ dest,
420
+ );
421
+
422
+ if (orderBookUnparsed === null || orderBookUnparsed.length === 0) {
423
+ this.logger.trace(
424
+ `${this.dexKey}: No orderBook found for ${src} and ${dest} on ${this.network} network`,
425
+ );
426
+ return null;
427
+ }
428
+
429
+ return orderBookUnparsed
430
+ .map(orderBook => ({
431
+ swappableMakerBalance: BigInt(orderBook.swappableMakerBalance),
432
+ swappableTakerBalance: BigInt(orderBook.swappableTakerBalance),
433
+ makerAmount: BigInt(orderBook.makerAmount),
434
+ takerAmount: BigInt(orderBook.takerAmount),
435
+ isFillOrKill: orderBook.isFillOrKill,
436
+ }))
437
+ .filter(
438
+ orderBook =>
439
+ orderBook.swappableMakerBalance > 0n &&
440
+ orderBook.swappableTakerBalance > 0n,
441
+ );
442
+ }
443
+
444
+ private async _prepareOrdersForTransaction(
445
+ srcToken: Address,
446
+ destToken: Address,
447
+ srcAmount: BigIntAsString,
448
+ destAmount: BigIntAsString,
449
+ side: SwapSide,
450
+ userAddress: Address,
451
+ ): Promise<{
452
+ encodingValues: OrderInfo[];
453
+ minDeadline: bigint;
454
+ }> {
455
+ // I assume that srcToken and destToken are already wrapped
456
+ // And received orders are fully match the amount we need and the price
457
+ // without further checks and calculations
458
+ const orderInfos = await this._limitOrderProvider!.fetchAndReserveOrders(
459
+ this.network,
460
+ srcToken,
461
+ destToken,
462
+ srcAmount,
463
+ destAmount,
464
+ side,
465
+ userAddress,
466
+ );
467
+
468
+ if (orderInfos === null)
469
+ throw new Error(
470
+ `${
471
+ this.dexKey
472
+ }: No orders received from _limitOrderProvider fetchAndReserveOrders request with params: ${JSON.stringify(
473
+ {
474
+ network: this.network,
475
+ srcToken,
476
+ destToken,
477
+ srcAmount,
478
+ destAmount,
479
+ side,
480
+ userAddress,
481
+ },
482
+ )}`,
483
+ );
484
+
485
+ const encodingValues: OrderInfo[] = new Array(orderInfos.length);
486
+
487
+ let minDeadline = BI_MAX_UINT256;
488
+ for (const [i, orderInfo] of orderInfos.entries()) {
489
+ // Find minimum deadline value
490
+ const { order } = orderInfo;
491
+
492
+ const orderExpiryBigInt = BigInt(order.expiry);
493
+ minDeadline =
494
+ orderExpiryBigInt < minDeadline ? orderExpiryBigInt : minDeadline;
495
+
496
+ encodingValues[i] = {
497
+ order: {
498
+ nonceAndMeta: order.nonceAndMeta,
499
+ expiry: order.expiry,
500
+ makerAsset: order.makerAsset,
501
+ takerAsset: order.takerAsset,
502
+ maker: order.maker,
503
+ taker: order.taker,
504
+ makerAmount: order.makerAmount,
505
+ takerAmount: order.takerAmount,
506
+ },
507
+ signature: orderInfo.signature,
508
+ takerTokenFillAmount: orderInfo.takerTokenFillAmount,
509
+ permitTakerAsset: orderInfo.permitTakerAsset
510
+ ? orderInfo.permitTakerAsset
511
+ : '0x',
512
+ permitMakerAsset: orderInfo.permitMakerAsset
513
+ ? orderInfo.permitMakerAsset
514
+ : '0x',
515
+ };
516
+ }
517
+ return { encodingValues, minDeadline };
518
+ }
519
+
520
+ private _getPrices(
521
+ amounts: bigint[],
522
+ orderBook: ParaSwapOrderBook[],
523
+ isSell: boolean,
524
+ ): { prices: bigint[]; gasCosts: bigint[]; maxOrdersCount: number } {
525
+ const prices = new Array<bigint>(amounts.length).fill(0n);
526
+ const gasCosts = new Array<bigint>(amounts.length).fill(0n);
527
+ let maxOrdersCount = 0;
528
+
529
+ const calcOutFunc = isSell
530
+ ? this._calcMakerFromTakerAmount
531
+ : this._calcTakerFromMakerAmount;
532
+
533
+ const srcKeyAmount = isSell
534
+ ? 'swappableTakerBalance'
535
+ : 'swappableMakerBalance';
536
+ const destKeyAmount = isSell
537
+ ? 'swappableMakerBalance'
538
+ : 'swappableTakerBalance';
539
+
540
+ const orderThresholdDenominators = [
541
+ BigInt(MAX_ORDERS_USED_FOR_SWAP) * BigInt(MAX_ORDERS_MULTI_FACTOR),
542
+ BigInt(MAX_ORDERS_USED_FOR_SWAP),
543
+ ];
544
+
545
+ for (const orderThresholdDenominator of orderThresholdDenominators) {
546
+ let latestFilteredOrderBook = orderBook;
547
+ for (const [i, amount] of amounts.entries()) {
548
+ if (!(prices[i] === 0n && gasCosts[i] === 0n)) {
549
+ // We don't want to recalculate prices if previous iterations succeeded
550
+ continue;
551
+ }
552
+
553
+ if (amount === 0n) {
554
+ prices[i] = 0n;
555
+ gasCosts[i] = 0n;
556
+ continue;
557
+ }
558
+
559
+ const amountThreshold = amount / orderThresholdDenominator;
560
+
561
+ latestFilteredOrderBook = latestFilteredOrderBook.filter(
562
+ ob => ob[srcKeyAmount] >= amountThreshold,
563
+ );
564
+
565
+ if (latestFilteredOrderBook.length === 0) {
566
+ prices[i] = 0n;
567
+ gasCosts[i] = 0n;
568
+ continue;
569
+ }
570
+
571
+ let toFill = amount;
572
+ let numberOfOrders = 0n;
573
+ let filled = 0n;
574
+
575
+ for (const order of latestFilteredOrderBook) {
576
+ if (toFill > 0n) {
577
+ if (toFill > order[srcKeyAmount]) {
578
+ toFill -= order[srcKeyAmount];
579
+ filled += order[destKeyAmount];
580
+ numberOfOrders++;
581
+ } else if (order.isFillOrKill) {
582
+ continue;
583
+ } else {
584
+ filled += calcOutFunc(
585
+ toFill,
586
+ order.makerAmount,
587
+ order.takerAmount,
588
+ );
589
+ toFill = 0n;
590
+ numberOfOrders++;
591
+ }
592
+ }
593
+ if (numberOfOrders >= MAX_ORDERS_USED_FOR_SWAP) {
594
+ break;
595
+ }
596
+ }
597
+
598
+ if (numberOfOrders > MAX_ORDERS_USED_FOR_SWAP) {
599
+ prices[i] = 0n;
600
+ gasCosts[i] = 0n;
601
+ } else if (toFill === 0n) {
602
+ prices[i] = filled;
603
+ gasCosts[i] = numberOfOrders * ONE_ORDER_GASCOST;
604
+ maxOrdersCount = Math.max(maxOrdersCount, +numberOfOrders.toString());
605
+ } else {
606
+ prices[i] = 0n;
607
+ gasCosts[i] = 0n;
608
+ }
609
+ }
610
+ }
611
+ return { prices, gasCosts, maxOrdersCount };
612
+ }
613
+
614
+ private _calcTakerFromMakerAmount(
615
+ swappableMakerAmount: bigint,
616
+ makerAmount: bigint,
617
+ takerAmount: bigint,
618
+ ): bigint {
619
+ return (
620
+ (swappableMakerAmount * takerAmount + (makerAmount - 1n)) / makerAmount
621
+ );
622
+ }
623
+
624
+ private _calcMakerFromTakerAmount(
625
+ swappableTakerAmount: bigint,
626
+ makerAmount: bigint,
627
+ takerAmount: bigint,
628
+ ): bigint {
629
+ return (swappableTakerAmount * makerAmount) / takerAmount;
630
+ }
631
+ }