@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,1392 +1,1392 @@
1
- import { ethers } from 'ethers';
2
- import {
3
- OptimalRoute,
4
- OptimalRate,
5
- OptimalSwap,
6
- OptimalSwapExchange,
7
- } from '@paraswap/core';
8
- import { DexExchangeBuildParam, DexExchangeParam } from '../types';
9
- import { Executors, Flag, SpecialDex } from './types';
10
- import { isETHAddress } from '../utils';
11
- import { DepositWithdrawReturn } from '../dex/weth/types';
12
- import {
13
- DexCallDataParams,
14
- ExecutorBytecodeBuilder,
15
- SingleSwapCallDataParams,
16
- } from './ExecutorBytecodeBuilder';
17
- import {
18
- BYTES_64_LENGTH,
19
- NOT_EXISTING_EXCHANGE_PARAM_INDEX,
20
- ETH_SRC_TOKEN_POS_FOR_MULTISWAP_METADATA,
21
- SWAP_EXCHANGE_100_PERCENTAGE,
22
- ZEROS_20_BYTES,
23
- ZEROS_28_BYTES,
24
- ZEROS_4_BYTES,
25
- DEFAULT_RETURN_AMOUNT_POS,
26
- } from './constants';
27
- import { assert } from 'ts-essentials';
28
-
29
- const {
30
- utils: { hexlify, hexDataLength, hexConcat, hexZeroPad, solidityPack },
31
- } = ethers;
32
-
33
- export type Executor02SingleSwapCallDataParams = {
34
- routeIndex: number;
35
- swapIndex: number;
36
- wrapToSwapMap: { [key: number]: boolean };
37
- wrapToSwapExchangeMap: { [key: string]: boolean };
38
- swap: OptimalSwap;
39
- };
40
-
41
- export type Executor02DexCallDataParams = {
42
- swapExchange: OptimalSwapExchange<any>;
43
- };
44
-
45
- /**
46
- * Class to build bytecode for Executor02 - simpleSwap with N DEXs (VERTICAL_BRANCH), multiSwaps (VERTICAL_BRANCH_HORIZONTAL_SEQUENCE) and megaswaps (NESTED_VERTICAL_BRANCH_HORIZONTAL_SEQUENCE)
47
- */
48
- export class Executor02BytecodeBuilder extends ExecutorBytecodeBuilder<
49
- Executor02SingleSwapCallDataParams,
50
- Executor02DexCallDataParams
51
- > {
52
- type = Executors.TWO;
53
- /**
54
- * Executor02 Flags:
55
- * switch (flag % 4):
56
- * case 0: don't instert fromAmount
57
- * case 1: sendEth equal to fromAmount
58
- * case 2: sendEth equal to fromAmount + insert fromAmount
59
- * case 3: insert fromAmount
60
-
61
- * switch (flag % 3):
62
- * case 0: don't check balance after swap
63
- * case 1: check eth balance after swap
64
- * case 2: check destToken balance after swap
65
- */
66
- protected buildSimpleSwapFlags(
67
- priceRoute: OptimalRate,
68
- exchangeParams: DexExchangeBuildParam[],
69
- routeIndex: number,
70
- swapIndex: number,
71
- swapExchangeIndex: number,
72
- exchangeParamIndex: number,
73
- maybeWethCallData?: DepositWithdrawReturn,
74
- ): { dexFlag: Flag; approveFlag: Flag } {
75
- const { srcToken, destToken } =
76
- priceRoute.bestRoute[routeIndex].swaps[swapIndex];
77
- const isEthSrc = isETHAddress(srcToken);
78
- const isEthDest = isETHAddress(destToken);
79
-
80
- const exchangeParam = exchangeParams[exchangeParamIndex];
81
- const {
82
- dexFuncHasRecipient,
83
- needWrapNative,
84
- specialDexFlag,
85
- specialDexSupportsInsertFromAmount,
86
- swappedAmountNotPresentInExchangeData,
87
- preSwapUnwrapCalldata,
88
- } = exchangeParam;
89
-
90
- const needWrap = needWrapNative && isEthSrc && maybeWethCallData?.deposit;
91
- const needUnwrap =
92
- needWrapNative && isEthDest && maybeWethCallData?.withdraw;
93
- const isSpecialDex =
94
- specialDexFlag !== undefined && specialDexFlag !== SpecialDex.DEFAULT;
95
-
96
- const forcePreventInsertFromAmount =
97
- swappedAmountNotPresentInExchangeData ||
98
- (isSpecialDex && !specialDexSupportsInsertFromAmount);
99
-
100
- let dexFlag = forcePreventInsertFromAmount
101
- ? Flag.DONT_INSERT_FROM_AMOUNT_DONT_CHECK_BALANCE_AFTER_SWAP
102
- : Flag.INSERT_FROM_AMOUNT_DONT_CHECK_BALANCE_AFTER_SWAP; // 0 or 3
103
- let approveFlag =
104
- Flag.DONT_INSERT_FROM_AMOUNT_DONT_CHECK_BALANCE_AFTER_SWAP; // 0
105
-
106
- if (isEthSrc && !needWrap) {
107
- dexFlag =
108
- Flag.SEND_ETH_EQUAL_TO_FROM_AMOUNT_CHECK_SRC_TOKEN_BALANCE_AFTER_SWAP; // 5
109
- } else if (isEthDest && !needUnwrap) {
110
- dexFlag = forcePreventInsertFromAmount
111
- ? Flag.DONT_INSERT_FROM_AMOUNT_CHECK_ETH_BALANCE_AFTER_SWAP
112
- : Flag.INSERT_FROM_AMOUNT_CHECK_ETH_BALANCE_AFTER_SWAP; // 4 or 7
113
- } else if (!dexFuncHasRecipient || (isEthDest && needUnwrap)) {
114
- dexFlag = forcePreventInsertFromAmount
115
- ? Flag.DONT_INSERT_FROM_AMOUNT_CHECK_SRC_TOKEN_BALANCE_AFTER_SWAP
116
- : Flag.INSERT_FROM_AMOUNT_CHECK_SRC_TOKEN_BALANCE_AFTER_SWAP; // 8 or 11
117
- }
118
-
119
- // Actual srcToken is eth, because we'll unwrap weth before swap.
120
- // Need to check balance, some dexes don't have 1:1 ETH -> custom_ETH rate
121
- if (preSwapUnwrapCalldata) {
122
- dexFlag =
123
- Flag.SEND_ETH_EQUAL_TO_FROM_AMOUNT_CHECK_SRC_TOKEN_BALANCE_AFTER_SWAP;
124
- }
125
-
126
- return {
127
- dexFlag,
128
- approveFlag,
129
- };
130
- }
131
-
132
- /**
133
- * Executor02 Flags:
134
- * switch (flag % 4):
135
- * case 0: don't instert fromAmount
136
- * case 1: sendEth equal to fromAmount
137
- * case 2: sendEth equal to fromAmount + insert fromAmount
138
- * case 3: insert fromAmount
139
-
140
- * switch (flag % 3):
141
- * case 0: don't check balance after swap
142
- * case 1: check eth balance after swap
143
- * case 2: check destToken balance after swap
144
- */
145
- protected buildMultiMegaSwapFlags(
146
- priceRoute: OptimalRate,
147
- exchangeParams: DexExchangeBuildParam[],
148
- routeIndex: number,
149
- swapIndex: number,
150
- swapExchangeIndex: number,
151
- exchangeParamIndex: number,
152
- maybeWethCallData?: DepositWithdrawReturn,
153
- ): { dexFlag: Flag; approveFlag: Flag } {
154
- const route = priceRoute.bestRoute[routeIndex];
155
- const swap = route.swaps[swapIndex];
156
-
157
- const exchangeParam = exchangeParams[exchangeParamIndex];
158
-
159
- const { srcToken, destToken } = swap;
160
- const applyVerticalBranching = this.doesSwapNeedToApplyVerticalBranching(
161
- priceRoute,
162
- routeIndex,
163
- swap,
164
- );
165
-
166
- const isHorizontalSequence = route.swaps.length > 1; // check if route is a multi-swap (horizontal sequence)
167
- const isFirstSwap = swapIndex === 0;
168
- const isLastSwap = !isFirstSwap && swapIndex === route.swaps.length - 1;
169
-
170
- const {
171
- dexFuncHasRecipient,
172
- needWrapNative,
173
- specialDexFlag,
174
- specialDexSupportsInsertFromAmount,
175
- swappedAmountNotPresentInExchangeData,
176
- wethAddress,
177
- sendEthButSupportsInsertFromAmount,
178
- preSwapUnwrapCalldata,
179
- } = exchangeParam;
180
-
181
- const isEthSrc = isETHAddress(srcToken);
182
- const isEthDest = isETHAddress(destToken);
183
- const isWethDest =
184
- (wethAddress && destToken.toLowerCase() === wethAddress.toLowerCase()) ||
185
- this.dexHelper.config.isWETH(destToken);
186
-
187
- const isSpecialDex =
188
- specialDexFlag !== undefined && specialDexFlag !== SpecialDex.DEFAULT;
189
-
190
- const forcePreventInsertFromAmount =
191
- swappedAmountNotPresentInExchangeData ||
192
- (isSpecialDex && !specialDexSupportsInsertFromAmount);
193
-
194
- const forceBalanceOfCheck =
195
- (isSpecialDex &&
196
- isHorizontalSequence &&
197
- !applyVerticalBranching &&
198
- !isLastSwap) ||
199
- !dexFuncHasRecipient;
200
-
201
- const needUnwrap =
202
- needWrapNative && isEthDest && maybeWethCallData?.withdraw;
203
-
204
- const needSendEth = isEthSrc && !needWrapNative;
205
- const needCheckEthBalance = isEthDest && !needWrapNative;
206
-
207
- const needCheckSrcTokenBalanceOf =
208
- (needUnwrap && !applyVerticalBranching) ||
209
- (isHorizontalSequence && !applyVerticalBranching && !isLastSwap);
210
-
211
- let dexFlag: Flag;
212
- let approveFlag =
213
- Flag.DONT_INSERT_FROM_AMOUNT_DONT_CHECK_BALANCE_AFTER_SWAP; // 0
214
-
215
- if (needSendEth) {
216
- const preventInsertForSendEth =
217
- forcePreventInsertFromAmount || !sendEthButSupportsInsertFromAmount;
218
- dexFlag = forceBalanceOfCheck
219
- ? preventInsertForSendEth
220
- ? Flag.SEND_ETH_EQUAL_TO_FROM_AMOUNT_CHECK_SRC_TOKEN_BALANCE_AFTER_SWAP // 5
221
- : Flag.SEND_ETH_EQUAL_TO_FROM_AMOUNT_PLUS_INSERT_FROM_AMOUNT_CHECK_SRC_TOKEN_BALANCE_AFTER_SWAP // 14
222
- : dexFuncHasRecipient
223
- ? preventInsertForSendEth
224
- ? Flag.SEND_ETH_EQUAL_TO_FROM_AMOUNT_DONT_CHECK_BALANCE_AFTER_SWAP // 9
225
- : Flag.SEND_ETH_EQUAL_TO_FROM_AMOUNT_PLUS_INSERT_FROM_AMOUNT_DONT_CHECK_BALANCE_AFTER_SWAP // 18
226
- : preventInsertForSendEth
227
- ? Flag.SEND_ETH_EQUAL_TO_FROM_AMOUNT_CHECK_SRC_TOKEN_BALANCE_AFTER_SWAP // 5
228
- : Flag.SEND_ETH_EQUAL_TO_FROM_AMOUNT_PLUS_INSERT_FROM_AMOUNT_DONT_CHECK_BALANCE_AFTER_SWAP; // 18
229
- } else if (needCheckEthBalance) {
230
- dexFlag =
231
- needCheckSrcTokenBalanceOf || forceBalanceOfCheck
232
- ? forcePreventInsertFromAmount && dexFuncHasRecipient
233
- ? Flag.DONT_INSERT_FROM_AMOUNT_CHECK_ETH_BALANCE_AFTER_SWAP // 4
234
- : Flag.INSERT_FROM_AMOUNT_CHECK_ETH_BALANCE_AFTER_SWAP // 7
235
- : forcePreventInsertFromAmount && dexFuncHasRecipient
236
- ? Flag.DONT_INSERT_FROM_AMOUNT_DONT_CHECK_BALANCE_AFTER_SWAP // 0
237
- : Flag.INSERT_FROM_AMOUNT_DONT_CHECK_BALANCE_AFTER_SWAP; // 3
238
- } else {
239
- dexFlag =
240
- needCheckSrcTokenBalanceOf || forceBalanceOfCheck
241
- ? forcePreventInsertFromAmount
242
- ? Flag.DONT_INSERT_FROM_AMOUNT_CHECK_SRC_TOKEN_BALANCE_AFTER_SWAP // 8
243
- : Flag.INSERT_FROM_AMOUNT_CHECK_SRC_TOKEN_BALANCE_AFTER_SWAP // 11
244
- : forcePreventInsertFromAmount
245
- ? Flag.DONT_INSERT_FROM_AMOUNT_DONT_CHECK_BALANCE_AFTER_SWAP // 0
246
- : Flag.INSERT_FROM_AMOUNT_DONT_CHECK_BALANCE_AFTER_SWAP; // 3
247
- }
248
-
249
- // Actual srcToken is eth, because we'll unwrap weth before swap.
250
- // Need to check balance, some dexes don't have 1:1 ETH -> custom_ETH rate
251
- if (preSwapUnwrapCalldata) {
252
- dexFlag =
253
- Flag.SEND_ETH_EQUAL_TO_FROM_AMOUNT_CHECK_SRC_TOKEN_BALANCE_AFTER_SWAP;
254
- }
255
-
256
- return {
257
- dexFlag,
258
- approveFlag,
259
- };
260
- }
261
-
262
- protected buildDexCallData(
263
- params: DexCallDataParams<Executor02DexCallDataParams>,
264
- ): string {
265
- const {
266
- priceRoute,
267
- exchangeParamIndex,
268
- swapExchange,
269
- exchangeParams,
270
- routeIndex,
271
- swapIndex,
272
- flag,
273
- } = params;
274
-
275
- const swap = priceRoute.bestRoute[routeIndex].swaps[swapIndex];
276
- const exchangeParam = exchangeParams[exchangeParamIndex];
277
- let { exchangeData, specialDexFlag, targetExchange, needWrapNative } =
278
- exchangeParam;
279
-
280
- const routeNeedsRootUnwrapEth = this.doesRouteNeedsRootUnwrapEth(
281
- priceRoute,
282
- exchangeParams,
283
- );
284
-
285
- const returnAmountPos =
286
- exchangeParam.returnAmountPos !== undefined && !routeNeedsRootUnwrapEth // prevent returnAmoutPos optimisation if route needs root unwrap eth
287
- ? exchangeParam.returnAmountPos
288
- : DEFAULT_RETURN_AMOUNT_POS;
289
-
290
- const applyVerticalBranching = this.doesSwapNeedToApplyVerticalBranching(
291
- priceRoute,
292
- routeIndex,
293
- swap,
294
- );
295
- const dontCheckBalanceAfterSwap = flag % 3 === 0;
296
- const checkDestTokenBalanceAfterSwap = flag % 3 === 2;
297
- const insertFromAmount = flag % 4 === 3 || flag % 4 === 2;
298
-
299
- const srcTokenAddress =
300
- isETHAddress(swap.srcToken) && needWrapNative
301
- ? this.getWETHAddress(exchangeParam)
302
- : swap.srcToken.toLowerCase();
303
-
304
- const destTokenAddress =
305
- isETHAddress(swap.destToken) && needWrapNative
306
- ? this.getWETHAddress(exchangeParam)
307
- : swap.destToken.toLowerCase();
308
-
309
- exchangeData = this.addTokenAddressToCallData(
310
- exchangeData,
311
- srcTokenAddress,
312
- );
313
-
314
- if (
315
- applyVerticalBranching ||
316
- (checkDestTokenBalanceAfterSwap && !dontCheckBalanceAfterSwap)
317
- ) {
318
- exchangeData = this.addTokenAddressToCallData(
319
- exchangeData,
320
- destTokenAddress,
321
- );
322
- }
323
-
324
- let destTokenPos = 0;
325
- if (checkDestTokenBalanceAfterSwap && !dontCheckBalanceAfterSwap) {
326
- const destTokenAddrIndex = exchangeData
327
- .replace('0x', '')
328
- .indexOf(destTokenAddress.replace('0x', ''));
329
- destTokenPos = (destTokenAddrIndex - 24) / 2;
330
- }
331
-
332
- let fromAmountPos = 0;
333
- if (insertFromAmount) {
334
- const fromAmount = ethers.utils.defaultAbiCoder.encode(
335
- ['uint256'],
336
- [swapExchange.srcAmount],
337
- );
338
- const fromAmountIndex = exchangeData
339
- .replace('0x', '')
340
- .indexOf(fromAmount.replace('0x', ''));
341
-
342
- assert(
343
- fromAmountIndex !== -1,
344
- 'Encoding error: could not resolve position of fromAmount in exchangeData',
345
- );
346
-
347
- fromAmountPos = fromAmountIndex / 2;
348
- }
349
-
350
- return this.buildCallData(
351
- targetExchange,
352
- exchangeData,
353
- fromAmountPos,
354
- destTokenPos,
355
- specialDexFlag || SpecialDex.DEFAULT,
356
- flag,
357
- undefined,
358
- returnAmountPos,
359
- );
360
- }
361
-
362
- private addMultiSwapMetadata(
363
- priceRoute: OptimalRate,
364
- exchangeParams: DexExchangeBuildParam[],
365
- callData: string,
366
- percentage: number,
367
- swap: OptimalSwap,
368
- exchangeParamIndex: number,
369
- wrapWasAddedInSwapExchange: boolean,
370
- addedUnwrapForDexWithNoNeedWrapNative = false,
371
- ) {
372
- let srcTokenAddress = swap.srcToken;
373
-
374
- let doesAnyDexOnSwapNeedsWrapNative: boolean;
375
- if (exchangeParamIndex > -1) {
376
- doesAnyDexOnSwapNeedsWrapNative =
377
- isETHAddress(srcTokenAddress) &&
378
- (exchangeParams[exchangeParamIndex].needWrapNative ||
379
- (!exchangeParams[exchangeParamIndex].needWrapNative &&
380
- addedUnwrapForDexWithNoNeedWrapNative));
381
- } else {
382
- doesAnyDexOnSwapNeedsWrapNative =
383
- isETHAddress(srcTokenAddress) &&
384
- this.anyDexOnSwapNeedsWrapNative(priceRoute, swap, exchangeParams);
385
- }
386
-
387
- if (
388
- doesAnyDexOnSwapNeedsWrapNative &&
389
- isETHAddress(srcTokenAddress) &&
390
- !wrapWasAddedInSwapExchange
391
- ) {
392
- srcTokenAddress =
393
- exchangeParamIndex > -1
394
- ? this.getWETHAddress(exchangeParams[exchangeParamIndex])
395
- : this.dexHelper.config.data.wrappedNativeTokenAddress;
396
- }
397
-
398
- let srcTokenAddressLowered = srcTokenAddress.toLowerCase();
399
- let srcTokenPos: string;
400
-
401
- if (percentage === SWAP_EXCHANGE_100_PERCENTAGE) {
402
- srcTokenPos = hexZeroPad(hexlify(0), 8);
403
- } else if (isETHAddress(srcTokenAddressLowered)) {
404
- srcTokenPos = ETH_SRC_TOKEN_POS_FOR_MULTISWAP_METADATA;
405
- } else {
406
- const srcTokenAddrIndex = callData
407
- .replace('0x', '')
408
- .indexOf(srcTokenAddressLowered.replace('0x', ''));
409
-
410
- srcTokenPos = hexZeroPad(hexlify(srcTokenAddrIndex / 2), 8);
411
- }
412
-
413
- return solidityPack(
414
- ['bytes16', 'bytes8', 'bytes8', 'bytes'],
415
- [
416
- hexZeroPad(hexlify(hexDataLength(callData)), 16), // calldata size
417
- srcTokenPos, // srcTokenPos
418
- hexZeroPad(hexlify(Math.round(percentage * 100)), 8), // percentage
419
- callData, // swap calldata
420
- ],
421
- );
422
- }
423
-
424
- private packVerticalBranchingData(swapCallData: string): string {
425
- return solidityPack(
426
- ['bytes28', 'bytes4', 'bytes32', 'bytes32', 'bytes'],
427
- [
428
- ZEROS_28_BYTES, // empty bytes28
429
- ZEROS_4_BYTES, // fallback selector
430
- hexZeroPad(hexlify(32), 32), // calldata offset
431
- hexZeroPad(hexlify(hexDataLength(swapCallData)), 32), // calldata length
432
- swapCallData, // calldata
433
- ],
434
- );
435
- }
436
-
437
- private packVerticalBranchingCallData(
438
- verticalBranchingData: string,
439
- fromAmountPos: number,
440
- destTokenPos: number,
441
- flag: Flag,
442
- ): string {
443
- return solidityPack(
444
- [
445
- 'bytes20',
446
- 'bytes4',
447
- 'bytes2',
448
- 'bytes2',
449
- 'bytes1',
450
- 'bytes1',
451
- 'bytes2',
452
- 'bytes',
453
- ],
454
- [
455
- ZEROS_20_BYTES, // bytes20(0)
456
- hexZeroPad(hexlify(hexDataLength(verticalBranchingData)), 4), // dex calldata length
457
- hexZeroPad(hexlify(fromAmountPos), 2), // fromAmountPos
458
- hexZeroPad(hexlify(destTokenPos), 2), // destTokenPos
459
- hexZeroPad(hexlify(0), 1), // returnAmountPos
460
- hexZeroPad(hexlify(SpecialDex.EXECUTE_VERTICAL_BRANCHING), 1), // special
461
- hexZeroPad(hexlify(flag), 2), // flag
462
- verticalBranchingData, // dexes calldata
463
- ],
464
- );
465
- }
466
-
467
- private buildVerticalBranchingCallData(
468
- priceRoute: OptimalRate,
469
- routeIndex: number,
470
- exchangeParams: DexExchangeBuildParam[],
471
- swap: OptimalSwap,
472
- swapCallData: string,
473
- flag: Flag,
474
- ) {
475
- const data = this.packVerticalBranchingData(swapCallData);
476
-
477
- const destTokenAddrLowered = swap.destToken.toLowerCase();
478
- const isEthDest = isETHAddress(destTokenAddrLowered);
479
-
480
- let anyDexOnSwapNeedsWrapNative = false;
481
- let anyDexOnSwapDoesntNeedWrapNative = false;
482
- let destTokenPos: number;
483
-
484
- if (isEthDest) {
485
- anyDexOnSwapNeedsWrapNative = this.anyDexOnSwapNeedsWrapNative(
486
- priceRoute,
487
- swap,
488
- exchangeParams,
489
- );
490
- anyDexOnSwapDoesntNeedWrapNative = this.anyDexOnSwapDoesntNeedWrapNative(
491
- priceRoute,
492
- swap,
493
- exchangeParams,
494
- );
495
- }
496
-
497
- if (
498
- isEthDest &&
499
- anyDexOnSwapDoesntNeedWrapNative &&
500
- !anyDexOnSwapNeedsWrapNative
501
- ) {
502
- destTokenPos = 0;
503
- } else {
504
- const destTokenAddrIndex = data
505
- .replace('0x', '')
506
- .indexOf(
507
- (isEthDest
508
- ? this.dexHelper.config.data.wrappedNativeTokenAddress.toLowerCase()
509
- : destTokenAddrLowered.toLowerCase()
510
- ).replace('0x', ''),
511
- );
512
-
513
- destTokenPos = destTokenAddrIndex / 2 - 40;
514
- }
515
-
516
- const fromAmountPos = hexDataLength(data) - 64 - 28; // 64 (position), 28 (selector padding);
517
-
518
- return this.packVerticalBranchingCallData(
519
- data,
520
- fromAmountPos,
521
- destTokenPos < 0 ? 0 : destTokenPos,
522
- flag,
523
- );
524
- }
525
-
526
- private buildSingleSwapExchangeCallData(
527
- priceRoute: OptimalRate,
528
- routeIndex: number,
529
- swapIndex: number,
530
- swapExchangeIndex: number,
531
- exchangeParams: DexExchangeBuildParam[],
532
- flags: { approves: Flag[]; dexes: Flag[]; wrap: Flag },
533
- addedWrapToSwapExchangeMap: { [key: string]: boolean },
534
- allowToAddWrap = true,
535
- prevBranchWasWrapped = false,
536
- maybeWethCallData?: DepositWithdrawReturn,
537
- addMultiSwapMetadata?: boolean,
538
- applyVerticalBranching?: boolean,
539
- ): string {
540
- const isSimpleSwap =
541
- priceRoute.bestRoute.length === 1 &&
542
- priceRoute.bestRoute[0].swaps.length === 1;
543
- let swapExchangeCallData = '';
544
- const swap = priceRoute.bestRoute[routeIndex].swaps[swapIndex];
545
- const swapExchange = swap.swapExchanges[swapExchangeIndex];
546
-
547
- let exchangeParamIndex = 0;
548
- let tempExchangeParamIndex = 0;
549
-
550
- priceRoute.bestRoute.map(route =>
551
- route.swaps.map(curSwap => {
552
- curSwap.swapExchanges.map(async se => {
553
- if (Object.is(se, swapExchange)) {
554
- exchangeParamIndex = tempExchangeParamIndex;
555
- }
556
- tempExchangeParamIndex++;
557
- });
558
- }),
559
- );
560
-
561
- const curExchangeParam = exchangeParams[exchangeParamIndex];
562
-
563
- const dexCallData = this.buildDexCallData({
564
- priceRoute,
565
- routeIndex,
566
- swapIndex,
567
- swapExchangeIndex,
568
- exchangeParams,
569
- exchangeParamIndex,
570
- isLastSwap: false,
571
- flag: flags.dexes[exchangeParamIndex],
572
- swapExchange,
573
- });
574
-
575
- if (curExchangeParam.preSwapUnwrapCalldata) {
576
- const withdrawCallData = this.buildUnwrapEthCallData(
577
- this.getWETHAddress(curExchangeParam),
578
- curExchangeParam.preSwapUnwrapCalldata,
579
- );
580
- swapExchangeCallData = hexConcat([withdrawCallData, dexCallData]);
581
- } else {
582
- swapExchangeCallData = hexConcat([dexCallData]);
583
- }
584
-
585
- const isLastSwap =
586
- swapIndex === priceRoute.bestRoute[routeIndex].swaps.length - 1;
587
- const isLast = exchangeParamIndex === exchangeParams.length - 1;
588
-
589
- if (curExchangeParam.transferSrcTokenBeforeSwap) {
590
- const transferCallData = this.buildTransferCallData(
591
- this.erc20Interface.encodeFunctionData('transfer', [
592
- curExchangeParam.transferSrcTokenBeforeSwap,
593
- swapExchange.srcAmount,
594
- ]),
595
- isETHAddress(swap.srcToken)
596
- ? this.getWETHAddress(curExchangeParam)
597
- : swap.srcToken.toLowerCase(),
598
- );
599
-
600
- swapExchangeCallData = hexConcat([
601
- transferCallData,
602
- swapExchangeCallData,
603
- ]);
604
- }
605
-
606
- if (
607
- !isETHAddress(swap.srcToken) &&
608
- !curExchangeParam.transferSrcTokenBeforeSwap &&
609
- curExchangeParam.approveData
610
- ) {
611
- const approveCallData = this.buildApproveCallData(
612
- curExchangeParam.approveData.target,
613
- curExchangeParam.approveData.token,
614
- flags.approves[exchangeParamIndex],
615
- );
616
-
617
- swapExchangeCallData = hexConcat([approveCallData, swapExchangeCallData]);
618
- }
619
-
620
- if (curExchangeParam.needWrapNative) {
621
- if (isETHAddress(swap.srcToken)) {
622
- let approveWethCalldata = '0x';
623
- if (
624
- curExchangeParam.approveData &&
625
- !curExchangeParam.transferSrcTokenBeforeSwap
626
- ) {
627
- approveWethCalldata = this.buildApproveCallData(
628
- curExchangeParam.approveData.target,
629
- curExchangeParam.approveData.token,
630
- flags.approves[exchangeParamIndex],
631
- );
632
- }
633
-
634
- const isNotFirstSwap = swapIndex !== 0;
635
- let skipWrap = false;
636
- if (isNotFirstSwap) {
637
- const prevSwap =
638
- priceRoute.bestRoute[routeIndex].swaps[swapIndex - 1];
639
- const anyDexOnSwapDoesntNeedWrapNative =
640
- this.anyDexOnSwapDoesntNeedWrapNative(
641
- priceRoute,
642
- prevSwap,
643
- exchangeParams,
644
- );
645
- skipWrap = !anyDexOnSwapDoesntNeedWrapNative;
646
- }
647
-
648
- let depositCallData = '0x';
649
- if (
650
- maybeWethCallData &&
651
- maybeWethCallData.deposit &&
652
- !this.doesRouteNeedsRootWrapEth(priceRoute, exchangeParams) &&
653
- allowToAddWrap &&
654
- !addedWrapToSwapExchangeMap[
655
- `${routeIndex}_${swapIndex}_${swapExchangeIndex}`
656
- ] &&
657
- !skipWrap
658
- ) {
659
- depositCallData = this.buildWrapEthCallData(
660
- this.getWETHAddress(curExchangeParam),
661
- maybeWethCallData.deposit.calldata,
662
- Flag.SEND_ETH_EQUAL_TO_FROM_AMOUNT_DONT_CHECK_BALANCE_AFTER_SWAP, // 9
663
- );
664
- addedWrapToSwapExchangeMap[
665
- `${routeIndex}_${swapIndex}_${swapExchangeIndex}`
666
- ] = true;
667
- }
668
-
669
- swapExchangeCallData = hexConcat([
670
- approveWethCalldata,
671
- depositCallData,
672
- swapExchangeCallData,
673
- ]);
674
- }
675
-
676
- if (
677
- maybeWethCallData &&
678
- maybeWethCallData.withdraw &&
679
- ((!applyVerticalBranching && isETHAddress(swap.destToken)) ||
680
- (applyVerticalBranching &&
681
- isETHAddress(swap.destToken) &&
682
- this.anyDexOnSwapDoesntNeedWrapNative(
683
- priceRoute,
684
- swap,
685
- exchangeParams,
686
- )))
687
- ) {
688
- let withdrawCallData = '0x';
689
-
690
- const customWethAddress = curExchangeParam.wethAddress;
691
- const isLastSimpleWithUnwrap =
692
- isSimpleSwap &&
693
- // check if current exchange is the last with needWrapNative
694
- exchangeParams.reduceRight(
695
- (acc, exchangeParam, index) =>
696
- exchangeParam.needWrapNative === true && acc === -1 ? index : acc,
697
- -1,
698
- ) === exchangeParamIndex;
699
-
700
- if (
701
- customWethAddress ||
702
- isLastSimpleWithUnwrap // unwrap after last dex call with unwrap for simple swap case
703
- ) {
704
- withdrawCallData = this.buildUnwrapEthCallData(
705
- this.getWETHAddress(curExchangeParam),
706
- maybeWethCallData.withdraw.calldata,
707
- );
708
- }
709
-
710
- swapExchangeCallData = hexConcat([
711
- swapExchangeCallData,
712
- withdrawCallData,
713
- ]);
714
-
715
- if (isLastSimpleWithUnwrap || customWethAddress) {
716
- const finalSpecialFlagCalldata = this.buildFinalSpecialFlagCalldata();
717
- swapExchangeCallData = hexConcat([
718
- swapExchangeCallData,
719
- finalSpecialFlagCalldata,
720
- ]);
721
- }
722
- }
723
- }
724
-
725
- let addedUnwrapForDexWithNoNeedWrapNative = false;
726
- if (
727
- isETHAddress(swap.srcToken) &&
728
- maybeWethCallData &&
729
- maybeWethCallData.withdraw &&
730
- !curExchangeParam.needWrapNative
731
- ) {
732
- const prevSwap = priceRoute.bestRoute[routeIndex].swaps[swapIndex - 1];
733
- let eachDexOnPrevSwapReturnsWeth: boolean = false;
734
-
735
- if (prevSwap && !prevBranchWasWrapped) {
736
- eachDexOnPrevSwapReturnsWeth = this.eachDexOnSwapNeedsWrapNative(
737
- priceRoute,
738
- prevSwap,
739
- exchangeParams,
740
- );
741
- }
742
-
743
- if (prevBranchWasWrapped || eachDexOnPrevSwapReturnsWeth) {
744
- const withdrawCallData = this.buildUnwrapEthCallData(
745
- this.getWETHAddress(curExchangeParam),
746
- maybeWethCallData.withdraw.calldata,
747
- );
748
-
749
- swapExchangeCallData = hexConcat([
750
- withdrawCallData,
751
- swapExchangeCallData,
752
- ]);
753
- addedUnwrapForDexWithNoNeedWrapNative = true;
754
- }
755
- }
756
-
757
- if (
758
- isLastSwap &&
759
- !exchangeParams[exchangeParamIndex].dexFuncHasRecipient &&
760
- !isETHAddress(swap.destToken) &&
761
- priceRoute.destToken === swap.destToken
762
- ) {
763
- const transferCallData = this.buildTransferCallData(
764
- this.erc20Interface.encodeFunctionData('transfer', [
765
- this.dexHelper.config.data.augustusV6Address,
766
- swapExchange.destAmount,
767
- ]),
768
- swap.destToken,
769
- );
770
-
771
- swapExchangeCallData = hexConcat([
772
- swapExchangeCallData,
773
- transferCallData,
774
- ]);
775
- }
776
-
777
- if (
778
- !exchangeParams[exchangeParamIndex].dexFuncHasRecipient &&
779
- isETHAddress(swap.destToken) &&
780
- isLast &&
781
- // don't need to send eth without unwrapping, handling unwrap and sendEth in the end of root branch
782
- !this.doesRouteNeedsRootUnwrapEth(priceRoute, exchangeParams)
783
- ) {
784
- const finalSpecialFlagCalldata = this.buildFinalSpecialFlagCalldata();
785
- swapExchangeCallData = hexConcat([
786
- swapExchangeCallData,
787
- finalSpecialFlagCalldata,
788
- ]);
789
- }
790
-
791
- if (addMultiSwapMetadata) {
792
- return this.addMultiSwapMetadata(
793
- priceRoute,
794
- exchangeParams,
795
- swapExchangeCallData,
796
- swapExchange.percent,
797
- swap,
798
- exchangeParamIndex,
799
- addedWrapToSwapExchangeMap[
800
- `${routeIndex}_${swapIndex}_${swapExchangeIndex}`
801
- ],
802
- addedUnwrapForDexWithNoNeedWrapNative,
803
- );
804
- }
805
-
806
- return swapExchangeCallData;
807
- }
808
-
809
- private appendWrapEthCallData(
810
- calldata: string,
811
- maybeWethCallData?: DepositWithdrawReturn,
812
- checkWethBalanceAfter = false,
813
- ) {
814
- if (maybeWethCallData?.deposit) {
815
- const callData = checkWethBalanceAfter
816
- ? this.addTokenAddressToCallData(
817
- maybeWethCallData.deposit.calldata,
818
- this.dexHelper.config.data.wrappedNativeTokenAddress.toLowerCase(),
819
- )
820
- : maybeWethCallData.deposit.calldata;
821
-
822
- const depositCallData = this.buildWrapEthCallData(
823
- this.dexHelper.config.data.wrappedNativeTokenAddress.toLowerCase(),
824
- callData,
825
- checkWethBalanceAfter
826
- ? Flag.SEND_ETH_EQUAL_TO_FROM_AMOUNT_CHECK_SRC_TOKEN_BALANCE_AFTER_SWAP // 5
827
- : Flag.SEND_ETH_EQUAL_TO_FROM_AMOUNT_DONT_CHECK_BALANCE_AFTER_SWAP, // 9
828
- checkWethBalanceAfter ? 4 : 0,
829
- );
830
-
831
- return hexConcat([calldata, depositCallData]);
832
- }
833
-
834
- return calldata;
835
- }
836
-
837
- private eachDexOnSwapNeedsWrapNative(
838
- priceRoute: OptimalRate,
839
- swap: OptimalSwap,
840
- exchangeParams: DexExchangeBuildParam[],
841
- ): boolean {
842
- return swap.swapExchanges.every(curSe => {
843
- let index = 0;
844
- let swapExchangeIndex = 0;
845
- priceRoute.bestRoute.map(route => {
846
- route.swaps.map(curSwap =>
847
- curSwap.swapExchanges.map(async se => {
848
- if (Object.is(se, curSe)) {
849
- index = swapExchangeIndex;
850
- }
851
- swapExchangeIndex++;
852
- }),
853
- );
854
- });
855
-
856
- const curExchangeParam = exchangeParams[index];
857
-
858
- return curExchangeParam.needWrapNative && !curExchangeParam.wethAddress;
859
- });
860
- }
861
-
862
- private anyDexOnSwapNeedsWrapNative(
863
- priceRoute: OptimalRate,
864
- swap: OptimalSwap,
865
- exchangeParams: DexExchangeBuildParam[],
866
- ): boolean {
867
- const res = swap.swapExchanges.map(curSe => {
868
- let index = 0;
869
- let swapExchangeIndex = 0;
870
- priceRoute.bestRoute.map(route => {
871
- route.swaps.map(curSwap => {
872
- return curSwap.swapExchanges.map(async se => {
873
- if (Object.is(se, curSe)) {
874
- index = swapExchangeIndex;
875
- }
876
- swapExchangeIndex++;
877
- });
878
- });
879
- });
880
-
881
- const curExchangeParam = exchangeParams[index];
882
-
883
- return curExchangeParam.needWrapNative && !curExchangeParam.wethAddress;
884
- });
885
-
886
- return res.includes(true);
887
- }
888
-
889
- private getSwapExchangesWhichNeedWrapNative(
890
- priceRoute: OptimalRate,
891
- swap: OptimalSwap,
892
- exchangeParams: DexExchangeBuildParam[],
893
- ) {
894
- return swap.swapExchanges.filter(curSe => {
895
- let index = 0;
896
- let swapExchangeIndex = 0;
897
- priceRoute.bestRoute.map(route => {
898
- route.swaps.map(curSwap => {
899
- return curSwap.swapExchanges.map(async se => {
900
- if (Object.is(se, curSe)) {
901
- index = swapExchangeIndex;
902
- }
903
- swapExchangeIndex++;
904
- });
905
- });
906
- });
907
-
908
- const curExchangeParam = exchangeParams[index];
909
-
910
- return curExchangeParam.needWrapNative && !curExchangeParam.wethAddress;
911
- });
912
- }
913
-
914
- private getSwapExchangesWhichDontNeedWrapNative(
915
- priceRoute: OptimalRate,
916
- swap: OptimalSwap,
917
- exchangeParams: DexExchangeBuildParam[],
918
- ) {
919
- return swap.swapExchanges.filter(curSe => {
920
- let index = 0;
921
- let swapExchangeIndex = 0;
922
- priceRoute.bestRoute.map(route => {
923
- route.swaps.map(curSwap => {
924
- return curSwap.swapExchanges.map(async se => {
925
- if (Object.is(se, curSe)) {
926
- index = swapExchangeIndex;
927
- }
928
- swapExchangeIndex++;
929
- });
930
- });
931
- });
932
-
933
- const curExchangeParam = exchangeParams[index];
934
-
935
- return !curExchangeParam.needWrapNative || curExchangeParam.wethAddress;
936
- });
937
- }
938
-
939
- private anyDexOnSwapDoesntNeedWrapNative(
940
- priceRoute: OptimalRate,
941
- swap: OptimalSwap,
942
- exchangeParams: DexExchangeBuildParam[],
943
- ): boolean {
944
- return swap.swapExchanges
945
- .map(curSe => {
946
- let index = 0;
947
- let swapExchangeIndex = 0;
948
- priceRoute.bestRoute.map(route => {
949
- route.swaps.map(curSwap =>
950
- curSwap.swapExchanges.map(async se => {
951
- if (Object.is(se, curSe)) {
952
- index = swapExchangeIndex;
953
- }
954
- swapExchangeIndex++;
955
- }),
956
- );
957
- });
958
-
959
- const curExchangeParam = exchangeParams[index];
960
-
961
- return !curExchangeParam.needWrapNative;
962
- })
963
- .includes(true);
964
- }
965
-
966
- private doesSwapNeedToApplyVerticalBranching(
967
- priceRoute: OptimalRate,
968
- routeIndex: number,
969
- swap: OptimalSwap,
970
- ): boolean {
971
- const isMegaSwap = priceRoute.bestRoute.length > 1;
972
- const isMultiSwap =
973
- !isMegaSwap && priceRoute.bestRoute[routeIndex].swaps.length > 1;
974
-
975
- return (isMultiSwap || isMegaSwap) && swap.swapExchanges.length > 1;
976
- }
977
-
978
- private buildVerticalBranchingFlag(
979
- priceRoute: OptimalRate,
980
- swap: OptimalSwap,
981
- exchangeParams: DexExchangeBuildParam[],
982
- routeIndex: number,
983
- swapIndex: number,
984
- ): Flag {
985
- let flag = Flag.INSERT_FROM_AMOUNT_CHECK_SRC_TOKEN_BALANCE_AFTER_SWAP; // 11
986
-
987
- const isLastSwap =
988
- swapIndex === priceRoute.bestRoute[routeIndex].swaps.length - 1;
989
-
990
- if (isLastSwap) {
991
- const isEthDest = isETHAddress(priceRoute.destToken);
992
- const lastSwap =
993
- priceRoute.bestRoute[routeIndex].swaps[
994
- priceRoute.bestRoute[routeIndex].swaps.length - 1
995
- ];
996
- const lastSwapExchanges = lastSwap.swapExchanges;
997
- const anyDexLastSwapNeedUnwrap = lastSwapExchanges
998
- .map(curSe => {
999
- let index = 0;
1000
- let swapExchangeIndex = 0;
1001
- priceRoute.bestRoute[routeIndex].swaps.map(curSwap =>
1002
- curSwap.swapExchanges.map(async se => {
1003
- if (Object.is(se, curSe)) {
1004
- index = swapExchangeIndex;
1005
- }
1006
- swapExchangeIndex++;
1007
- }),
1008
- );
1009
-
1010
- const curExchangeParam = exchangeParams[index];
1011
-
1012
- return (
1013
- curExchangeParam.needWrapNative && !curExchangeParam.wethAddress
1014
- );
1015
- })
1016
- .includes(true);
1017
-
1018
- const noNeedUnwrap = isEthDest && !anyDexLastSwapNeedUnwrap;
1019
-
1020
- if (noNeedUnwrap || !isEthDest) {
1021
- flag = Flag.INSERT_FROM_AMOUNT_DONT_CHECK_BALANCE_AFTER_SWAP; // 3
1022
- }
1023
- } else {
1024
- const isEthDest = isETHAddress(swap.destToken);
1025
-
1026
- if (isEthDest) {
1027
- if (
1028
- this.anyDexOnSwapDoesntNeedWrapNative(
1029
- priceRoute,
1030
- swap,
1031
- exchangeParams,
1032
- )
1033
- ) {
1034
- flag = Flag.INSERT_FROM_AMOUNT_CHECK_ETH_BALANCE_AFTER_SWAP; // 7
1035
- }
1036
- }
1037
- }
1038
-
1039
- return flag;
1040
- }
1041
-
1042
- protected buildSingleSwapCallData(
1043
- params: SingleSwapCallDataParams<Executor02SingleSwapCallDataParams>,
1044
- ): string {
1045
- const {
1046
- priceRoute,
1047
- exchangeParams,
1048
- routeIndex,
1049
- swapIndex,
1050
- flags,
1051
- maybeWethCallData,
1052
- wrapToSwapMap,
1053
- wrapToSwapExchangeMap,
1054
- swap,
1055
- } = params;
1056
- const isLastSwap =
1057
- swapIndex === priceRoute.bestRoute[routeIndex].swaps.length - 1;
1058
- const isMegaSwap = priceRoute.bestRoute.length > 1;
1059
- const isMultiSwap =
1060
- !isMegaSwap && priceRoute.bestRoute[routeIndex].swaps.length > 1;
1061
-
1062
- const { swapExchanges } = swap;
1063
-
1064
- const applyVerticalBranching = this.doesSwapNeedToApplyVerticalBranching(
1065
- priceRoute,
1066
- routeIndex,
1067
- swap,
1068
- );
1069
-
1070
- const anyDexOnSwapDoesntNeedWrapNative =
1071
- this.anyDexOnSwapDoesntNeedWrapNative(priceRoute, swap, exchangeParams);
1072
-
1073
- const needToAppendWrapCallData =
1074
- isETHAddress(swap.destToken) &&
1075
- anyDexOnSwapDoesntNeedWrapNative &&
1076
- !isLastSwap &&
1077
- maybeWethCallData?.deposit;
1078
-
1079
- let swapCallData = swapExchanges.reduce(
1080
- (acc, swapExchange, swapExchangeIndex) => {
1081
- return hexConcat([
1082
- acc,
1083
- this.buildSingleSwapExchangeCallData(
1084
- priceRoute,
1085
- routeIndex,
1086
- swapIndex,
1087
- swapExchangeIndex,
1088
- exchangeParams,
1089
- flags,
1090
- wrapToSwapExchangeMap,
1091
- !wrapToSwapMap[swapIndex - 1],
1092
- wrapToSwapMap[swapIndex - 1],
1093
- maybeWethCallData,
1094
- swap.swapExchanges.length > 1,
1095
- applyVerticalBranching,
1096
- ),
1097
- ]);
1098
- },
1099
- '0x',
1100
- );
1101
-
1102
- if (needToAppendWrapCallData) {
1103
- wrapToSwapMap[swapIndex] = true;
1104
- }
1105
-
1106
- if (!isMultiSwap && !isMegaSwap) {
1107
- return needToAppendWrapCallData
1108
- ? this.appendWrapEthCallData(swapCallData, maybeWethCallData)
1109
- : swapCallData;
1110
- }
1111
-
1112
- if (applyVerticalBranching) {
1113
- const vertBranchingCallData = this.buildVerticalBranchingCallData(
1114
- priceRoute,
1115
- routeIndex,
1116
- exchangeParams,
1117
- swap,
1118
- swapCallData,
1119
- this.buildVerticalBranchingFlag(
1120
- priceRoute,
1121
- swap,
1122
- exchangeParams,
1123
- routeIndex,
1124
- swapIndex,
1125
- ),
1126
- );
1127
-
1128
- return needToAppendWrapCallData
1129
- ? this.appendWrapEthCallData(
1130
- vertBranchingCallData,
1131
- maybeWethCallData,
1132
- true,
1133
- )
1134
- : vertBranchingCallData;
1135
- }
1136
-
1137
- return needToAppendWrapCallData
1138
- ? this.appendWrapEthCallData(swapCallData, maybeWethCallData)
1139
- : swapCallData;
1140
- }
1141
-
1142
- protected buildSingleRouteCallData(
1143
- priceRoute: OptimalRate,
1144
- exchangeParams: DexExchangeBuildParam[],
1145
- route: OptimalRoute,
1146
- routeIndex: number,
1147
- flags: { approves: Flag[]; dexes: Flag[]; wrap: Flag },
1148
- sender: string,
1149
- maybeWethCallData?: DepositWithdrawReturn,
1150
- ): string {
1151
- const isMegaSwap = priceRoute.bestRoute.length > 1;
1152
-
1153
- const { swaps } = route;
1154
-
1155
- const appendedWrapToSwapExchangeMap = {};
1156
- const addedWrapToSwapMap = {};
1157
- const callData = swaps.reduce<string>(
1158
- (swapAcc, swap, swapIndex) =>
1159
- hexConcat([
1160
- swapAcc,
1161
- this.buildSingleSwapCallData({
1162
- priceRoute,
1163
- exchangeParams,
1164
- routeIndex,
1165
- swapIndex,
1166
- flags,
1167
- sender,
1168
- wrapToSwapExchangeMap: appendedWrapToSwapExchangeMap,
1169
- wrapToSwapMap: addedWrapToSwapMap,
1170
- maybeWethCallData,
1171
- swap,
1172
- index: 0,
1173
- }),
1174
- ]),
1175
- '0x',
1176
- );
1177
-
1178
- const routeDoesntNeedToAddMultiSwapMetadata =
1179
- route.swaps.length === 1 &&
1180
- route.swaps[0].swapExchanges.length !== 1 &&
1181
- !this.doesSwapNeedToApplyVerticalBranching(
1182
- priceRoute,
1183
- routeIndex,
1184
- route.swaps[0],
1185
- );
1186
-
1187
- if (isMegaSwap && !routeDoesntNeedToAddMultiSwapMetadata) {
1188
- return this.addMultiSwapMetadata(
1189
- priceRoute,
1190
- exchangeParams,
1191
- callData,
1192
- route.percent,
1193
- route.swaps[0],
1194
- NOT_EXISTING_EXCHANGE_PARAM_INDEX,
1195
- Object.values(addedWrapToSwapMap).includes(true),
1196
- );
1197
- }
1198
-
1199
- return callData;
1200
- }
1201
-
1202
- private doesRouteNeedsRootWrapEth(
1203
- priceRoute: OptimalRate,
1204
- exchangeParams: DexExchangeBuildParam[],
1205
- ): boolean {
1206
- if (!isETHAddress(priceRoute.srcToken)) {
1207
- return false;
1208
- }
1209
-
1210
- const res = priceRoute.bestRoute.every((route, routeIndex) => {
1211
- const firstSwap = route.swaps[0];
1212
- const eachDexOnSwapNeedsWrapNative = this.eachDexOnSwapNeedsWrapNative(
1213
- priceRoute,
1214
- firstSwap,
1215
- exchangeParams,
1216
- );
1217
-
1218
- return eachDexOnSwapNeedsWrapNative;
1219
- });
1220
-
1221
- return res;
1222
- }
1223
-
1224
- private doesRouteNeedsRootUnwrapEth(
1225
- priceRoute: OptimalRate,
1226
- exchangeParams: DexExchangeParam[],
1227
- ): boolean {
1228
- if (!isETHAddress(priceRoute.destToken)) {
1229
- return false;
1230
- }
1231
-
1232
- const res = priceRoute.bestRoute.some((route, routeIndex) => {
1233
- const lastSwap = route.swaps[route.swaps.length - 1];
1234
- const anyDexOnSwapNeedsWrapNative = this.anyDexOnSwapNeedsWrapNative(
1235
- priceRoute,
1236
- lastSwap,
1237
- exchangeParams,
1238
- );
1239
-
1240
- return anyDexOnSwapNeedsWrapNative;
1241
- });
1242
-
1243
- return res;
1244
- }
1245
-
1246
- public getAddress(): string {
1247
- return this.dexHelper.config.data.executorsAddresses![Executors.TWO];
1248
- }
1249
-
1250
- public buildByteCode(
1251
- priceRoute: OptimalRate,
1252
- exchangeParams: DexExchangeBuildParam[],
1253
- sender: string,
1254
- maybeWethCallData?: DepositWithdrawReturn,
1255
- ): string {
1256
- const isMegaSwap = priceRoute.bestRoute.length > 1;
1257
- const isMultiSwap = !isMegaSwap && priceRoute.bestRoute[0].swaps.length > 1;
1258
-
1259
- const needWrapEth =
1260
- maybeWethCallData?.deposit && isETHAddress(priceRoute.srcToken);
1261
- const needUnwrapEth =
1262
- maybeWethCallData?.withdraw && isETHAddress(priceRoute.destToken);
1263
- const needSendNativeEth = isETHAddress(priceRoute.destToken);
1264
- const routeNeedsRootWrapEth = this.doesRouteNeedsRootWrapEth(
1265
- priceRoute,
1266
- exchangeParams,
1267
- );
1268
- const routeNeedsRootUnwrapEth = this.doesRouteNeedsRootUnwrapEth(
1269
- priceRoute,
1270
- exchangeParams,
1271
- );
1272
-
1273
- const flags = this.buildFlags(
1274
- priceRoute,
1275
- exchangeParams,
1276
- maybeWethCallData,
1277
- );
1278
-
1279
- let swapsCalldata = priceRoute.bestRoute.reduce<string>(
1280
- (routeAcc, route, routeIndex) =>
1281
- hexConcat([
1282
- routeAcc,
1283
- this.buildSingleRouteCallData(
1284
- priceRoute,
1285
- exchangeParams,
1286
- route,
1287
- routeIndex,
1288
- flags,
1289
- sender,
1290
- maybeWethCallData,
1291
- ),
1292
- ]),
1293
- '0x',
1294
- );
1295
-
1296
- if (isMegaSwap && (needWrapEth || needUnwrapEth)) {
1297
- const lastPriceRoute =
1298
- priceRoute.bestRoute[priceRoute.bestRoute.length - 1];
1299
- swapsCalldata = this.buildVerticalBranchingCallData(
1300
- priceRoute,
1301
- priceRoute.bestRoute.length - 1,
1302
- exchangeParams,
1303
- lastPriceRoute.swaps[lastPriceRoute.swaps.length - 1],
1304
- swapsCalldata,
1305
- needWrapEth
1306
- ? Flag.DONT_INSERT_FROM_AMOUNT_DONT_CHECK_BALANCE_AFTER_SWAP // 0
1307
- : Flag.DONT_INSERT_FROM_AMOUNT_CHECK_SRC_TOKEN_BALANCE_AFTER_SWAP, // 8
1308
- );
1309
- }
1310
-
1311
- // ETH wrap
1312
- if (needWrapEth && routeNeedsRootWrapEth) {
1313
- let depositCallData = this.buildWrapEthCallData(
1314
- this.dexHelper.config.data.wrappedNativeTokenAddress,
1315
- maybeWethCallData.deposit!.calldata,
1316
- Flag.SEND_ETH_EQUAL_TO_FROM_AMOUNT_DONT_CHECK_BALANCE_AFTER_SWAP, // 9
1317
- );
1318
-
1319
- if (!(isMegaSwap || isMultiSwap)) {
1320
- const swap = priceRoute.bestRoute[0].swaps[0];
1321
- const percent = exchangeParams.every(ep => ep.needWrapNative)
1322
- ? 100
1323
- : swap.swapExchanges
1324
- .filter((se, index) => {
1325
- return exchangeParams[index].needWrapNative;
1326
- })
1327
- .reduce<number>((acc, se) => {
1328
- acc += se.percent;
1329
- return acc;
1330
- }, 0);
1331
-
1332
- depositCallData = solidityPack(
1333
- ['bytes16', 'bytes16', 'bytes'],
1334
- [
1335
- hexZeroPad(hexlify(hexDataLength(depositCallData)), 16),
1336
- hexZeroPad(hexlify(100 * percent), 16),
1337
- depositCallData,
1338
- ],
1339
- );
1340
- }
1341
-
1342
- swapsCalldata = hexConcat([depositCallData, swapsCalldata]);
1343
- }
1344
-
1345
- // ETH unwrap, only for multiswaps and mega swaps
1346
- if (
1347
- needUnwrapEth &&
1348
- routeNeedsRootUnwrapEth &&
1349
- (isMultiSwap || isMegaSwap)
1350
- ) {
1351
- const withdrawCallData = this.buildUnwrapEthCallData(
1352
- this.dexHelper.config.data.wrappedNativeTokenAddress,
1353
- maybeWethCallData.withdraw!.calldata,
1354
- );
1355
- swapsCalldata = hexConcat([swapsCalldata, withdrawCallData]);
1356
- }
1357
-
1358
- // Special flag (send native) calldata, only for multiswaps and mega swaps
1359
- if (
1360
- needSendNativeEth &&
1361
- routeNeedsRootUnwrapEth &&
1362
- (isMultiSwap || isMegaSwap)
1363
- ) {
1364
- const finalSpecialFlagCalldata = this.buildFinalSpecialFlagCalldata();
1365
- swapsCalldata = hexConcat([swapsCalldata, finalSpecialFlagCalldata]);
1366
- }
1367
-
1368
- if (((needWrapEth || needUnwrapEth) && isMegaSwap) || isMultiSwap) {
1369
- swapsCalldata = this.addMultiSwapMetadata(
1370
- priceRoute,
1371
- exchangeParams,
1372
- swapsCalldata,
1373
- SWAP_EXCHANGE_100_PERCENTAGE,
1374
- priceRoute.bestRoute[0].swaps[0],
1375
- NOT_EXISTING_EXCHANGE_PARAM_INDEX,
1376
- false,
1377
- );
1378
- }
1379
-
1380
- return solidityPack(
1381
- ['bytes32', 'bytes', 'bytes'],
1382
- [
1383
- hexZeroPad(hexlify(32), 32), // calldata offset
1384
- hexZeroPad(
1385
- hexlify(hexDataLength(swapsCalldata) + BYTES_64_LENGTH), // calldata length (64 bytes = bytes12(0) + msg.sender)
1386
- 32,
1387
- ),
1388
- swapsCalldata, // calldata
1389
- ],
1390
- );
1391
- }
1392
- }
1
+ import { ethers } from 'ethers';
2
+ import {
3
+ OptimalRoute,
4
+ OptimalRate,
5
+ OptimalSwap,
6
+ OptimalSwapExchange,
7
+ } from '@paraswap/core';
8
+ import { DexExchangeBuildParam, DexExchangeParam } from '../types';
9
+ import { Executors, Flag, SpecialDex } from './types';
10
+ import { isETHAddress } from '../utils';
11
+ import { DepositWithdrawReturn } from '../dex/weth/types';
12
+ import {
13
+ DexCallDataParams,
14
+ ExecutorBytecodeBuilder,
15
+ SingleSwapCallDataParams,
16
+ } from './ExecutorBytecodeBuilder';
17
+ import {
18
+ BYTES_64_LENGTH,
19
+ NOT_EXISTING_EXCHANGE_PARAM_INDEX,
20
+ ETH_SRC_TOKEN_POS_FOR_MULTISWAP_METADATA,
21
+ SWAP_EXCHANGE_100_PERCENTAGE,
22
+ ZEROS_20_BYTES,
23
+ ZEROS_28_BYTES,
24
+ ZEROS_4_BYTES,
25
+ DEFAULT_RETURN_AMOUNT_POS,
26
+ } from './constants';
27
+ import { assert } from 'ts-essentials';
28
+
29
+ const {
30
+ utils: { hexlify, hexDataLength, hexConcat, hexZeroPad, solidityPack },
31
+ } = ethers;
32
+
33
+ export type Executor02SingleSwapCallDataParams = {
34
+ routeIndex: number;
35
+ swapIndex: number;
36
+ wrapToSwapMap: { [key: number]: boolean };
37
+ wrapToSwapExchangeMap: { [key: string]: boolean };
38
+ swap: OptimalSwap;
39
+ };
40
+
41
+ export type Executor02DexCallDataParams = {
42
+ swapExchange: OptimalSwapExchange<any>;
43
+ };
44
+
45
+ /**
46
+ * Class to build bytecode for Executor02 - simpleSwap with N DEXs (VERTICAL_BRANCH), multiSwaps (VERTICAL_BRANCH_HORIZONTAL_SEQUENCE) and megaswaps (NESTED_VERTICAL_BRANCH_HORIZONTAL_SEQUENCE)
47
+ */
48
+ export class Executor02BytecodeBuilder extends ExecutorBytecodeBuilder<
49
+ Executor02SingleSwapCallDataParams,
50
+ Executor02DexCallDataParams
51
+ > {
52
+ type = Executors.TWO;
53
+ /**
54
+ * Executor02 Flags:
55
+ * switch (flag % 4):
56
+ * case 0: don't instert fromAmount
57
+ * case 1: sendEth equal to fromAmount
58
+ * case 2: sendEth equal to fromAmount + insert fromAmount
59
+ * case 3: insert fromAmount
60
+
61
+ * switch (flag % 3):
62
+ * case 0: don't check balance after swap
63
+ * case 1: check eth balance after swap
64
+ * case 2: check destToken balance after swap
65
+ */
66
+ protected buildSimpleSwapFlags(
67
+ priceRoute: OptimalRate,
68
+ exchangeParams: DexExchangeBuildParam[],
69
+ routeIndex: number,
70
+ swapIndex: number,
71
+ swapExchangeIndex: number,
72
+ exchangeParamIndex: number,
73
+ maybeWethCallData?: DepositWithdrawReturn,
74
+ ): { dexFlag: Flag; approveFlag: Flag } {
75
+ const { srcToken, destToken } =
76
+ priceRoute.bestRoute[routeIndex].swaps[swapIndex];
77
+ const isEthSrc = isETHAddress(srcToken);
78
+ const isEthDest = isETHAddress(destToken);
79
+
80
+ const exchangeParam = exchangeParams[exchangeParamIndex];
81
+ const {
82
+ dexFuncHasRecipient,
83
+ needWrapNative,
84
+ specialDexFlag,
85
+ specialDexSupportsInsertFromAmount,
86
+ swappedAmountNotPresentInExchangeData,
87
+ preSwapUnwrapCalldata,
88
+ } = exchangeParam;
89
+
90
+ const needWrap = needWrapNative && isEthSrc && maybeWethCallData?.deposit;
91
+ const needUnwrap =
92
+ needWrapNative && isEthDest && maybeWethCallData?.withdraw;
93
+ const isSpecialDex =
94
+ specialDexFlag !== undefined && specialDexFlag !== SpecialDex.DEFAULT;
95
+
96
+ const forcePreventInsertFromAmount =
97
+ swappedAmountNotPresentInExchangeData ||
98
+ (isSpecialDex && !specialDexSupportsInsertFromAmount);
99
+
100
+ let dexFlag = forcePreventInsertFromAmount
101
+ ? Flag.DONT_INSERT_FROM_AMOUNT_DONT_CHECK_BALANCE_AFTER_SWAP
102
+ : Flag.INSERT_FROM_AMOUNT_DONT_CHECK_BALANCE_AFTER_SWAP; // 0 or 3
103
+ let approveFlag =
104
+ Flag.DONT_INSERT_FROM_AMOUNT_DONT_CHECK_BALANCE_AFTER_SWAP; // 0
105
+
106
+ if (isEthSrc && !needWrap) {
107
+ dexFlag =
108
+ Flag.SEND_ETH_EQUAL_TO_FROM_AMOUNT_CHECK_SRC_TOKEN_BALANCE_AFTER_SWAP; // 5
109
+ } else if (isEthDest && !needUnwrap) {
110
+ dexFlag = forcePreventInsertFromAmount
111
+ ? Flag.DONT_INSERT_FROM_AMOUNT_CHECK_ETH_BALANCE_AFTER_SWAP
112
+ : Flag.INSERT_FROM_AMOUNT_CHECK_ETH_BALANCE_AFTER_SWAP; // 4 or 7
113
+ } else if (!dexFuncHasRecipient || (isEthDest && needUnwrap)) {
114
+ dexFlag = forcePreventInsertFromAmount
115
+ ? Flag.DONT_INSERT_FROM_AMOUNT_CHECK_SRC_TOKEN_BALANCE_AFTER_SWAP
116
+ : Flag.INSERT_FROM_AMOUNT_CHECK_SRC_TOKEN_BALANCE_AFTER_SWAP; // 8 or 11
117
+ }
118
+
119
+ // Actual srcToken is eth, because we'll unwrap weth before swap.
120
+ // Need to check balance, some dexes don't have 1:1 ETH -> custom_ETH rate
121
+ if (preSwapUnwrapCalldata) {
122
+ dexFlag =
123
+ Flag.SEND_ETH_EQUAL_TO_FROM_AMOUNT_CHECK_SRC_TOKEN_BALANCE_AFTER_SWAP;
124
+ }
125
+
126
+ return {
127
+ dexFlag,
128
+ approveFlag,
129
+ };
130
+ }
131
+
132
+ /**
133
+ * Executor02 Flags:
134
+ * switch (flag % 4):
135
+ * case 0: don't instert fromAmount
136
+ * case 1: sendEth equal to fromAmount
137
+ * case 2: sendEth equal to fromAmount + insert fromAmount
138
+ * case 3: insert fromAmount
139
+
140
+ * switch (flag % 3):
141
+ * case 0: don't check balance after swap
142
+ * case 1: check eth balance after swap
143
+ * case 2: check destToken balance after swap
144
+ */
145
+ protected buildMultiMegaSwapFlags(
146
+ priceRoute: OptimalRate,
147
+ exchangeParams: DexExchangeBuildParam[],
148
+ routeIndex: number,
149
+ swapIndex: number,
150
+ swapExchangeIndex: number,
151
+ exchangeParamIndex: number,
152
+ maybeWethCallData?: DepositWithdrawReturn,
153
+ ): { dexFlag: Flag; approveFlag: Flag } {
154
+ const route = priceRoute.bestRoute[routeIndex];
155
+ const swap = route.swaps[swapIndex];
156
+
157
+ const exchangeParam = exchangeParams[exchangeParamIndex];
158
+
159
+ const { srcToken, destToken } = swap;
160
+ const applyVerticalBranching = this.doesSwapNeedToApplyVerticalBranching(
161
+ priceRoute,
162
+ routeIndex,
163
+ swap,
164
+ );
165
+
166
+ const isHorizontalSequence = route.swaps.length > 1; // check if route is a multi-swap (horizontal sequence)
167
+ const isFirstSwap = swapIndex === 0;
168
+ const isLastSwap = !isFirstSwap && swapIndex === route.swaps.length - 1;
169
+
170
+ const {
171
+ dexFuncHasRecipient,
172
+ needWrapNative,
173
+ specialDexFlag,
174
+ specialDexSupportsInsertFromAmount,
175
+ swappedAmountNotPresentInExchangeData,
176
+ wethAddress,
177
+ sendEthButSupportsInsertFromAmount,
178
+ preSwapUnwrapCalldata,
179
+ } = exchangeParam;
180
+
181
+ const isEthSrc = isETHAddress(srcToken);
182
+ const isEthDest = isETHAddress(destToken);
183
+ const isWethDest =
184
+ (wethAddress && destToken.toLowerCase() === wethAddress.toLowerCase()) ||
185
+ this.dexHelper.config.isWETH(destToken);
186
+
187
+ const isSpecialDex =
188
+ specialDexFlag !== undefined && specialDexFlag !== SpecialDex.DEFAULT;
189
+
190
+ const forcePreventInsertFromAmount =
191
+ swappedAmountNotPresentInExchangeData ||
192
+ (isSpecialDex && !specialDexSupportsInsertFromAmount);
193
+
194
+ const forceBalanceOfCheck =
195
+ (isSpecialDex &&
196
+ isHorizontalSequence &&
197
+ !applyVerticalBranching &&
198
+ !isLastSwap) ||
199
+ !dexFuncHasRecipient;
200
+
201
+ const needUnwrap =
202
+ needWrapNative && isEthDest && maybeWethCallData?.withdraw;
203
+
204
+ const needSendEth = isEthSrc && !needWrapNative;
205
+ const needCheckEthBalance = isEthDest && !needWrapNative;
206
+
207
+ const needCheckSrcTokenBalanceOf =
208
+ (needUnwrap && !applyVerticalBranching) ||
209
+ (isHorizontalSequence && !applyVerticalBranching && !isLastSwap);
210
+
211
+ let dexFlag: Flag;
212
+ let approveFlag =
213
+ Flag.DONT_INSERT_FROM_AMOUNT_DONT_CHECK_BALANCE_AFTER_SWAP; // 0
214
+
215
+ if (needSendEth) {
216
+ const preventInsertForSendEth =
217
+ forcePreventInsertFromAmount || !sendEthButSupportsInsertFromAmount;
218
+ dexFlag = forceBalanceOfCheck
219
+ ? preventInsertForSendEth
220
+ ? Flag.SEND_ETH_EQUAL_TO_FROM_AMOUNT_CHECK_SRC_TOKEN_BALANCE_AFTER_SWAP // 5
221
+ : Flag.SEND_ETH_EQUAL_TO_FROM_AMOUNT_PLUS_INSERT_FROM_AMOUNT_CHECK_SRC_TOKEN_BALANCE_AFTER_SWAP // 14
222
+ : dexFuncHasRecipient
223
+ ? preventInsertForSendEth
224
+ ? Flag.SEND_ETH_EQUAL_TO_FROM_AMOUNT_DONT_CHECK_BALANCE_AFTER_SWAP // 9
225
+ : Flag.SEND_ETH_EQUAL_TO_FROM_AMOUNT_PLUS_INSERT_FROM_AMOUNT_DONT_CHECK_BALANCE_AFTER_SWAP // 18
226
+ : preventInsertForSendEth
227
+ ? Flag.SEND_ETH_EQUAL_TO_FROM_AMOUNT_CHECK_SRC_TOKEN_BALANCE_AFTER_SWAP // 5
228
+ : Flag.SEND_ETH_EQUAL_TO_FROM_AMOUNT_PLUS_INSERT_FROM_AMOUNT_DONT_CHECK_BALANCE_AFTER_SWAP; // 18
229
+ } else if (needCheckEthBalance) {
230
+ dexFlag =
231
+ needCheckSrcTokenBalanceOf || forceBalanceOfCheck
232
+ ? forcePreventInsertFromAmount && dexFuncHasRecipient
233
+ ? Flag.DONT_INSERT_FROM_AMOUNT_CHECK_ETH_BALANCE_AFTER_SWAP // 4
234
+ : Flag.INSERT_FROM_AMOUNT_CHECK_ETH_BALANCE_AFTER_SWAP // 7
235
+ : forcePreventInsertFromAmount && dexFuncHasRecipient
236
+ ? Flag.DONT_INSERT_FROM_AMOUNT_DONT_CHECK_BALANCE_AFTER_SWAP // 0
237
+ : Flag.INSERT_FROM_AMOUNT_DONT_CHECK_BALANCE_AFTER_SWAP; // 3
238
+ } else {
239
+ dexFlag =
240
+ needCheckSrcTokenBalanceOf || forceBalanceOfCheck
241
+ ? forcePreventInsertFromAmount
242
+ ? Flag.DONT_INSERT_FROM_AMOUNT_CHECK_SRC_TOKEN_BALANCE_AFTER_SWAP // 8
243
+ : Flag.INSERT_FROM_AMOUNT_CHECK_SRC_TOKEN_BALANCE_AFTER_SWAP // 11
244
+ : forcePreventInsertFromAmount
245
+ ? Flag.DONT_INSERT_FROM_AMOUNT_DONT_CHECK_BALANCE_AFTER_SWAP // 0
246
+ : Flag.INSERT_FROM_AMOUNT_DONT_CHECK_BALANCE_AFTER_SWAP; // 3
247
+ }
248
+
249
+ // Actual srcToken is eth, because we'll unwrap weth before swap.
250
+ // Need to check balance, some dexes don't have 1:1 ETH -> custom_ETH rate
251
+ if (preSwapUnwrapCalldata) {
252
+ dexFlag =
253
+ Flag.SEND_ETH_EQUAL_TO_FROM_AMOUNT_CHECK_SRC_TOKEN_BALANCE_AFTER_SWAP;
254
+ }
255
+
256
+ return {
257
+ dexFlag,
258
+ approveFlag,
259
+ };
260
+ }
261
+
262
+ protected buildDexCallData(
263
+ params: DexCallDataParams<Executor02DexCallDataParams>,
264
+ ): string {
265
+ const {
266
+ priceRoute,
267
+ exchangeParamIndex,
268
+ swapExchange,
269
+ exchangeParams,
270
+ routeIndex,
271
+ swapIndex,
272
+ flag,
273
+ } = params;
274
+
275
+ const swap = priceRoute.bestRoute[routeIndex].swaps[swapIndex];
276
+ const exchangeParam = exchangeParams[exchangeParamIndex];
277
+ let { exchangeData, specialDexFlag, targetExchange, needWrapNative } =
278
+ exchangeParam;
279
+
280
+ const routeNeedsRootUnwrapEth = this.doesRouteNeedsRootUnwrapEth(
281
+ priceRoute,
282
+ exchangeParams,
283
+ );
284
+
285
+ const returnAmountPos =
286
+ exchangeParam.returnAmountPos !== undefined && !routeNeedsRootUnwrapEth // prevent returnAmoutPos optimisation if route needs root unwrap eth
287
+ ? exchangeParam.returnAmountPos
288
+ : DEFAULT_RETURN_AMOUNT_POS;
289
+
290
+ const applyVerticalBranching = this.doesSwapNeedToApplyVerticalBranching(
291
+ priceRoute,
292
+ routeIndex,
293
+ swap,
294
+ );
295
+ const dontCheckBalanceAfterSwap = flag % 3 === 0;
296
+ const checkDestTokenBalanceAfterSwap = flag % 3 === 2;
297
+ const insertFromAmount = flag % 4 === 3 || flag % 4 === 2;
298
+
299
+ const srcTokenAddress =
300
+ isETHAddress(swap.srcToken) && needWrapNative
301
+ ? this.getWETHAddress(exchangeParam)
302
+ : swap.srcToken.toLowerCase();
303
+
304
+ const destTokenAddress =
305
+ isETHAddress(swap.destToken) && needWrapNative
306
+ ? this.getWETHAddress(exchangeParam)
307
+ : swap.destToken.toLowerCase();
308
+
309
+ exchangeData = this.addTokenAddressToCallData(
310
+ exchangeData,
311
+ srcTokenAddress,
312
+ );
313
+
314
+ if (
315
+ applyVerticalBranching ||
316
+ (checkDestTokenBalanceAfterSwap && !dontCheckBalanceAfterSwap)
317
+ ) {
318
+ exchangeData = this.addTokenAddressToCallData(
319
+ exchangeData,
320
+ destTokenAddress,
321
+ );
322
+ }
323
+
324
+ let destTokenPos = 0;
325
+ if (checkDestTokenBalanceAfterSwap && !dontCheckBalanceAfterSwap) {
326
+ const destTokenAddrIndex = exchangeData
327
+ .replace('0x', '')
328
+ .indexOf(destTokenAddress.replace('0x', ''));
329
+ destTokenPos = (destTokenAddrIndex - 24) / 2;
330
+ }
331
+
332
+ let fromAmountPos = 0;
333
+ if (insertFromAmount) {
334
+ const fromAmount = ethers.utils.defaultAbiCoder.encode(
335
+ ['uint256'],
336
+ [swapExchange.srcAmount],
337
+ );
338
+ const fromAmountIndex = exchangeData
339
+ .replace('0x', '')
340
+ .indexOf(fromAmount.replace('0x', ''));
341
+
342
+ assert(
343
+ fromAmountIndex !== -1,
344
+ 'Encoding error: could not resolve position of fromAmount in exchangeData',
345
+ );
346
+
347
+ fromAmountPos = fromAmountIndex / 2;
348
+ }
349
+
350
+ return this.buildCallData(
351
+ targetExchange,
352
+ exchangeData,
353
+ fromAmountPos,
354
+ destTokenPos,
355
+ specialDexFlag || SpecialDex.DEFAULT,
356
+ flag,
357
+ undefined,
358
+ returnAmountPos,
359
+ );
360
+ }
361
+
362
+ private addMultiSwapMetadata(
363
+ priceRoute: OptimalRate,
364
+ exchangeParams: DexExchangeBuildParam[],
365
+ callData: string,
366
+ percentage: number,
367
+ swap: OptimalSwap,
368
+ exchangeParamIndex: number,
369
+ wrapWasAddedInSwapExchange: boolean,
370
+ addedUnwrapForDexWithNoNeedWrapNative = false,
371
+ ) {
372
+ let srcTokenAddress = swap.srcToken;
373
+
374
+ let doesAnyDexOnSwapNeedsWrapNative: boolean;
375
+ if (exchangeParamIndex > -1) {
376
+ doesAnyDexOnSwapNeedsWrapNative =
377
+ isETHAddress(srcTokenAddress) &&
378
+ (exchangeParams[exchangeParamIndex].needWrapNative ||
379
+ (!exchangeParams[exchangeParamIndex].needWrapNative &&
380
+ addedUnwrapForDexWithNoNeedWrapNative));
381
+ } else {
382
+ doesAnyDexOnSwapNeedsWrapNative =
383
+ isETHAddress(srcTokenAddress) &&
384
+ this.anyDexOnSwapNeedsWrapNative(priceRoute, swap, exchangeParams);
385
+ }
386
+
387
+ if (
388
+ doesAnyDexOnSwapNeedsWrapNative &&
389
+ isETHAddress(srcTokenAddress) &&
390
+ !wrapWasAddedInSwapExchange
391
+ ) {
392
+ srcTokenAddress =
393
+ exchangeParamIndex > -1
394
+ ? this.getWETHAddress(exchangeParams[exchangeParamIndex])
395
+ : this.dexHelper.config.data.wrappedNativeTokenAddress;
396
+ }
397
+
398
+ let srcTokenAddressLowered = srcTokenAddress.toLowerCase();
399
+ let srcTokenPos: string;
400
+
401
+ if (percentage === SWAP_EXCHANGE_100_PERCENTAGE) {
402
+ srcTokenPos = hexZeroPad(hexlify(0), 8);
403
+ } else if (isETHAddress(srcTokenAddressLowered)) {
404
+ srcTokenPos = ETH_SRC_TOKEN_POS_FOR_MULTISWAP_METADATA;
405
+ } else {
406
+ const srcTokenAddrIndex = callData
407
+ .replace('0x', '')
408
+ .indexOf(srcTokenAddressLowered.replace('0x', ''));
409
+
410
+ srcTokenPos = hexZeroPad(hexlify(srcTokenAddrIndex / 2), 8);
411
+ }
412
+
413
+ return solidityPack(
414
+ ['bytes16', 'bytes8', 'bytes8', 'bytes'],
415
+ [
416
+ hexZeroPad(hexlify(hexDataLength(callData)), 16), // calldata size
417
+ srcTokenPos, // srcTokenPos
418
+ hexZeroPad(hexlify(Math.round(percentage * 100)), 8), // percentage
419
+ callData, // swap calldata
420
+ ],
421
+ );
422
+ }
423
+
424
+ private packVerticalBranchingData(swapCallData: string): string {
425
+ return solidityPack(
426
+ ['bytes28', 'bytes4', 'bytes32', 'bytes32', 'bytes'],
427
+ [
428
+ ZEROS_28_BYTES, // empty bytes28
429
+ ZEROS_4_BYTES, // fallback selector
430
+ hexZeroPad(hexlify(32), 32), // calldata offset
431
+ hexZeroPad(hexlify(hexDataLength(swapCallData)), 32), // calldata length
432
+ swapCallData, // calldata
433
+ ],
434
+ );
435
+ }
436
+
437
+ private packVerticalBranchingCallData(
438
+ verticalBranchingData: string,
439
+ fromAmountPos: number,
440
+ destTokenPos: number,
441
+ flag: Flag,
442
+ ): string {
443
+ return solidityPack(
444
+ [
445
+ 'bytes20',
446
+ 'bytes4',
447
+ 'bytes2',
448
+ 'bytes2',
449
+ 'bytes1',
450
+ 'bytes1',
451
+ 'bytes2',
452
+ 'bytes',
453
+ ],
454
+ [
455
+ ZEROS_20_BYTES, // bytes20(0)
456
+ hexZeroPad(hexlify(hexDataLength(verticalBranchingData)), 4), // dex calldata length
457
+ hexZeroPad(hexlify(fromAmountPos), 2), // fromAmountPos
458
+ hexZeroPad(hexlify(destTokenPos), 2), // destTokenPos
459
+ hexZeroPad(hexlify(0), 1), // returnAmountPos
460
+ hexZeroPad(hexlify(SpecialDex.EXECUTE_VERTICAL_BRANCHING), 1), // special
461
+ hexZeroPad(hexlify(flag), 2), // flag
462
+ verticalBranchingData, // dexes calldata
463
+ ],
464
+ );
465
+ }
466
+
467
+ private buildVerticalBranchingCallData(
468
+ priceRoute: OptimalRate,
469
+ routeIndex: number,
470
+ exchangeParams: DexExchangeBuildParam[],
471
+ swap: OptimalSwap,
472
+ swapCallData: string,
473
+ flag: Flag,
474
+ ) {
475
+ const data = this.packVerticalBranchingData(swapCallData);
476
+
477
+ const destTokenAddrLowered = swap.destToken.toLowerCase();
478
+ const isEthDest = isETHAddress(destTokenAddrLowered);
479
+
480
+ let anyDexOnSwapNeedsWrapNative = false;
481
+ let anyDexOnSwapDoesntNeedWrapNative = false;
482
+ let destTokenPos: number;
483
+
484
+ if (isEthDest) {
485
+ anyDexOnSwapNeedsWrapNative = this.anyDexOnSwapNeedsWrapNative(
486
+ priceRoute,
487
+ swap,
488
+ exchangeParams,
489
+ );
490
+ anyDexOnSwapDoesntNeedWrapNative = this.anyDexOnSwapDoesntNeedWrapNative(
491
+ priceRoute,
492
+ swap,
493
+ exchangeParams,
494
+ );
495
+ }
496
+
497
+ if (
498
+ isEthDest &&
499
+ anyDexOnSwapDoesntNeedWrapNative &&
500
+ !anyDexOnSwapNeedsWrapNative
501
+ ) {
502
+ destTokenPos = 0;
503
+ } else {
504
+ const destTokenAddrIndex = data
505
+ .replace('0x', '')
506
+ .indexOf(
507
+ (isEthDest
508
+ ? this.dexHelper.config.data.wrappedNativeTokenAddress.toLowerCase()
509
+ : destTokenAddrLowered.toLowerCase()
510
+ ).replace('0x', ''),
511
+ );
512
+
513
+ destTokenPos = destTokenAddrIndex / 2 - 40;
514
+ }
515
+
516
+ const fromAmountPos = hexDataLength(data) - 64 - 28; // 64 (position), 28 (selector padding);
517
+
518
+ return this.packVerticalBranchingCallData(
519
+ data,
520
+ fromAmountPos,
521
+ destTokenPos < 0 ? 0 : destTokenPos,
522
+ flag,
523
+ );
524
+ }
525
+
526
+ private buildSingleSwapExchangeCallData(
527
+ priceRoute: OptimalRate,
528
+ routeIndex: number,
529
+ swapIndex: number,
530
+ swapExchangeIndex: number,
531
+ exchangeParams: DexExchangeBuildParam[],
532
+ flags: { approves: Flag[]; dexes: Flag[]; wrap: Flag },
533
+ addedWrapToSwapExchangeMap: { [key: string]: boolean },
534
+ allowToAddWrap = true,
535
+ prevBranchWasWrapped = false,
536
+ maybeWethCallData?: DepositWithdrawReturn,
537
+ addMultiSwapMetadata?: boolean,
538
+ applyVerticalBranching?: boolean,
539
+ ): string {
540
+ const isSimpleSwap =
541
+ priceRoute.bestRoute.length === 1 &&
542
+ priceRoute.bestRoute[0].swaps.length === 1;
543
+ let swapExchangeCallData = '';
544
+ const swap = priceRoute.bestRoute[routeIndex].swaps[swapIndex];
545
+ const swapExchange = swap.swapExchanges[swapExchangeIndex];
546
+
547
+ let exchangeParamIndex = 0;
548
+ let tempExchangeParamIndex = 0;
549
+
550
+ priceRoute.bestRoute.map(route =>
551
+ route.swaps.map(curSwap => {
552
+ curSwap.swapExchanges.map(async se => {
553
+ if (Object.is(se, swapExchange)) {
554
+ exchangeParamIndex = tempExchangeParamIndex;
555
+ }
556
+ tempExchangeParamIndex++;
557
+ });
558
+ }),
559
+ );
560
+
561
+ const curExchangeParam = exchangeParams[exchangeParamIndex];
562
+
563
+ const dexCallData = this.buildDexCallData({
564
+ priceRoute,
565
+ routeIndex,
566
+ swapIndex,
567
+ swapExchangeIndex,
568
+ exchangeParams,
569
+ exchangeParamIndex,
570
+ isLastSwap: false,
571
+ flag: flags.dexes[exchangeParamIndex],
572
+ swapExchange,
573
+ });
574
+
575
+ if (curExchangeParam.preSwapUnwrapCalldata) {
576
+ const withdrawCallData = this.buildUnwrapEthCallData(
577
+ this.getWETHAddress(curExchangeParam),
578
+ curExchangeParam.preSwapUnwrapCalldata,
579
+ );
580
+ swapExchangeCallData = hexConcat([withdrawCallData, dexCallData]);
581
+ } else {
582
+ swapExchangeCallData = hexConcat([dexCallData]);
583
+ }
584
+
585
+ const isLastSwap =
586
+ swapIndex === priceRoute.bestRoute[routeIndex].swaps.length - 1;
587
+ const isLast = exchangeParamIndex === exchangeParams.length - 1;
588
+
589
+ if (curExchangeParam.transferSrcTokenBeforeSwap) {
590
+ const transferCallData = this.buildTransferCallData(
591
+ this.erc20Interface.encodeFunctionData('transfer', [
592
+ curExchangeParam.transferSrcTokenBeforeSwap,
593
+ swapExchange.srcAmount,
594
+ ]),
595
+ isETHAddress(swap.srcToken)
596
+ ? this.getWETHAddress(curExchangeParam)
597
+ : swap.srcToken.toLowerCase(),
598
+ );
599
+
600
+ swapExchangeCallData = hexConcat([
601
+ transferCallData,
602
+ swapExchangeCallData,
603
+ ]);
604
+ }
605
+
606
+ if (
607
+ !isETHAddress(swap.srcToken) &&
608
+ !curExchangeParam.transferSrcTokenBeforeSwap &&
609
+ curExchangeParam.approveData
610
+ ) {
611
+ const approveCallData = this.buildApproveCallData(
612
+ curExchangeParam.approveData.target,
613
+ curExchangeParam.approveData.token,
614
+ flags.approves[exchangeParamIndex],
615
+ );
616
+
617
+ swapExchangeCallData = hexConcat([approveCallData, swapExchangeCallData]);
618
+ }
619
+
620
+ if (curExchangeParam.needWrapNative) {
621
+ if (isETHAddress(swap.srcToken)) {
622
+ let approveWethCalldata = '0x';
623
+ if (
624
+ curExchangeParam.approveData &&
625
+ !curExchangeParam.transferSrcTokenBeforeSwap
626
+ ) {
627
+ approveWethCalldata = this.buildApproveCallData(
628
+ curExchangeParam.approveData.target,
629
+ curExchangeParam.approveData.token,
630
+ flags.approves[exchangeParamIndex],
631
+ );
632
+ }
633
+
634
+ const isNotFirstSwap = swapIndex !== 0;
635
+ let skipWrap = false;
636
+ if (isNotFirstSwap) {
637
+ const prevSwap =
638
+ priceRoute.bestRoute[routeIndex].swaps[swapIndex - 1];
639
+ const anyDexOnSwapDoesntNeedWrapNative =
640
+ this.anyDexOnSwapDoesntNeedWrapNative(
641
+ priceRoute,
642
+ prevSwap,
643
+ exchangeParams,
644
+ );
645
+ skipWrap = !anyDexOnSwapDoesntNeedWrapNative;
646
+ }
647
+
648
+ let depositCallData = '0x';
649
+ if (
650
+ maybeWethCallData &&
651
+ maybeWethCallData.deposit &&
652
+ !this.doesRouteNeedsRootWrapEth(priceRoute, exchangeParams) &&
653
+ allowToAddWrap &&
654
+ !addedWrapToSwapExchangeMap[
655
+ `${routeIndex}_${swapIndex}_${swapExchangeIndex}`
656
+ ] &&
657
+ !skipWrap
658
+ ) {
659
+ depositCallData = this.buildWrapEthCallData(
660
+ this.getWETHAddress(curExchangeParam),
661
+ maybeWethCallData.deposit.calldata,
662
+ Flag.SEND_ETH_EQUAL_TO_FROM_AMOUNT_DONT_CHECK_BALANCE_AFTER_SWAP, // 9
663
+ );
664
+ addedWrapToSwapExchangeMap[
665
+ `${routeIndex}_${swapIndex}_${swapExchangeIndex}`
666
+ ] = true;
667
+ }
668
+
669
+ swapExchangeCallData = hexConcat([
670
+ approveWethCalldata,
671
+ depositCallData,
672
+ swapExchangeCallData,
673
+ ]);
674
+ }
675
+
676
+ if (
677
+ maybeWethCallData &&
678
+ maybeWethCallData.withdraw &&
679
+ ((!applyVerticalBranching && isETHAddress(swap.destToken)) ||
680
+ (applyVerticalBranching &&
681
+ isETHAddress(swap.destToken) &&
682
+ this.anyDexOnSwapDoesntNeedWrapNative(
683
+ priceRoute,
684
+ swap,
685
+ exchangeParams,
686
+ )))
687
+ ) {
688
+ let withdrawCallData = '0x';
689
+
690
+ const customWethAddress = curExchangeParam.wethAddress;
691
+ const isLastSimpleWithUnwrap =
692
+ isSimpleSwap &&
693
+ // check if current exchange is the last with needWrapNative
694
+ exchangeParams.reduceRight(
695
+ (acc, exchangeParam, index) =>
696
+ exchangeParam.needWrapNative === true && acc === -1 ? index : acc,
697
+ -1,
698
+ ) === exchangeParamIndex;
699
+
700
+ if (
701
+ customWethAddress ||
702
+ isLastSimpleWithUnwrap // unwrap after last dex call with unwrap for simple swap case
703
+ ) {
704
+ withdrawCallData = this.buildUnwrapEthCallData(
705
+ this.getWETHAddress(curExchangeParam),
706
+ maybeWethCallData.withdraw.calldata,
707
+ );
708
+ }
709
+
710
+ swapExchangeCallData = hexConcat([
711
+ swapExchangeCallData,
712
+ withdrawCallData,
713
+ ]);
714
+
715
+ if (isLastSimpleWithUnwrap || customWethAddress) {
716
+ const finalSpecialFlagCalldata = this.buildFinalSpecialFlagCalldata();
717
+ swapExchangeCallData = hexConcat([
718
+ swapExchangeCallData,
719
+ finalSpecialFlagCalldata,
720
+ ]);
721
+ }
722
+ }
723
+ }
724
+
725
+ let addedUnwrapForDexWithNoNeedWrapNative = false;
726
+ if (
727
+ isETHAddress(swap.srcToken) &&
728
+ maybeWethCallData &&
729
+ maybeWethCallData.withdraw &&
730
+ !curExchangeParam.needWrapNative
731
+ ) {
732
+ const prevSwap = priceRoute.bestRoute[routeIndex].swaps[swapIndex - 1];
733
+ let eachDexOnPrevSwapReturnsWeth: boolean = false;
734
+
735
+ if (prevSwap && !prevBranchWasWrapped) {
736
+ eachDexOnPrevSwapReturnsWeth = this.eachDexOnSwapNeedsWrapNative(
737
+ priceRoute,
738
+ prevSwap,
739
+ exchangeParams,
740
+ );
741
+ }
742
+
743
+ if (prevBranchWasWrapped || eachDexOnPrevSwapReturnsWeth) {
744
+ const withdrawCallData = this.buildUnwrapEthCallData(
745
+ this.getWETHAddress(curExchangeParam),
746
+ maybeWethCallData.withdraw.calldata,
747
+ );
748
+
749
+ swapExchangeCallData = hexConcat([
750
+ withdrawCallData,
751
+ swapExchangeCallData,
752
+ ]);
753
+ addedUnwrapForDexWithNoNeedWrapNative = true;
754
+ }
755
+ }
756
+
757
+ if (
758
+ isLastSwap &&
759
+ !exchangeParams[exchangeParamIndex].dexFuncHasRecipient &&
760
+ !isETHAddress(swap.destToken) &&
761
+ priceRoute.destToken === swap.destToken
762
+ ) {
763
+ const transferCallData = this.buildTransferCallData(
764
+ this.erc20Interface.encodeFunctionData('transfer', [
765
+ this.dexHelper.config.data.augustusV6Address,
766
+ swapExchange.destAmount,
767
+ ]),
768
+ swap.destToken,
769
+ );
770
+
771
+ swapExchangeCallData = hexConcat([
772
+ swapExchangeCallData,
773
+ transferCallData,
774
+ ]);
775
+ }
776
+
777
+ if (
778
+ !exchangeParams[exchangeParamIndex].dexFuncHasRecipient &&
779
+ isETHAddress(swap.destToken) &&
780
+ isLast &&
781
+ // don't need to send eth without unwrapping, handling unwrap and sendEth in the end of root branch
782
+ !this.doesRouteNeedsRootUnwrapEth(priceRoute, exchangeParams)
783
+ ) {
784
+ const finalSpecialFlagCalldata = this.buildFinalSpecialFlagCalldata();
785
+ swapExchangeCallData = hexConcat([
786
+ swapExchangeCallData,
787
+ finalSpecialFlagCalldata,
788
+ ]);
789
+ }
790
+
791
+ if (addMultiSwapMetadata) {
792
+ return this.addMultiSwapMetadata(
793
+ priceRoute,
794
+ exchangeParams,
795
+ swapExchangeCallData,
796
+ swapExchange.percent,
797
+ swap,
798
+ exchangeParamIndex,
799
+ addedWrapToSwapExchangeMap[
800
+ `${routeIndex}_${swapIndex}_${swapExchangeIndex}`
801
+ ],
802
+ addedUnwrapForDexWithNoNeedWrapNative,
803
+ );
804
+ }
805
+
806
+ return swapExchangeCallData;
807
+ }
808
+
809
+ private appendWrapEthCallData(
810
+ calldata: string,
811
+ maybeWethCallData?: DepositWithdrawReturn,
812
+ checkWethBalanceAfter = false,
813
+ ) {
814
+ if (maybeWethCallData?.deposit) {
815
+ const callData = checkWethBalanceAfter
816
+ ? this.addTokenAddressToCallData(
817
+ maybeWethCallData.deposit.calldata,
818
+ this.dexHelper.config.data.wrappedNativeTokenAddress.toLowerCase(),
819
+ )
820
+ : maybeWethCallData.deposit.calldata;
821
+
822
+ const depositCallData = this.buildWrapEthCallData(
823
+ this.dexHelper.config.data.wrappedNativeTokenAddress.toLowerCase(),
824
+ callData,
825
+ checkWethBalanceAfter
826
+ ? Flag.SEND_ETH_EQUAL_TO_FROM_AMOUNT_CHECK_SRC_TOKEN_BALANCE_AFTER_SWAP // 5
827
+ : Flag.SEND_ETH_EQUAL_TO_FROM_AMOUNT_DONT_CHECK_BALANCE_AFTER_SWAP, // 9
828
+ checkWethBalanceAfter ? 4 : 0,
829
+ );
830
+
831
+ return hexConcat([calldata, depositCallData]);
832
+ }
833
+
834
+ return calldata;
835
+ }
836
+
837
+ private eachDexOnSwapNeedsWrapNative(
838
+ priceRoute: OptimalRate,
839
+ swap: OptimalSwap,
840
+ exchangeParams: DexExchangeBuildParam[],
841
+ ): boolean {
842
+ return swap.swapExchanges.every(curSe => {
843
+ let index = 0;
844
+ let swapExchangeIndex = 0;
845
+ priceRoute.bestRoute.map(route => {
846
+ route.swaps.map(curSwap =>
847
+ curSwap.swapExchanges.map(async se => {
848
+ if (Object.is(se, curSe)) {
849
+ index = swapExchangeIndex;
850
+ }
851
+ swapExchangeIndex++;
852
+ }),
853
+ );
854
+ });
855
+
856
+ const curExchangeParam = exchangeParams[index];
857
+
858
+ return curExchangeParam.needWrapNative && !curExchangeParam.wethAddress;
859
+ });
860
+ }
861
+
862
+ private anyDexOnSwapNeedsWrapNative(
863
+ priceRoute: OptimalRate,
864
+ swap: OptimalSwap,
865
+ exchangeParams: DexExchangeBuildParam[],
866
+ ): boolean {
867
+ const res = swap.swapExchanges.map(curSe => {
868
+ let index = 0;
869
+ let swapExchangeIndex = 0;
870
+ priceRoute.bestRoute.map(route => {
871
+ route.swaps.map(curSwap => {
872
+ return curSwap.swapExchanges.map(async se => {
873
+ if (Object.is(se, curSe)) {
874
+ index = swapExchangeIndex;
875
+ }
876
+ swapExchangeIndex++;
877
+ });
878
+ });
879
+ });
880
+
881
+ const curExchangeParam = exchangeParams[index];
882
+
883
+ return curExchangeParam.needWrapNative && !curExchangeParam.wethAddress;
884
+ });
885
+
886
+ return res.includes(true);
887
+ }
888
+
889
+ private getSwapExchangesWhichNeedWrapNative(
890
+ priceRoute: OptimalRate,
891
+ swap: OptimalSwap,
892
+ exchangeParams: DexExchangeBuildParam[],
893
+ ) {
894
+ return swap.swapExchanges.filter(curSe => {
895
+ let index = 0;
896
+ let swapExchangeIndex = 0;
897
+ priceRoute.bestRoute.map(route => {
898
+ route.swaps.map(curSwap => {
899
+ return curSwap.swapExchanges.map(async se => {
900
+ if (Object.is(se, curSe)) {
901
+ index = swapExchangeIndex;
902
+ }
903
+ swapExchangeIndex++;
904
+ });
905
+ });
906
+ });
907
+
908
+ const curExchangeParam = exchangeParams[index];
909
+
910
+ return curExchangeParam.needWrapNative && !curExchangeParam.wethAddress;
911
+ });
912
+ }
913
+
914
+ private getSwapExchangesWhichDontNeedWrapNative(
915
+ priceRoute: OptimalRate,
916
+ swap: OptimalSwap,
917
+ exchangeParams: DexExchangeBuildParam[],
918
+ ) {
919
+ return swap.swapExchanges.filter(curSe => {
920
+ let index = 0;
921
+ let swapExchangeIndex = 0;
922
+ priceRoute.bestRoute.map(route => {
923
+ route.swaps.map(curSwap => {
924
+ return curSwap.swapExchanges.map(async se => {
925
+ if (Object.is(se, curSe)) {
926
+ index = swapExchangeIndex;
927
+ }
928
+ swapExchangeIndex++;
929
+ });
930
+ });
931
+ });
932
+
933
+ const curExchangeParam = exchangeParams[index];
934
+
935
+ return !curExchangeParam.needWrapNative || curExchangeParam.wethAddress;
936
+ });
937
+ }
938
+
939
+ private anyDexOnSwapDoesntNeedWrapNative(
940
+ priceRoute: OptimalRate,
941
+ swap: OptimalSwap,
942
+ exchangeParams: DexExchangeBuildParam[],
943
+ ): boolean {
944
+ return swap.swapExchanges
945
+ .map(curSe => {
946
+ let index = 0;
947
+ let swapExchangeIndex = 0;
948
+ priceRoute.bestRoute.map(route => {
949
+ route.swaps.map(curSwap =>
950
+ curSwap.swapExchanges.map(async se => {
951
+ if (Object.is(se, curSe)) {
952
+ index = swapExchangeIndex;
953
+ }
954
+ swapExchangeIndex++;
955
+ }),
956
+ );
957
+ });
958
+
959
+ const curExchangeParam = exchangeParams[index];
960
+
961
+ return !curExchangeParam.needWrapNative;
962
+ })
963
+ .includes(true);
964
+ }
965
+
966
+ private doesSwapNeedToApplyVerticalBranching(
967
+ priceRoute: OptimalRate,
968
+ routeIndex: number,
969
+ swap: OptimalSwap,
970
+ ): boolean {
971
+ const isMegaSwap = priceRoute.bestRoute.length > 1;
972
+ const isMultiSwap =
973
+ !isMegaSwap && priceRoute.bestRoute[routeIndex].swaps.length > 1;
974
+
975
+ return (isMultiSwap || isMegaSwap) && swap.swapExchanges.length > 1;
976
+ }
977
+
978
+ private buildVerticalBranchingFlag(
979
+ priceRoute: OptimalRate,
980
+ swap: OptimalSwap,
981
+ exchangeParams: DexExchangeBuildParam[],
982
+ routeIndex: number,
983
+ swapIndex: number,
984
+ ): Flag {
985
+ let flag = Flag.INSERT_FROM_AMOUNT_CHECK_SRC_TOKEN_BALANCE_AFTER_SWAP; // 11
986
+
987
+ const isLastSwap =
988
+ swapIndex === priceRoute.bestRoute[routeIndex].swaps.length - 1;
989
+
990
+ if (isLastSwap) {
991
+ const isEthDest = isETHAddress(priceRoute.destToken);
992
+ const lastSwap =
993
+ priceRoute.bestRoute[routeIndex].swaps[
994
+ priceRoute.bestRoute[routeIndex].swaps.length - 1
995
+ ];
996
+ const lastSwapExchanges = lastSwap.swapExchanges;
997
+ const anyDexLastSwapNeedUnwrap = lastSwapExchanges
998
+ .map(curSe => {
999
+ let index = 0;
1000
+ let swapExchangeIndex = 0;
1001
+ priceRoute.bestRoute[routeIndex].swaps.map(curSwap =>
1002
+ curSwap.swapExchanges.map(async se => {
1003
+ if (Object.is(se, curSe)) {
1004
+ index = swapExchangeIndex;
1005
+ }
1006
+ swapExchangeIndex++;
1007
+ }),
1008
+ );
1009
+
1010
+ const curExchangeParam = exchangeParams[index];
1011
+
1012
+ return (
1013
+ curExchangeParam.needWrapNative && !curExchangeParam.wethAddress
1014
+ );
1015
+ })
1016
+ .includes(true);
1017
+
1018
+ const noNeedUnwrap = isEthDest && !anyDexLastSwapNeedUnwrap;
1019
+
1020
+ if (noNeedUnwrap || !isEthDest) {
1021
+ flag = Flag.INSERT_FROM_AMOUNT_DONT_CHECK_BALANCE_AFTER_SWAP; // 3
1022
+ }
1023
+ } else {
1024
+ const isEthDest = isETHAddress(swap.destToken);
1025
+
1026
+ if (isEthDest) {
1027
+ if (
1028
+ this.anyDexOnSwapDoesntNeedWrapNative(
1029
+ priceRoute,
1030
+ swap,
1031
+ exchangeParams,
1032
+ )
1033
+ ) {
1034
+ flag = Flag.INSERT_FROM_AMOUNT_CHECK_ETH_BALANCE_AFTER_SWAP; // 7
1035
+ }
1036
+ }
1037
+ }
1038
+
1039
+ return flag;
1040
+ }
1041
+
1042
+ protected buildSingleSwapCallData(
1043
+ params: SingleSwapCallDataParams<Executor02SingleSwapCallDataParams>,
1044
+ ): string {
1045
+ const {
1046
+ priceRoute,
1047
+ exchangeParams,
1048
+ routeIndex,
1049
+ swapIndex,
1050
+ flags,
1051
+ maybeWethCallData,
1052
+ wrapToSwapMap,
1053
+ wrapToSwapExchangeMap,
1054
+ swap,
1055
+ } = params;
1056
+ const isLastSwap =
1057
+ swapIndex === priceRoute.bestRoute[routeIndex].swaps.length - 1;
1058
+ const isMegaSwap = priceRoute.bestRoute.length > 1;
1059
+ const isMultiSwap =
1060
+ !isMegaSwap && priceRoute.bestRoute[routeIndex].swaps.length > 1;
1061
+
1062
+ const { swapExchanges } = swap;
1063
+
1064
+ const applyVerticalBranching = this.doesSwapNeedToApplyVerticalBranching(
1065
+ priceRoute,
1066
+ routeIndex,
1067
+ swap,
1068
+ );
1069
+
1070
+ const anyDexOnSwapDoesntNeedWrapNative =
1071
+ this.anyDexOnSwapDoesntNeedWrapNative(priceRoute, swap, exchangeParams);
1072
+
1073
+ const needToAppendWrapCallData =
1074
+ isETHAddress(swap.destToken) &&
1075
+ anyDexOnSwapDoesntNeedWrapNative &&
1076
+ !isLastSwap &&
1077
+ maybeWethCallData?.deposit;
1078
+
1079
+ let swapCallData = swapExchanges.reduce(
1080
+ (acc, swapExchange, swapExchangeIndex) => {
1081
+ return hexConcat([
1082
+ acc,
1083
+ this.buildSingleSwapExchangeCallData(
1084
+ priceRoute,
1085
+ routeIndex,
1086
+ swapIndex,
1087
+ swapExchangeIndex,
1088
+ exchangeParams,
1089
+ flags,
1090
+ wrapToSwapExchangeMap,
1091
+ !wrapToSwapMap[swapIndex - 1],
1092
+ wrapToSwapMap[swapIndex - 1],
1093
+ maybeWethCallData,
1094
+ swap.swapExchanges.length > 1,
1095
+ applyVerticalBranching,
1096
+ ),
1097
+ ]);
1098
+ },
1099
+ '0x',
1100
+ );
1101
+
1102
+ if (needToAppendWrapCallData) {
1103
+ wrapToSwapMap[swapIndex] = true;
1104
+ }
1105
+
1106
+ if (!isMultiSwap && !isMegaSwap) {
1107
+ return needToAppendWrapCallData
1108
+ ? this.appendWrapEthCallData(swapCallData, maybeWethCallData)
1109
+ : swapCallData;
1110
+ }
1111
+
1112
+ if (applyVerticalBranching) {
1113
+ const vertBranchingCallData = this.buildVerticalBranchingCallData(
1114
+ priceRoute,
1115
+ routeIndex,
1116
+ exchangeParams,
1117
+ swap,
1118
+ swapCallData,
1119
+ this.buildVerticalBranchingFlag(
1120
+ priceRoute,
1121
+ swap,
1122
+ exchangeParams,
1123
+ routeIndex,
1124
+ swapIndex,
1125
+ ),
1126
+ );
1127
+
1128
+ return needToAppendWrapCallData
1129
+ ? this.appendWrapEthCallData(
1130
+ vertBranchingCallData,
1131
+ maybeWethCallData,
1132
+ true,
1133
+ )
1134
+ : vertBranchingCallData;
1135
+ }
1136
+
1137
+ return needToAppendWrapCallData
1138
+ ? this.appendWrapEthCallData(swapCallData, maybeWethCallData)
1139
+ : swapCallData;
1140
+ }
1141
+
1142
+ protected buildSingleRouteCallData(
1143
+ priceRoute: OptimalRate,
1144
+ exchangeParams: DexExchangeBuildParam[],
1145
+ route: OptimalRoute,
1146
+ routeIndex: number,
1147
+ flags: { approves: Flag[]; dexes: Flag[]; wrap: Flag },
1148
+ sender: string,
1149
+ maybeWethCallData?: DepositWithdrawReturn,
1150
+ ): string {
1151
+ const isMegaSwap = priceRoute.bestRoute.length > 1;
1152
+
1153
+ const { swaps } = route;
1154
+
1155
+ const appendedWrapToSwapExchangeMap = {};
1156
+ const addedWrapToSwapMap = {};
1157
+ const callData = swaps.reduce<string>(
1158
+ (swapAcc, swap, swapIndex) =>
1159
+ hexConcat([
1160
+ swapAcc,
1161
+ this.buildSingleSwapCallData({
1162
+ priceRoute,
1163
+ exchangeParams,
1164
+ routeIndex,
1165
+ swapIndex,
1166
+ flags,
1167
+ sender,
1168
+ wrapToSwapExchangeMap: appendedWrapToSwapExchangeMap,
1169
+ wrapToSwapMap: addedWrapToSwapMap,
1170
+ maybeWethCallData,
1171
+ swap,
1172
+ index: 0,
1173
+ }),
1174
+ ]),
1175
+ '0x',
1176
+ );
1177
+
1178
+ const routeDoesntNeedToAddMultiSwapMetadata =
1179
+ route.swaps.length === 1 &&
1180
+ route.swaps[0].swapExchanges.length !== 1 &&
1181
+ !this.doesSwapNeedToApplyVerticalBranching(
1182
+ priceRoute,
1183
+ routeIndex,
1184
+ route.swaps[0],
1185
+ );
1186
+
1187
+ if (isMegaSwap && !routeDoesntNeedToAddMultiSwapMetadata) {
1188
+ return this.addMultiSwapMetadata(
1189
+ priceRoute,
1190
+ exchangeParams,
1191
+ callData,
1192
+ route.percent,
1193
+ route.swaps[0],
1194
+ NOT_EXISTING_EXCHANGE_PARAM_INDEX,
1195
+ Object.values(addedWrapToSwapMap).includes(true),
1196
+ );
1197
+ }
1198
+
1199
+ return callData;
1200
+ }
1201
+
1202
+ private doesRouteNeedsRootWrapEth(
1203
+ priceRoute: OptimalRate,
1204
+ exchangeParams: DexExchangeBuildParam[],
1205
+ ): boolean {
1206
+ if (!isETHAddress(priceRoute.srcToken)) {
1207
+ return false;
1208
+ }
1209
+
1210
+ const res = priceRoute.bestRoute.every((route, routeIndex) => {
1211
+ const firstSwap = route.swaps[0];
1212
+ const eachDexOnSwapNeedsWrapNative = this.eachDexOnSwapNeedsWrapNative(
1213
+ priceRoute,
1214
+ firstSwap,
1215
+ exchangeParams,
1216
+ );
1217
+
1218
+ return eachDexOnSwapNeedsWrapNative;
1219
+ });
1220
+
1221
+ return res;
1222
+ }
1223
+
1224
+ private doesRouteNeedsRootUnwrapEth(
1225
+ priceRoute: OptimalRate,
1226
+ exchangeParams: DexExchangeParam[],
1227
+ ): boolean {
1228
+ if (!isETHAddress(priceRoute.destToken)) {
1229
+ return false;
1230
+ }
1231
+
1232
+ const res = priceRoute.bestRoute.some((route, routeIndex) => {
1233
+ const lastSwap = route.swaps[route.swaps.length - 1];
1234
+ const anyDexOnSwapNeedsWrapNative = this.anyDexOnSwapNeedsWrapNative(
1235
+ priceRoute,
1236
+ lastSwap,
1237
+ exchangeParams,
1238
+ );
1239
+
1240
+ return anyDexOnSwapNeedsWrapNative;
1241
+ });
1242
+
1243
+ return res;
1244
+ }
1245
+
1246
+ public getAddress(): string {
1247
+ return this.dexHelper.config.data.executorsAddresses![Executors.TWO];
1248
+ }
1249
+
1250
+ public buildByteCode(
1251
+ priceRoute: OptimalRate,
1252
+ exchangeParams: DexExchangeBuildParam[],
1253
+ sender: string,
1254
+ maybeWethCallData?: DepositWithdrawReturn,
1255
+ ): string {
1256
+ const isMegaSwap = priceRoute.bestRoute.length > 1;
1257
+ const isMultiSwap = !isMegaSwap && priceRoute.bestRoute[0].swaps.length > 1;
1258
+
1259
+ const needWrapEth =
1260
+ maybeWethCallData?.deposit && isETHAddress(priceRoute.srcToken);
1261
+ const needUnwrapEth =
1262
+ maybeWethCallData?.withdraw && isETHAddress(priceRoute.destToken);
1263
+ const needSendNativeEth = isETHAddress(priceRoute.destToken);
1264
+ const routeNeedsRootWrapEth = this.doesRouteNeedsRootWrapEth(
1265
+ priceRoute,
1266
+ exchangeParams,
1267
+ );
1268
+ const routeNeedsRootUnwrapEth = this.doesRouteNeedsRootUnwrapEth(
1269
+ priceRoute,
1270
+ exchangeParams,
1271
+ );
1272
+
1273
+ const flags = this.buildFlags(
1274
+ priceRoute,
1275
+ exchangeParams,
1276
+ maybeWethCallData,
1277
+ );
1278
+
1279
+ let swapsCalldata = priceRoute.bestRoute.reduce<string>(
1280
+ (routeAcc, route, routeIndex) =>
1281
+ hexConcat([
1282
+ routeAcc,
1283
+ this.buildSingleRouteCallData(
1284
+ priceRoute,
1285
+ exchangeParams,
1286
+ route,
1287
+ routeIndex,
1288
+ flags,
1289
+ sender,
1290
+ maybeWethCallData,
1291
+ ),
1292
+ ]),
1293
+ '0x',
1294
+ );
1295
+
1296
+ if (isMegaSwap && (needWrapEth || needUnwrapEth)) {
1297
+ const lastPriceRoute =
1298
+ priceRoute.bestRoute[priceRoute.bestRoute.length - 1];
1299
+ swapsCalldata = this.buildVerticalBranchingCallData(
1300
+ priceRoute,
1301
+ priceRoute.bestRoute.length - 1,
1302
+ exchangeParams,
1303
+ lastPriceRoute.swaps[lastPriceRoute.swaps.length - 1],
1304
+ swapsCalldata,
1305
+ needWrapEth
1306
+ ? Flag.DONT_INSERT_FROM_AMOUNT_DONT_CHECK_BALANCE_AFTER_SWAP // 0
1307
+ : Flag.DONT_INSERT_FROM_AMOUNT_CHECK_SRC_TOKEN_BALANCE_AFTER_SWAP, // 8
1308
+ );
1309
+ }
1310
+
1311
+ // ETH wrap
1312
+ if (needWrapEth && routeNeedsRootWrapEth) {
1313
+ let depositCallData = this.buildWrapEthCallData(
1314
+ this.dexHelper.config.data.wrappedNativeTokenAddress,
1315
+ maybeWethCallData.deposit!.calldata,
1316
+ Flag.SEND_ETH_EQUAL_TO_FROM_AMOUNT_DONT_CHECK_BALANCE_AFTER_SWAP, // 9
1317
+ );
1318
+
1319
+ if (!(isMegaSwap || isMultiSwap)) {
1320
+ const swap = priceRoute.bestRoute[0].swaps[0];
1321
+ const percent = exchangeParams.every(ep => ep.needWrapNative)
1322
+ ? 100
1323
+ : swap.swapExchanges
1324
+ .filter((se, index) => {
1325
+ return exchangeParams[index].needWrapNative;
1326
+ })
1327
+ .reduce<number>((acc, se) => {
1328
+ acc += se.percent;
1329
+ return acc;
1330
+ }, 0);
1331
+
1332
+ depositCallData = solidityPack(
1333
+ ['bytes16', 'bytes16', 'bytes'],
1334
+ [
1335
+ hexZeroPad(hexlify(hexDataLength(depositCallData)), 16),
1336
+ hexZeroPad(hexlify(100 * percent), 16),
1337
+ depositCallData,
1338
+ ],
1339
+ );
1340
+ }
1341
+
1342
+ swapsCalldata = hexConcat([depositCallData, swapsCalldata]);
1343
+ }
1344
+
1345
+ // ETH unwrap, only for multiswaps and mega swaps
1346
+ if (
1347
+ needUnwrapEth &&
1348
+ routeNeedsRootUnwrapEth &&
1349
+ (isMultiSwap || isMegaSwap)
1350
+ ) {
1351
+ const withdrawCallData = this.buildUnwrapEthCallData(
1352
+ this.dexHelper.config.data.wrappedNativeTokenAddress,
1353
+ maybeWethCallData.withdraw!.calldata,
1354
+ );
1355
+ swapsCalldata = hexConcat([swapsCalldata, withdrawCallData]);
1356
+ }
1357
+
1358
+ // Special flag (send native) calldata, only for multiswaps and mega swaps
1359
+ if (
1360
+ needSendNativeEth &&
1361
+ routeNeedsRootUnwrapEth &&
1362
+ (isMultiSwap || isMegaSwap)
1363
+ ) {
1364
+ const finalSpecialFlagCalldata = this.buildFinalSpecialFlagCalldata();
1365
+ swapsCalldata = hexConcat([swapsCalldata, finalSpecialFlagCalldata]);
1366
+ }
1367
+
1368
+ if (((needWrapEth || needUnwrapEth) && isMegaSwap) || isMultiSwap) {
1369
+ swapsCalldata = this.addMultiSwapMetadata(
1370
+ priceRoute,
1371
+ exchangeParams,
1372
+ swapsCalldata,
1373
+ SWAP_EXCHANGE_100_PERCENTAGE,
1374
+ priceRoute.bestRoute[0].swaps[0],
1375
+ NOT_EXISTING_EXCHANGE_PARAM_INDEX,
1376
+ false,
1377
+ );
1378
+ }
1379
+
1380
+ return solidityPack(
1381
+ ['bytes32', 'bytes', 'bytes'],
1382
+ [
1383
+ hexZeroPad(hexlify(32), 32), // calldata offset
1384
+ hexZeroPad(
1385
+ hexlify(hexDataLength(swapsCalldata) + BYTES_64_LENGTH), // calldata length (64 bytes = bytes12(0) + msg.sender)
1386
+ 32,
1387
+ ),
1388
+ swapsCalldata, // calldata
1389
+ ],
1390
+ );
1391
+ }
1392
+ }