@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.
- package/README.md +377 -377
- package/build/config.js +9 -5
- package/build/config.js.map +1 -1
- package/build/dex/algebra/algebra.js +25 -25
- package/build/dex/balancer-v1/balancer-v1.js +34 -34
- package/build/dex/balancer-v2/balancer-v2.d.ts +1 -1
- package/build/dex/balancer-v2/balancer-v2.js +65 -78
- package/build/dex/balancer-v2/balancer-v2.js.map +1 -1
- package/build/dex/camelot/camelot.js +27 -27
- package/build/dex/kyberdmm/kyberdmm.js +27 -27
- package/build/dex/pancakeswap-v3/pancakeswap-v3.js +25 -25
- package/build/dex/solidly/solidly.js +27 -27
- package/build/dex/solidly-v3/solidly-v3.js +25 -25
- package/build/dex/swaap-v2/config.js +15 -0
- package/build/dex/swaap-v2/config.js.map +1 -1
- package/build/dex/uniswap-v2/uniswap-v2.js +26 -26
- package/build/dex/uniswap-v3/uniswap-v3.js +25 -25
- package/funding.json +5 -5
- package/package.json +1 -1
- package/src/config.ts +500 -496
- package/src/constants.ts +67 -67
- package/src/dex/aave-v2/aave-v2.ts +409 -409
- package/src/dex/aave-v3/aave-v3.ts +320 -320
- package/src/dex/algebra/algebra.ts +1180 -1180
- package/src/dex/angle-staked-stable/angle-staked-stable.ts +347 -347
- package/src/dex/augustus-rfq.ts +180 -180
- package/src/dex/bProtocol/bProtocol.ts +108 -108
- package/src/dex/balancer-v1/balancer-v1.ts +572 -572
- package/src/dex/balancer-v2/balancer-v2-e2e.test.ts +1699 -1699
- package/src/dex/balancer-v2/balancer-v2.ts +1669 -1701
- package/src/dex/bancor/bancor.ts +128 -128
- package/src/dex/camelot/camelot.ts +873 -873
- package/src/dex/compound/compound.ts +107 -107
- package/src/dex/curve-v1/config.ts +1179 -1179
- package/src/dex/curve-v1/curve-v1-e2e.test.ts +507 -507
- package/src/dex/curve-v1/curve-v1.ts +1315 -1315
- package/src/dex/curve-v1-factory/config.ts +1018 -1018
- package/src/dex/curve-v1-factory/curve-v1-factory.ts +1288 -1288
- package/src/dex/curve-v2/curve-v2.ts +440 -440
- package/src/dex/dexalot/dexalot.ts +1100 -1100
- package/src/dex/dodo-v1/dodo-v1.ts +132 -132
- package/src/dex/dodo-v2.ts +266 -266
- package/src/dex/etherfi.ts +239 -239
- package/src/dex/generic-rfq/generic-rfq.ts +431 -431
- package/src/dex/gmx/gmx.ts +321 -321
- package/src/dex/hashflow/hashflow.ts +1135 -1135
- package/src/dex/idex.ts +254 -254
- package/src/dex/index.ts +357 -357
- package/src/dex/jarvis-v6/jarvis-v6.ts +509 -509
- package/src/dex/jarvis.ts +262 -262
- package/src/dex/kyberdmm/kyberdmm.ts +649 -649
- package/src/dex/lido/lido.ts +110 -110
- package/src/dex/maker-psm/maker-psm.ts +592 -592
- package/src/dex/maverick-v1/maverick-v1.ts +537 -537
- package/src/dex/nerve/config.ts +635 -635
- package/src/dex/nerve/nerve-e2e.test.ts +731 -731
- package/src/dex/nerve/nerve-events.test.ts +911 -911
- package/src/dex/nerve/nerve.ts +467 -467
- package/src/dex/onebit/onebit.ts +113 -113
- package/src/dex/pancakeswap-v3/pancakeswap-v3.ts +1150 -1150
- package/src/dex/paraswap-limit-orders/config.ts +60 -60
- package/src/dex/paraswap-limit-orders/paraswap-limit-orders-e2e.test.ts +137 -137
- package/src/dex/paraswap-limit-orders/paraswap-limit-orders-integration.test.ts +328 -328
- package/src/dex/paraswap-limit-orders/paraswap-limit-orders.ts +631 -631
- package/src/dex/polygon-migrator/polygon-migrator.ts +194 -194
- package/src/dex/quick-perps/quick-perps.ts +337 -337
- package/src/dex/quickswap/config.ts +16 -16
- package/src/dex/quickswap/quickswap-e2e.test.ts +384 -384
- package/src/dex/smoothy/smoothy.ts +110 -110
- package/src/dex/solidly/config.ts +225 -225
- package/src/dex/solidly/solidly-e2e.test.ts +1421 -1421
- package/src/dex/solidly/solidly-integration.test.ts +1753 -1753
- package/src/dex/solidly/solidly.ts +687 -687
- package/src/dex/solidly-v3/solidly-v3.ts +968 -968
- package/src/dex/spark/spark.ts +284 -284
- package/src/dex/stable-pool/stable-pool.ts +129 -129
- package/src/dex/swaap-v2/config.ts +15 -0
- package/src/dex/swaap-v2/swaap-v2-e2e.test.ts +233 -17
- package/src/dex/swaap-v2/swaap-v2-integration.test.ts +1 -1
- package/src/dex/swaap-v2/swaap-v2.ts +1056 -1056
- package/src/dex/swell/swell.ts +312 -312
- package/src/dex/synthetix/synthetix.ts +420 -420
- package/src/dex/trader-joe-v2.1/trader-joe-v2.1.ts +180 -180
- package/src/dex/uniswap-v2/config.ts +637 -637
- package/src/dex/uniswap-v2/constants.ts +58 -58
- package/src/dex/uniswap-v2/uniswap-v2-e2e-avalanche.test.ts +1257 -1257
- package/src/dex/uniswap-v2/uniswap-v2-e2e-base.test.ts +42 -42
- package/src/dex/uniswap-v2/uniswap-v2-e2e-bsc.test.ts +1295 -1295
- package/src/dex/uniswap-v2/uniswap-v2-e2e-mainnet.test.ts +1834 -1834
- package/src/dex/uniswap-v2/uniswap-v2-e2e-polygon.test.ts +871 -871
- package/src/dex/uniswap-v2/uniswap-v2.ts +1117 -1117
- package/src/dex/uniswap-v3/uniswap-v3.ts +1501 -1501
- package/src/dex/uniswap-v3.ts +242 -242
- package/src/dex/weth/config.ts +73 -73
- package/src/dex/weth/weth.ts +230 -230
- package/src/dex/wombat/wombat.ts +467 -467
- package/src/dex/woo-fi-v2/woo-fi-v2.ts +529 -529
- package/src/dex/wsteth/wsteth.ts +304 -304
- package/src/dex/zerox/index.ts +328 -328
- package/src/executor/Executor01BytecodeBuilder.ts +474 -474
- package/src/executor/Executor02BytecodeBuilder.ts +1392 -1392
- package/src/executor/ExecutorBytecodeBuilder.ts +390 -390
- package/src/executor/ExecutorDetector.ts +131 -131
- package/src/executor/constants.ts +66 -66
- package/src/executor/executor01-bytecode-builder-e2e.test.ts +1478 -1478
- package/src/executor/utils.ts +62 -62
- package/src/router/simpleswap.ts +366 -366
- package/src/types.ts +342 -342
- package/tests/constants-e2e.ts +1632 -1622
- 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
|
+
}
|