@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,1117 +1,1117 @@
|
|
|
1
|
-
import { AbiCoder, Interface } from '@ethersproject/abi';
|
|
2
|
-
import { pack } from '@ethersproject/solidity';
|
|
3
|
-
import _ from 'lodash';
|
|
4
|
-
import { AsyncOrSync, DeepReadonly } from 'ts-essentials';
|
|
5
|
-
import erc20ABI from '../../abi/erc20.json';
|
|
6
|
-
import { StatefulEventSubscriber } from '../../stateful-event-subscriber';
|
|
7
|
-
import {
|
|
8
|
-
AdapterExchangeParam,
|
|
9
|
-
Address,
|
|
10
|
-
ExchangePrices,
|
|
11
|
-
PoolPrices,
|
|
12
|
-
Log,
|
|
13
|
-
Logger,
|
|
14
|
-
PoolLiquidity,
|
|
15
|
-
SimpleExchangeParam,
|
|
16
|
-
Token,
|
|
17
|
-
TxInfo,
|
|
18
|
-
TransferFeeParams,
|
|
19
|
-
DexExchangeParam,
|
|
20
|
-
} from '../../types';
|
|
21
|
-
import {
|
|
22
|
-
UniswapData,
|
|
23
|
-
UniswapDataLegacy,
|
|
24
|
-
UniswapParam,
|
|
25
|
-
UniswapPool,
|
|
26
|
-
UniswapV2Data,
|
|
27
|
-
UniswapV2Functions,
|
|
28
|
-
UniswapV2FunctionsV6,
|
|
29
|
-
UniswapV2ParamsDirect,
|
|
30
|
-
UniswapV2ParamsDirectBase,
|
|
31
|
-
UniswapV2PoolOrderedParams,
|
|
32
|
-
} from './types';
|
|
33
|
-
import { IDex } from '../idex';
|
|
34
|
-
import {
|
|
35
|
-
DEST_TOKEN_PARASWAP_TRANSFERS,
|
|
36
|
-
ETHER_ADDRESS,
|
|
37
|
-
Network,
|
|
38
|
-
NULL_ADDRESS,
|
|
39
|
-
SRC_TOKEN_PARASWAP_TRANSFERS,
|
|
40
|
-
SUBGRAPH_TIMEOUT,
|
|
41
|
-
} from '../../constants';
|
|
42
|
-
import * as CALLDATA_GAS_COST from '../../calldata-gas-cost';
|
|
43
|
-
import { SimpleExchange } from '../simple-exchange';
|
|
44
|
-
import { NumberAsString, SwapSide } from '@paraswap/core';
|
|
45
|
-
import { IDexHelper } from '../../dex-helper';
|
|
46
|
-
import {
|
|
47
|
-
getDexKeysWithNetwork,
|
|
48
|
-
isETHAddress,
|
|
49
|
-
prependWithOx,
|
|
50
|
-
getBigIntPow,
|
|
51
|
-
uuidToBytes16,
|
|
52
|
-
} from '../../utils';
|
|
53
|
-
import uniswapV2ABI from '../../abi/uniswap-v2/uniswap-v2-pool.json';
|
|
54
|
-
import uniswapV2factoryABI from '../../abi/uniswap-v2/uniswap-v2-factory.json';
|
|
55
|
-
import ParaSwapABI from '../../abi/IParaswap.json';
|
|
56
|
-
import UniswapV2ExchangeRouterABI from '../../abi/UniswapV2ExchangeRouter.json';
|
|
57
|
-
import { Contract } from 'web3-eth-contract';
|
|
58
|
-
import { UniswapV2Config, Adapters } from './config';
|
|
59
|
-
import { Uniswapv2ConstantProductPool } from './uniswap-v2-constant-product-pool';
|
|
60
|
-
import { applyTransferFee } from '../../lib/token-transfer-fee';
|
|
61
|
-
import _rebaseTokens from '../../rebase-tokens.json';
|
|
62
|
-
import { Flag, SpecialDex } from '../../executor/types';
|
|
63
|
-
import {
|
|
64
|
-
hexZeroPad,
|
|
65
|
-
hexlify,
|
|
66
|
-
solidityPack,
|
|
67
|
-
hexDataLength,
|
|
68
|
-
id,
|
|
69
|
-
hexConcat,
|
|
70
|
-
} from 'ethers/lib/utils';
|
|
71
|
-
import { BigNumber } from 'ethers';
|
|
72
|
-
|
|
73
|
-
const rebaseTokens = _rebaseTokens as { chainId: number; address: string }[];
|
|
74
|
-
|
|
75
|
-
const rebaseTokensSetsByChain = rebaseTokens.reduce<{
|
|
76
|
-
[chainId: number]: Set<string>;
|
|
77
|
-
}>((acc, curr) => {
|
|
78
|
-
if (!acc[curr.chainId]) {
|
|
79
|
-
acc[curr.chainId] = new Set();
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
acc[curr.chainId].add(curr.address.toLowerCase());
|
|
83
|
-
|
|
84
|
-
return acc;
|
|
85
|
-
}, {});
|
|
86
|
-
|
|
87
|
-
const DefaultUniswapV2PoolGasCost = 90 * 1000;
|
|
88
|
-
|
|
89
|
-
export const RESERVE_LIMIT = 2n ** 112n - 1n;
|
|
90
|
-
|
|
91
|
-
const LogCallTopics = [
|
|
92
|
-
'0x1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1', // event Sync(uint112 reserve0, uint112 reserve1) // uni-V2 and most forks
|
|
93
|
-
'0xcf2aa50876cdfbb541206f89af0ee78d44a2abf8d328e37fa4917f982149848a', // event Sync(uint256 reserve0, uint256 reserve1) // commonly seen in solidly & forks
|
|
94
|
-
];
|
|
95
|
-
|
|
96
|
-
interface UniswapV2PoolState {
|
|
97
|
-
reserves0: string;
|
|
98
|
-
reserves1: string;
|
|
99
|
-
feeCode: number;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const uniswapV2PoolIface = new Interface(uniswapV2ABI);
|
|
103
|
-
const erc20iface = new Interface(erc20ABI);
|
|
104
|
-
const coder = new AbiCoder();
|
|
105
|
-
|
|
106
|
-
export const directUniswapFunctionName = [
|
|
107
|
-
UniswapV2Functions.swapOnUniswap,
|
|
108
|
-
UniswapV2Functions.buyOnUniswap,
|
|
109
|
-
UniswapV2Functions.swapOnUniswapFork,
|
|
110
|
-
UniswapV2Functions.buyOnUniswapFork,
|
|
111
|
-
UniswapV2Functions.swapOnUniswapV2Fork,
|
|
112
|
-
UniswapV2Functions.buyOnUniswapV2Fork,
|
|
113
|
-
];
|
|
114
|
-
|
|
115
|
-
export const directUniswapFunctionNameV6 = [
|
|
116
|
-
UniswapV2FunctionsV6.swap,
|
|
117
|
-
UniswapV2FunctionsV6.buy,
|
|
118
|
-
];
|
|
119
|
-
|
|
120
|
-
export interface UniswapV2Pair {
|
|
121
|
-
token0: Token;
|
|
122
|
-
token1: Token;
|
|
123
|
-
exchange?: Address;
|
|
124
|
-
pool?: UniswapV2EventPool;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
export class UniswapV2EventPool extends StatefulEventSubscriber<UniswapV2PoolState> {
|
|
128
|
-
decoder = (log: Log) => this.iface.parseLog(log);
|
|
129
|
-
|
|
130
|
-
constructor(
|
|
131
|
-
parentName: string,
|
|
132
|
-
protected dexHelper: IDexHelper,
|
|
133
|
-
private poolAddress: Address,
|
|
134
|
-
private token0: Token,
|
|
135
|
-
private token1: Token,
|
|
136
|
-
// feeCode is ignored if DynamicFees is set to true
|
|
137
|
-
private feeCode: number,
|
|
138
|
-
logger: Logger,
|
|
139
|
-
private dynamicFees = false,
|
|
140
|
-
// feesMultiCallData is only used if dynamicFees is set to true
|
|
141
|
-
private feesMultiCallEntry?: { target: Address; callData: string },
|
|
142
|
-
private feesMultiCallDecoder?: (values: any[]) => number,
|
|
143
|
-
private iface: Interface = uniswapV2PoolIface,
|
|
144
|
-
) {
|
|
145
|
-
super(
|
|
146
|
-
parentName,
|
|
147
|
-
(token0.symbol || token0.address) +
|
|
148
|
-
'-' +
|
|
149
|
-
(token1.symbol || token1.address) +
|
|
150
|
-
' pool',
|
|
151
|
-
dexHelper,
|
|
152
|
-
logger,
|
|
153
|
-
);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
protected processLog(
|
|
157
|
-
state: DeepReadonly<UniswapV2PoolState>,
|
|
158
|
-
log: Readonly<Log>,
|
|
159
|
-
): AsyncOrSync<DeepReadonly<UniswapV2PoolState> | null> {
|
|
160
|
-
if (!LogCallTopics.includes(log.topics[0])) return null;
|
|
161
|
-
|
|
162
|
-
const event = this.decoder(log);
|
|
163
|
-
switch (event.name) {
|
|
164
|
-
case 'Sync':
|
|
165
|
-
return {
|
|
166
|
-
reserves0: event.args.reserve0.toString(),
|
|
167
|
-
reserves1: event.args.reserve1.toString(),
|
|
168
|
-
feeCode: state.feeCode,
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
return null;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
async generateState(
|
|
175
|
-
blockNumber: number | 'latest' = 'latest',
|
|
176
|
-
): Promise<DeepReadonly<UniswapV2PoolState>> {
|
|
177
|
-
let calldata = [
|
|
178
|
-
{
|
|
179
|
-
target: this.poolAddress,
|
|
180
|
-
callData: this.iface.encodeFunctionData('getReserves', []),
|
|
181
|
-
},
|
|
182
|
-
];
|
|
183
|
-
|
|
184
|
-
if (this.dynamicFees) {
|
|
185
|
-
calldata.push(this.feesMultiCallEntry!);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
const data: { returnData: any[] } =
|
|
189
|
-
await this.dexHelper.multiContract.methods
|
|
190
|
-
.aggregate(calldata)
|
|
191
|
-
.call({}, blockNumber);
|
|
192
|
-
|
|
193
|
-
const decodedData = coder.decode(
|
|
194
|
-
['uint112', 'uint112', 'uint32'],
|
|
195
|
-
data.returnData[0],
|
|
196
|
-
);
|
|
197
|
-
|
|
198
|
-
return {
|
|
199
|
-
reserves0: decodedData[0].toString(),
|
|
200
|
-
reserves1: decodedData[1].toString(),
|
|
201
|
-
feeCode: this.dynamicFees
|
|
202
|
-
? this.feesMultiCallDecoder!(data.returnData[1])
|
|
203
|
-
: this.feeCode,
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
function encodePools(
|
|
209
|
-
pools: UniswapPool[],
|
|
210
|
-
feeFactor: number,
|
|
211
|
-
): NumberAsString[] {
|
|
212
|
-
return pools.map(({ fee, direction, address }) => {
|
|
213
|
-
return (
|
|
214
|
-
(BigInt(feeFactor - fee) << 161n) +
|
|
215
|
-
((direction ? 0n : 1n) << 160n) +
|
|
216
|
-
BigInt(address)
|
|
217
|
-
).toString();
|
|
218
|
-
});
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
export class UniswapV2
|
|
222
|
-
extends SimpleExchange
|
|
223
|
-
implements IDex<UniswapV2Data, UniswapParam | UniswapV2ParamsDirect>
|
|
224
|
-
{
|
|
225
|
-
pairs: { [key: string]: UniswapV2Pair } = {};
|
|
226
|
-
feeFactor = 10000;
|
|
227
|
-
factory: Contract;
|
|
228
|
-
|
|
229
|
-
routerInterface: Interface;
|
|
230
|
-
exchangeRouterInterface: Interface;
|
|
231
|
-
|
|
232
|
-
needWrapNative = true;
|
|
233
|
-
|
|
234
|
-
logger: Logger;
|
|
235
|
-
|
|
236
|
-
static directFunctionName = directUniswapFunctionName;
|
|
237
|
-
static directFunctionNameV6 = directUniswapFunctionNameV6;
|
|
238
|
-
|
|
239
|
-
readonly hasConstantPriceLargeAmounts = false;
|
|
240
|
-
readonly isFeeOnTransferSupported: boolean = true;
|
|
241
|
-
readonly SRC_TOKEN_DEX_TRANSFERS = 1;
|
|
242
|
-
readonly DEST_TOKEN_DEX_TRANSFERS = 1;
|
|
243
|
-
|
|
244
|
-
public static dexKeysWithNetwork: { key: string; networks: Network[] }[] =
|
|
245
|
-
getDexKeysWithNetwork(UniswapV2Config);
|
|
246
|
-
|
|
247
|
-
constructor(
|
|
248
|
-
protected network: Network,
|
|
249
|
-
dexKey: string,
|
|
250
|
-
protected dexHelper: IDexHelper,
|
|
251
|
-
protected isDynamicFees = false,
|
|
252
|
-
protected factoryAddress: Address = UniswapV2Config[dexKey][network]
|
|
253
|
-
.factoryAddress,
|
|
254
|
-
protected subgraphURL: string | undefined = UniswapV2Config[dexKey] &&
|
|
255
|
-
UniswapV2Config[dexKey][network].subgraphURL,
|
|
256
|
-
protected initCode: string = UniswapV2Config[dexKey][network].initCode,
|
|
257
|
-
// feeCode is ignored when isDynamicFees is set to true
|
|
258
|
-
protected feeCode: number = UniswapV2Config[dexKey][network].feeCode,
|
|
259
|
-
protected poolGasCost: number = (UniswapV2Config[dexKey] &&
|
|
260
|
-
UniswapV2Config[dexKey][network].poolGasCost) ??
|
|
261
|
-
DefaultUniswapV2PoolGasCost,
|
|
262
|
-
protected decoderIface: Interface = uniswapV2PoolIface,
|
|
263
|
-
protected adapters = (UniswapV2Config[dexKey] &&
|
|
264
|
-
UniswapV2Config[dexKey][network].adapters) ??
|
|
265
|
-
Adapters[network],
|
|
266
|
-
protected router = (UniswapV2Config[dexKey] &&
|
|
267
|
-
UniswapV2Config[dexKey][network].router) ??
|
|
268
|
-
dexHelper.config.data.uniswapV2ExchangeRouterAddress,
|
|
269
|
-
) {
|
|
270
|
-
super(dexHelper, dexKey);
|
|
271
|
-
this.logger = dexHelper.getLogger(dexKey);
|
|
272
|
-
|
|
273
|
-
this.factory = new dexHelper.web3Provider.eth.Contract(
|
|
274
|
-
uniswapV2factoryABI as any,
|
|
275
|
-
factoryAddress,
|
|
276
|
-
);
|
|
277
|
-
|
|
278
|
-
this.routerInterface = new Interface(ParaSwapABI);
|
|
279
|
-
this.exchangeRouterInterface = new Interface(UniswapV2ExchangeRouterABI);
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
// getFeesMultiCallData should be override
|
|
283
|
-
// when isDynamicFees is set to true
|
|
284
|
-
protected getFeesMultiCallData(pair: UniswapV2Pair):
|
|
285
|
-
| undefined
|
|
286
|
-
| {
|
|
287
|
-
callEntry: { target: Address; callData: string };
|
|
288
|
-
callDecoder: (values: any[]) => number;
|
|
289
|
-
} {
|
|
290
|
-
return undefined;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
protected async addPool(
|
|
294
|
-
pair: UniswapV2Pair,
|
|
295
|
-
reserves0: string,
|
|
296
|
-
reserves1: string,
|
|
297
|
-
feeCode: number,
|
|
298
|
-
blockNumber: number,
|
|
299
|
-
) {
|
|
300
|
-
const { callEntry, callDecoder } = this.getFeesMultiCallData(pair) || {};
|
|
301
|
-
pair.pool = new UniswapV2EventPool(
|
|
302
|
-
this.dexKey,
|
|
303
|
-
this.dexHelper,
|
|
304
|
-
pair.exchange!,
|
|
305
|
-
pair.token0,
|
|
306
|
-
pair.token1,
|
|
307
|
-
feeCode,
|
|
308
|
-
this.logger,
|
|
309
|
-
this.isDynamicFees,
|
|
310
|
-
callEntry,
|
|
311
|
-
callDecoder,
|
|
312
|
-
this.decoderIface,
|
|
313
|
-
);
|
|
314
|
-
pair.pool.addressesSubscribed.push(pair.exchange!);
|
|
315
|
-
|
|
316
|
-
await pair.pool.initialize(blockNumber, {
|
|
317
|
-
state: { reserves0, reserves1, feeCode },
|
|
318
|
-
});
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
async getBuyPrice(
|
|
322
|
-
priceParams: UniswapV2PoolOrderedParams,
|
|
323
|
-
destAmount: bigint,
|
|
324
|
-
): Promise<bigint> {
|
|
325
|
-
return Uniswapv2ConstantProductPool.getBuyPrice(
|
|
326
|
-
priceParams,
|
|
327
|
-
destAmount,
|
|
328
|
-
this.feeFactor,
|
|
329
|
-
);
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
async getSellPrice(
|
|
333
|
-
priceParams: UniswapV2PoolOrderedParams,
|
|
334
|
-
srcAmount: bigint,
|
|
335
|
-
): Promise<bigint> {
|
|
336
|
-
return Uniswapv2ConstantProductPool.getSellPrice(
|
|
337
|
-
priceParams,
|
|
338
|
-
srcAmount,
|
|
339
|
-
this.feeFactor,
|
|
340
|
-
);
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
async getBuyPricePath(
|
|
344
|
-
amount: bigint,
|
|
345
|
-
params: UniswapV2PoolOrderedParams[],
|
|
346
|
-
): Promise<bigint> {
|
|
347
|
-
let price = amount;
|
|
348
|
-
for (const param of params.reverse()) {
|
|
349
|
-
price = await this.getBuyPrice(param, price);
|
|
350
|
-
}
|
|
351
|
-
return price;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
async getSellPricePath(
|
|
355
|
-
amount: bigint,
|
|
356
|
-
params: UniswapV2PoolOrderedParams[],
|
|
357
|
-
): Promise<bigint> {
|
|
358
|
-
let price = amount;
|
|
359
|
-
for (const param of params) {
|
|
360
|
-
price = await this.getSellPrice(param, price);
|
|
361
|
-
}
|
|
362
|
-
return price;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
async findPair(from: Token, to: Token) {
|
|
366
|
-
if (from.address.toLowerCase() === to.address.toLowerCase()) return null;
|
|
367
|
-
const [token0, token1] =
|
|
368
|
-
from.address.toLowerCase() < to.address.toLowerCase()
|
|
369
|
-
? [from, to]
|
|
370
|
-
: [to, from];
|
|
371
|
-
|
|
372
|
-
const key = `${token0.address.toLowerCase()}-${token1.address.toLowerCase()}`;
|
|
373
|
-
let pair = this.pairs[key];
|
|
374
|
-
if (pair) return pair;
|
|
375
|
-
const exchange = await this.factory.methods
|
|
376
|
-
.getPair(token0.address, token1.address)
|
|
377
|
-
.call();
|
|
378
|
-
if (exchange === NULL_ADDRESS) {
|
|
379
|
-
pair = { token0, token1 };
|
|
380
|
-
} else {
|
|
381
|
-
pair = { token0, token1, exchange };
|
|
382
|
-
}
|
|
383
|
-
this.pairs[key] = pair;
|
|
384
|
-
return pair;
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
async getManyPoolReserves(
|
|
388
|
-
pairs: UniswapV2Pair[],
|
|
389
|
-
blockNumber: number,
|
|
390
|
-
): Promise<UniswapV2PoolState[]> {
|
|
391
|
-
try {
|
|
392
|
-
const multiCallFeeData = pairs.map(pair =>
|
|
393
|
-
this.getFeesMultiCallData(pair),
|
|
394
|
-
);
|
|
395
|
-
const calldata = pairs
|
|
396
|
-
.map((pair, i) => {
|
|
397
|
-
let calldata = [
|
|
398
|
-
{
|
|
399
|
-
target: pair.token0.address,
|
|
400
|
-
callData: erc20iface.encodeFunctionData('balanceOf', [
|
|
401
|
-
pair.exchange!,
|
|
402
|
-
]),
|
|
403
|
-
},
|
|
404
|
-
{
|
|
405
|
-
target: pair.token1.address,
|
|
406
|
-
callData: erc20iface.encodeFunctionData('balanceOf', [
|
|
407
|
-
pair.exchange!,
|
|
408
|
-
]),
|
|
409
|
-
},
|
|
410
|
-
];
|
|
411
|
-
if (this.isDynamicFees) calldata.push(multiCallFeeData[i]!.callEntry);
|
|
412
|
-
return calldata;
|
|
413
|
-
})
|
|
414
|
-
.flat();
|
|
415
|
-
|
|
416
|
-
// const data: { returnData: any[] } =
|
|
417
|
-
// await this.dexHelper.multiContract.callStatic.aggregate(calldata, {
|
|
418
|
-
// blockTag: blockNumber,
|
|
419
|
-
// });
|
|
420
|
-
|
|
421
|
-
const data: { returnData: any[] } =
|
|
422
|
-
await this.dexHelper.multiContract.methods
|
|
423
|
-
.aggregate(calldata)
|
|
424
|
-
.call({}, blockNumber);
|
|
425
|
-
|
|
426
|
-
const returnData = _.chunk(data.returnData, this.isDynamicFees ? 3 : 2);
|
|
427
|
-
return pairs.map((pair, i) => ({
|
|
428
|
-
reserves0: coder.decode(['uint256'], returnData[i][0])[0].toString(),
|
|
429
|
-
reserves1: coder.decode(['uint256'], returnData[i][1])[0].toString(),
|
|
430
|
-
feeCode: this.isDynamicFees
|
|
431
|
-
? multiCallFeeData[i]!.callDecoder(returnData[i][2])
|
|
432
|
-
: this.feeCode,
|
|
433
|
-
}));
|
|
434
|
-
} catch (e) {
|
|
435
|
-
this.logger.error(
|
|
436
|
-
`Error_getManyPoolReserves could not get reserves with error:`,
|
|
437
|
-
e,
|
|
438
|
-
);
|
|
439
|
-
return [];
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
async batchCatchUpPairs(pairs: [Token, Token][], blockNumber: number) {
|
|
444
|
-
if (!blockNumber) return;
|
|
445
|
-
const pairsToFetch: UniswapV2Pair[] = [];
|
|
446
|
-
for (const _pair of pairs) {
|
|
447
|
-
const pair = await this.findPair(_pair[0], _pair[1]);
|
|
448
|
-
if (!(pair && pair.exchange)) continue;
|
|
449
|
-
if (!pair.pool) {
|
|
450
|
-
pairsToFetch.push(pair);
|
|
451
|
-
} else if (!pair.pool.getState(blockNumber)) {
|
|
452
|
-
pairsToFetch.push(pair);
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
if (!pairsToFetch.length) return;
|
|
457
|
-
|
|
458
|
-
const reserves = await this.getManyPoolReserves(pairsToFetch, blockNumber);
|
|
459
|
-
|
|
460
|
-
if (reserves.length !== pairsToFetch.length) {
|
|
461
|
-
this.logger.error(
|
|
462
|
-
`Error_getManyPoolReserves didn't get any pool reserves`,
|
|
463
|
-
);
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
for (let i = 0; i < pairsToFetch.length; i++) {
|
|
467
|
-
const pairState = reserves[i];
|
|
468
|
-
const pair = pairsToFetch[i];
|
|
469
|
-
if (!pair.pool) {
|
|
470
|
-
await this.addPool(
|
|
471
|
-
pair,
|
|
472
|
-
pairState.reserves0,
|
|
473
|
-
pairState.reserves1,
|
|
474
|
-
pairState.feeCode,
|
|
475
|
-
blockNumber,
|
|
476
|
-
);
|
|
477
|
-
} else pair.pool.setState(pairState, blockNumber);
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
async getPairOrderedParams(
|
|
482
|
-
from: Token,
|
|
483
|
-
to: Token,
|
|
484
|
-
blockNumber: number,
|
|
485
|
-
tokenDexTransferFee: number,
|
|
486
|
-
): Promise<UniswapV2PoolOrderedParams | null> {
|
|
487
|
-
const pair = await this.findPair(from, to);
|
|
488
|
-
if (!(pair && pair.pool && pair.exchange)) return null;
|
|
489
|
-
const pairState = pair.pool.getState(blockNumber);
|
|
490
|
-
if (!pairState) {
|
|
491
|
-
this.logger.error(
|
|
492
|
-
`Error_orderPairParams expected reserves, got none (maybe the pool doesn't exist) ${
|
|
493
|
-
from.symbol || from.address
|
|
494
|
-
} ${to.symbol || to.address}`,
|
|
495
|
-
);
|
|
496
|
-
return null;
|
|
497
|
-
}
|
|
498
|
-
const fee = (pairState.feeCode + tokenDexTransferFee).toString();
|
|
499
|
-
const pairReversed =
|
|
500
|
-
pair.token1.address.toLowerCase() === from.address.toLowerCase();
|
|
501
|
-
if (pairReversed) {
|
|
502
|
-
return {
|
|
503
|
-
tokenIn: from.address,
|
|
504
|
-
tokenOut: to.address,
|
|
505
|
-
reservesIn: pairState.reserves1,
|
|
506
|
-
reservesOut: pairState.reserves0,
|
|
507
|
-
fee,
|
|
508
|
-
direction: false,
|
|
509
|
-
exchange: pair.exchange,
|
|
510
|
-
};
|
|
511
|
-
}
|
|
512
|
-
return {
|
|
513
|
-
tokenIn: from.address,
|
|
514
|
-
tokenOut: to.address,
|
|
515
|
-
reservesIn: pairState.reserves0,
|
|
516
|
-
reservesOut: pairState.reserves1,
|
|
517
|
-
fee,
|
|
518
|
-
direction: true,
|
|
519
|
-
exchange: pair.exchange,
|
|
520
|
-
};
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
async getPoolIdentifiers(
|
|
524
|
-
_from: Token,
|
|
525
|
-
_to: Token,
|
|
526
|
-
side: SwapSide,
|
|
527
|
-
blockNumber: number,
|
|
528
|
-
): Promise<string[]> {
|
|
529
|
-
const from = this.dexHelper.config.wrapETH(_from);
|
|
530
|
-
const to = this.dexHelper.config.wrapETH(_to);
|
|
531
|
-
|
|
532
|
-
if (from.address.toLowerCase() === to.address.toLowerCase()) {
|
|
533
|
-
return [];
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
const tokenAddress = [from.address.toLowerCase(), to.address.toLowerCase()]
|
|
537
|
-
.sort((a, b) => (a > b ? 1 : -1))
|
|
538
|
-
.join('_');
|
|
539
|
-
|
|
540
|
-
const poolIdentifier = `${this.dexKey}_${tokenAddress}`;
|
|
541
|
-
return [poolIdentifier];
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
async getPricesVolume(
|
|
545
|
-
_from: Token,
|
|
546
|
-
_to: Token,
|
|
547
|
-
amounts: bigint[],
|
|
548
|
-
side: SwapSide,
|
|
549
|
-
blockNumber: number,
|
|
550
|
-
// list of pool identifiers to use for pricing, if undefined use all pools
|
|
551
|
-
limitPools?: string[],
|
|
552
|
-
transferFees: TransferFeeParams = {
|
|
553
|
-
srcFee: 0,
|
|
554
|
-
destFee: 0,
|
|
555
|
-
srcDexFee: 0,
|
|
556
|
-
destDexFee: 0,
|
|
557
|
-
},
|
|
558
|
-
): Promise<ExchangePrices<UniswapV2Data> | null> {
|
|
559
|
-
try {
|
|
560
|
-
const from = this.dexHelper.config.wrapETH(_from);
|
|
561
|
-
const to = this.dexHelper.config.wrapETH(_to);
|
|
562
|
-
|
|
563
|
-
if (from.address.toLowerCase() === to.address.toLowerCase()) {
|
|
564
|
-
return null;
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
const tokenAddress = [
|
|
568
|
-
from.address.toLowerCase(),
|
|
569
|
-
to.address.toLowerCase(),
|
|
570
|
-
]
|
|
571
|
-
.sort((a, b) => (a > b ? 1 : -1))
|
|
572
|
-
.join('_');
|
|
573
|
-
|
|
574
|
-
const poolIdentifier = `${this.dexKey}_${tokenAddress}`;
|
|
575
|
-
|
|
576
|
-
if (limitPools && limitPools.every(p => p !== poolIdentifier))
|
|
577
|
-
return null;
|
|
578
|
-
|
|
579
|
-
await this.batchCatchUpPairs([[from, to]], blockNumber);
|
|
580
|
-
const isSell = side === SwapSide.SELL;
|
|
581
|
-
const pairParam = await this.getPairOrderedParams(
|
|
582
|
-
from,
|
|
583
|
-
to,
|
|
584
|
-
blockNumber,
|
|
585
|
-
transferFees.srcDexFee,
|
|
586
|
-
);
|
|
587
|
-
|
|
588
|
-
if (!pairParam) return null;
|
|
589
|
-
|
|
590
|
-
const unitAmount = getBigIntPow(isSell ? from.decimals : to.decimals);
|
|
591
|
-
|
|
592
|
-
const [unitVolumeWithFee, ...amountsWithFee] = applyTransferFee(
|
|
593
|
-
[unitAmount, ...amounts],
|
|
594
|
-
side,
|
|
595
|
-
isSell ? transferFees.srcFee : transferFees.destFee,
|
|
596
|
-
isSell ? SRC_TOKEN_PARASWAP_TRANSFERS : DEST_TOKEN_PARASWAP_TRANSFERS,
|
|
597
|
-
);
|
|
598
|
-
|
|
599
|
-
const unit = isSell
|
|
600
|
-
? await this.getSellPricePath(unitVolumeWithFee, [pairParam])
|
|
601
|
-
: await this.getBuyPricePath(unitVolumeWithFee, [pairParam]);
|
|
602
|
-
|
|
603
|
-
const prices = isSell
|
|
604
|
-
? await Promise.all(
|
|
605
|
-
amountsWithFee.map(amount =>
|
|
606
|
-
this.getSellPricePath(amount, [pairParam]),
|
|
607
|
-
),
|
|
608
|
-
)
|
|
609
|
-
: await Promise.all(
|
|
610
|
-
amountsWithFee.map(amount =>
|
|
611
|
-
this.getBuyPricePath(amount, [pairParam]),
|
|
612
|
-
),
|
|
613
|
-
);
|
|
614
|
-
|
|
615
|
-
const [unitOutWithFee, ...outputsWithFee] = applyTransferFee(
|
|
616
|
-
[unit, ...prices],
|
|
617
|
-
side,
|
|
618
|
-
// This part is confusing, because we treat differently SELL and BUY fees
|
|
619
|
-
// If Buy, we should apply transfer fee on srcToken on top of dexFee applied earlier
|
|
620
|
-
// But for Sell we should apply only one dexFee
|
|
621
|
-
isSell ? transferFees.destDexFee : transferFees.srcFee,
|
|
622
|
-
isSell ? this.DEST_TOKEN_DEX_TRANSFERS : SRC_TOKEN_PARASWAP_TRANSFERS,
|
|
623
|
-
);
|
|
624
|
-
|
|
625
|
-
// As uniswapv2 just has one pool per token pair
|
|
626
|
-
return [
|
|
627
|
-
{
|
|
628
|
-
prices: outputsWithFee,
|
|
629
|
-
unit: unitOutWithFee,
|
|
630
|
-
data: {
|
|
631
|
-
router: this.router,
|
|
632
|
-
path: [from.address.toLowerCase(), to.address.toLowerCase()],
|
|
633
|
-
factory: this.factoryAddress,
|
|
634
|
-
initCode: this.initCode,
|
|
635
|
-
feeFactor: this.feeFactor,
|
|
636
|
-
pools: [
|
|
637
|
-
{
|
|
638
|
-
address: pairParam.exchange,
|
|
639
|
-
fee: parseInt(pairParam.fee),
|
|
640
|
-
direction: pairParam.direction,
|
|
641
|
-
},
|
|
642
|
-
],
|
|
643
|
-
},
|
|
644
|
-
exchange: this.dexKey,
|
|
645
|
-
poolIdentifier,
|
|
646
|
-
gasCost: this.poolGasCost,
|
|
647
|
-
poolAddresses: [pairParam.exchange],
|
|
648
|
-
},
|
|
649
|
-
];
|
|
650
|
-
} catch (e) {
|
|
651
|
-
if (blockNumber === 0)
|
|
652
|
-
this.logger.error(
|
|
653
|
-
`Error_getPricesVolume: Aurelius block manager not yet instantiated`,
|
|
654
|
-
);
|
|
655
|
-
this.logger.error(`Error_getPrices:`, e);
|
|
656
|
-
return null;
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
// Returns estimated gas cost of calldata for this DEX in multiSwap
|
|
661
|
-
getCalldataGasCost(poolPrices: PoolPrices<UniswapV2Data>): number | number[] {
|
|
662
|
-
return (
|
|
663
|
-
CALLDATA_GAS_COST.DEX_OVERHEAD +
|
|
664
|
-
CALLDATA_GAS_COST.LENGTH_SMALL +
|
|
665
|
-
// ParentStruct header
|
|
666
|
-
CALLDATA_GAS_COST.OFFSET_SMALL +
|
|
667
|
-
// ParentStruct -> weth
|
|
668
|
-
CALLDATA_GAS_COST.ADDRESS +
|
|
669
|
-
// ParentStruct -> pools[] header
|
|
670
|
-
CALLDATA_GAS_COST.OFFSET_SMALL +
|
|
671
|
-
// ParentStruct -> pools[]
|
|
672
|
-
CALLDATA_GAS_COST.LENGTH_SMALL +
|
|
673
|
-
// ParentStruct -> pools[0]
|
|
674
|
-
CALLDATA_GAS_COST.wordNonZeroBytes(22)
|
|
675
|
-
);
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
getAdapters(side: SwapSide): { name: string; index: number }[] | null {
|
|
679
|
-
return this.adapters[side];
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
async getTopPoolsForToken(
|
|
683
|
-
tokenAddress: Address,
|
|
684
|
-
count: number,
|
|
685
|
-
): Promise<PoolLiquidity[]> {
|
|
686
|
-
if (!this.subgraphURL) return [];
|
|
687
|
-
const query = `
|
|
688
|
-
query ($token: Bytes!, $count: Int) {
|
|
689
|
-
pools0: pairs(first: $count, orderBy: reserveUSD, orderDirection: desc, where: {token0: $token, reserve0_gt: 1, reserve1_gt: 1}) {
|
|
690
|
-
id
|
|
691
|
-
token0 {
|
|
692
|
-
id
|
|
693
|
-
decimals
|
|
694
|
-
}
|
|
695
|
-
token1 {
|
|
696
|
-
id
|
|
697
|
-
decimals
|
|
698
|
-
}
|
|
699
|
-
reserveUSD
|
|
700
|
-
}
|
|
701
|
-
pools1: pairs(first: $count, orderBy: reserveUSD, orderDirection: desc, where: {token1: $token, reserve0_gt: 1, reserve1_gt: 1}) {
|
|
702
|
-
id
|
|
703
|
-
token0 {
|
|
704
|
-
id
|
|
705
|
-
decimals
|
|
706
|
-
}
|
|
707
|
-
token1 {
|
|
708
|
-
id
|
|
709
|
-
decimals
|
|
710
|
-
}
|
|
711
|
-
reserveUSD
|
|
712
|
-
}
|
|
713
|
-
}`;
|
|
714
|
-
|
|
715
|
-
const { data } = await this.dexHelper.httpRequest.post(
|
|
716
|
-
this.subgraphURL,
|
|
717
|
-
{
|
|
718
|
-
query,
|
|
719
|
-
variables: { token: tokenAddress.toLowerCase(), count },
|
|
720
|
-
},
|
|
721
|
-
SUBGRAPH_TIMEOUT,
|
|
722
|
-
);
|
|
723
|
-
|
|
724
|
-
if (!(data && data.pools0 && data.pools1))
|
|
725
|
-
throw new Error("Couldn't fetch the pools from the subgraph");
|
|
726
|
-
const pools0 = _.map(data.pools0, pool => ({
|
|
727
|
-
exchange: this.dexKey,
|
|
728
|
-
address: pool.id.toLowerCase(),
|
|
729
|
-
connectorTokens: [
|
|
730
|
-
{
|
|
731
|
-
address: pool.token1.id.toLowerCase(),
|
|
732
|
-
decimals: parseInt(pool.token1.decimals),
|
|
733
|
-
},
|
|
734
|
-
],
|
|
735
|
-
liquidityUSD: parseFloat(pool.reserveUSD),
|
|
736
|
-
}));
|
|
737
|
-
|
|
738
|
-
const pools1 = _.map(data.pools1, pool => ({
|
|
739
|
-
exchange: this.dexKey,
|
|
740
|
-
address: pool.id.toLowerCase(),
|
|
741
|
-
connectorTokens: [
|
|
742
|
-
{
|
|
743
|
-
address: pool.token0.id.toLowerCase(),
|
|
744
|
-
decimals: parseInt(pool.token0.decimals),
|
|
745
|
-
},
|
|
746
|
-
],
|
|
747
|
-
liquidityUSD: parseFloat(pool.reserveUSD),
|
|
748
|
-
}));
|
|
749
|
-
|
|
750
|
-
return _.slice(
|
|
751
|
-
_.sortBy(_.concat(pools0, pools1), [pool => -1 * pool.liquidityUSD]),
|
|
752
|
-
0,
|
|
753
|
-
count,
|
|
754
|
-
);
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
protected fixPath(path: Address[], srcToken: Address, destToken: Address) {
|
|
758
|
-
return path.map((token: string, i: number) => {
|
|
759
|
-
if (
|
|
760
|
-
(i === 0 && srcToken.toLowerCase() === ETHER_ADDRESS.toLowerCase()) ||
|
|
761
|
-
(i === path.length - 1 &&
|
|
762
|
-
destToken.toLowerCase() === ETHER_ADDRESS.toLowerCase())
|
|
763
|
-
)
|
|
764
|
-
return ETHER_ADDRESS;
|
|
765
|
-
return token;
|
|
766
|
-
});
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
getWETHAddress(srcToken: Address, destToken: Address, weth?: Address) {
|
|
770
|
-
if (!isETHAddress(srcToken) && !isETHAddress(destToken))
|
|
771
|
-
return NULL_ADDRESS;
|
|
772
|
-
return weth || this.dexHelper.config.data.wrappedNativeTokenAddress;
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
getAdapterParam(
|
|
776
|
-
srcToken: Address,
|
|
777
|
-
destToken: Address,
|
|
778
|
-
srcAmount: NumberAsString,
|
|
779
|
-
toAmount: NumberAsString, // required for buy case
|
|
780
|
-
data: UniswapData,
|
|
781
|
-
side: SwapSide,
|
|
782
|
-
): AdapterExchangeParam {
|
|
783
|
-
const pools = encodePools(data.pools, this.feeFactor);
|
|
784
|
-
const weth = this.getWETHAddress(srcToken, destToken, data.weth);
|
|
785
|
-
const payload = this.abiCoder.encodeParameter(
|
|
786
|
-
{
|
|
787
|
-
ParentStruct: {
|
|
788
|
-
weth: 'address',
|
|
789
|
-
pools: 'uint256[]',
|
|
790
|
-
},
|
|
791
|
-
},
|
|
792
|
-
{ pools, weth },
|
|
793
|
-
);
|
|
794
|
-
return {
|
|
795
|
-
targetExchange: data.router,
|
|
796
|
-
payload,
|
|
797
|
-
networkFee: '0',
|
|
798
|
-
};
|
|
799
|
-
}
|
|
800
|
-
|
|
801
|
-
async getSimpleParam(
|
|
802
|
-
src: Address,
|
|
803
|
-
dest: Address,
|
|
804
|
-
srcAmount: NumberAsString,
|
|
805
|
-
destAmount: NumberAsString,
|
|
806
|
-
data: UniswapData,
|
|
807
|
-
side: SwapSide,
|
|
808
|
-
): Promise<SimpleExchangeParam> {
|
|
809
|
-
const pools = encodePools(data.pools, this.feeFactor);
|
|
810
|
-
const weth = this.getWETHAddress(src, dest, data.weth);
|
|
811
|
-
const swapData = this.exchangeRouterInterface.encodeFunctionData(
|
|
812
|
-
side === SwapSide.SELL ? UniswapV2Functions.swap : UniswapV2Functions.buy,
|
|
813
|
-
[src, srcAmount, destAmount, weth, pools],
|
|
814
|
-
);
|
|
815
|
-
|
|
816
|
-
const hasRebaseTokenSrc = rebaseTokensSetsByChain[this.network]?.has(
|
|
817
|
-
src.toLowerCase(),
|
|
818
|
-
);
|
|
819
|
-
const hasRebaseTokenDest = rebaseTokensSetsByChain[this.network]?.has(
|
|
820
|
-
dest.toLowerCase(),
|
|
821
|
-
);
|
|
822
|
-
|
|
823
|
-
const maybeSyncCall =
|
|
824
|
-
hasRebaseTokenSrc || hasRebaseTokenDest
|
|
825
|
-
? {
|
|
826
|
-
callees: [
|
|
827
|
-
hasRebaseTokenSrc
|
|
828
|
-
? data.pools[0].address
|
|
829
|
-
: data.pools[data.pools.length - 1].address,
|
|
830
|
-
],
|
|
831
|
-
calldata: [uniswapV2PoolIface.encodeFunctionData('sync')],
|
|
832
|
-
values: ['0'],
|
|
833
|
-
}
|
|
834
|
-
: undefined;
|
|
835
|
-
|
|
836
|
-
return this.buildSimpleParamWithoutWETHConversion(
|
|
837
|
-
src,
|
|
838
|
-
srcAmount,
|
|
839
|
-
dest,
|
|
840
|
-
destAmount,
|
|
841
|
-
swapData,
|
|
842
|
-
data.router,
|
|
843
|
-
data.router,
|
|
844
|
-
'0',
|
|
845
|
-
maybeSyncCall,
|
|
846
|
-
);
|
|
847
|
-
}
|
|
848
|
-
|
|
849
|
-
// TODO: Rebase tokens handling?
|
|
850
|
-
getDexParam(
|
|
851
|
-
srcToken: Address,
|
|
852
|
-
destToken: Address,
|
|
853
|
-
srcAmount: NumberAsString,
|
|
854
|
-
destAmount: NumberAsString,
|
|
855
|
-
recipient: Address,
|
|
856
|
-
data: UniswapData,
|
|
857
|
-
side: SwapSide,
|
|
858
|
-
): DexExchangeParam {
|
|
859
|
-
const pools = encodePools(data.pools, this.feeFactor);
|
|
860
|
-
|
|
861
|
-
let exchangeData: string;
|
|
862
|
-
let specialDexFlag: SpecialDex;
|
|
863
|
-
let transferSrcTokenBeforeSwap: Address | undefined;
|
|
864
|
-
let targetExchange: Address;
|
|
865
|
-
let dexFuncHasRecipient: boolean;
|
|
866
|
-
|
|
867
|
-
// if (this.dexKey === 'BakerySwap') {
|
|
868
|
-
// const weth = this.getWETHAddress(srcToken, destToken, data.weth);
|
|
869
|
-
|
|
870
|
-
// exchangeData = this.exchangeRouterInterface.encodeFunctionData(
|
|
871
|
-
// UniswapV2Functions.swap,
|
|
872
|
-
// [srcToken, srcAmount, destAmount, weth, pools],
|
|
873
|
-
// );
|
|
874
|
-
// specialDexFlag = SpecialDex.DEFAULT;
|
|
875
|
-
// targetExchange = data.router;
|
|
876
|
-
// dexFuncHasRecipient = false;
|
|
877
|
-
// } else if (side === SwapSide.SELL) {
|
|
878
|
-
|
|
879
|
-
if (side === SwapSide.SELL) {
|
|
880
|
-
// 28 bytes are prepended in the Bytecode builder
|
|
881
|
-
const exchangeDataTypes = ['bytes4', 'bytes32', 'bytes32'];
|
|
882
|
-
const exchangeDataToPack = [
|
|
883
|
-
hexZeroPad(hexlify(0), 4),
|
|
884
|
-
hexZeroPad(hexlify(data.pools.length), 32), // pool count
|
|
885
|
-
hexZeroPad(hexlify(BigNumber.from(srcAmount)), 32),
|
|
886
|
-
];
|
|
887
|
-
pools.forEach(pool => {
|
|
888
|
-
exchangeDataTypes.push('bytes32');
|
|
889
|
-
exchangeDataToPack.push(hexZeroPad(hexlify(BigNumber.from(pool)), 32));
|
|
890
|
-
});
|
|
891
|
-
|
|
892
|
-
exchangeData = solidityPack(exchangeDataTypes, exchangeDataToPack);
|
|
893
|
-
specialDexFlag = SpecialDex.SWAP_ON_UNISWAP_V2_FORK;
|
|
894
|
-
transferSrcTokenBeforeSwap = data.pools[0].address;
|
|
895
|
-
targetExchange = recipient;
|
|
896
|
-
dexFuncHasRecipient = true;
|
|
897
|
-
} else {
|
|
898
|
-
const weth = this.getWETHAddress(srcToken, destToken, data.weth);
|
|
899
|
-
|
|
900
|
-
exchangeData = this.exchangeRouterInterface.encodeFunctionData(
|
|
901
|
-
UniswapV2Functions.buy,
|
|
902
|
-
[srcToken, srcAmount, destAmount, weth, pools],
|
|
903
|
-
);
|
|
904
|
-
specialDexFlag = SpecialDex.DEFAULT;
|
|
905
|
-
targetExchange = data.router;
|
|
906
|
-
dexFuncHasRecipient = false;
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
return {
|
|
910
|
-
needWrapNative: this.needWrapNative,
|
|
911
|
-
specialDexSupportsInsertFromAmount: true,
|
|
912
|
-
dexFuncHasRecipient,
|
|
913
|
-
exchangeData,
|
|
914
|
-
targetExchange,
|
|
915
|
-
specialDexFlag,
|
|
916
|
-
transferSrcTokenBeforeSwap,
|
|
917
|
-
returnAmountPos: undefined,
|
|
918
|
-
};
|
|
919
|
-
}
|
|
920
|
-
|
|
921
|
-
// TODO: Move to new uniswapv2&forks router interface
|
|
922
|
-
getDirectParam(
|
|
923
|
-
srcToken: Address,
|
|
924
|
-
destToken: Address,
|
|
925
|
-
srcAmount: NumberAsString,
|
|
926
|
-
destAmount: NumberAsString,
|
|
927
|
-
expectedAmount: NumberAsString,
|
|
928
|
-
_data: UniswapData,
|
|
929
|
-
side: SwapSide,
|
|
930
|
-
permit: string,
|
|
931
|
-
uuid: string,
|
|
932
|
-
feePercent: NumberAsString,
|
|
933
|
-
deadline: NumberAsString,
|
|
934
|
-
partner: string,
|
|
935
|
-
beneficiary: string,
|
|
936
|
-
contractMethod?: string,
|
|
937
|
-
): TxInfo<UniswapParam> {
|
|
938
|
-
if (!contractMethod) throw new Error(`contractMethod need to be passed`);
|
|
939
|
-
if (permit !== '0x') contractMethod += 'WithPermit';
|
|
940
|
-
|
|
941
|
-
const swapParams = ((): UniswapParam => {
|
|
942
|
-
const data = _data as unknown as UniswapDataLegacy;
|
|
943
|
-
const path = this.fixPath(data.path, srcToken, destToken);
|
|
944
|
-
|
|
945
|
-
switch (contractMethod) {
|
|
946
|
-
case UniswapV2Functions.swapOnUniswap:
|
|
947
|
-
case UniswapV2Functions.buyOnUniswap:
|
|
948
|
-
return [srcAmount, destAmount, path];
|
|
949
|
-
|
|
950
|
-
case UniswapV2Functions.swapOnUniswapFork:
|
|
951
|
-
case UniswapV2Functions.buyOnUniswapFork:
|
|
952
|
-
return [
|
|
953
|
-
data.factory,
|
|
954
|
-
prependWithOx(data.initCode),
|
|
955
|
-
srcAmount,
|
|
956
|
-
destAmount,
|
|
957
|
-
path,
|
|
958
|
-
];
|
|
959
|
-
|
|
960
|
-
case UniswapV2Functions.swapOnUniswapV2Fork:
|
|
961
|
-
case UniswapV2Functions.buyOnUniswapV2Fork:
|
|
962
|
-
return [
|
|
963
|
-
srcToken,
|
|
964
|
-
srcAmount,
|
|
965
|
-
destAmount,
|
|
966
|
-
this.getWETHAddress(srcToken, destToken, _data.weth),
|
|
967
|
-
encodePools(_data.pools, this.feeFactor),
|
|
968
|
-
];
|
|
969
|
-
|
|
970
|
-
case UniswapV2Functions.swapOnUniswapV2ForkWithPermit:
|
|
971
|
-
case UniswapV2Functions.buyOnUniswapV2ForkWithPermit:
|
|
972
|
-
return [
|
|
973
|
-
srcToken,
|
|
974
|
-
srcAmount,
|
|
975
|
-
destAmount,
|
|
976
|
-
this.getWETHAddress(srcToken, destToken, _data.weth),
|
|
977
|
-
encodePools(_data.pools, this.feeFactor),
|
|
978
|
-
permit,
|
|
979
|
-
];
|
|
980
|
-
|
|
981
|
-
default:
|
|
982
|
-
throw new Error(`contractMethod=${contractMethod} is not supported`);
|
|
983
|
-
}
|
|
984
|
-
})();
|
|
985
|
-
|
|
986
|
-
const encoder = (...params: UniswapParam) =>
|
|
987
|
-
this.routerInterface.encodeFunctionData(contractMethod!, params);
|
|
988
|
-
return {
|
|
989
|
-
params: swapParams,
|
|
990
|
-
encoder,
|
|
991
|
-
networkFee: '0',
|
|
992
|
-
};
|
|
993
|
-
}
|
|
994
|
-
|
|
995
|
-
static getDirectFunctionName(): string[] {
|
|
996
|
-
return this.directFunctionName;
|
|
997
|
-
}
|
|
998
|
-
|
|
999
|
-
getDirectParamV6(
|
|
1000
|
-
srcToken: Address,
|
|
1001
|
-
destToken: Address,
|
|
1002
|
-
fromAmount: NumberAsString,
|
|
1003
|
-
toAmount: NumberAsString,
|
|
1004
|
-
quotedAmount: NumberAsString,
|
|
1005
|
-
data: UniswapV2Data,
|
|
1006
|
-
side: SwapSide,
|
|
1007
|
-
permit: string,
|
|
1008
|
-
uuid: string,
|
|
1009
|
-
partnerAndFee: string,
|
|
1010
|
-
beneficiary: string,
|
|
1011
|
-
blockNumber: number,
|
|
1012
|
-
contractMethod?: string,
|
|
1013
|
-
) {
|
|
1014
|
-
if (!contractMethod) throw new Error(`contractMethod need to be passed`);
|
|
1015
|
-
if (!UniswapV2.getDirectFunctionNameV6().includes(contractMethod!)) {
|
|
1016
|
-
throw new Error(`Invalid contract method ${contractMethod}`);
|
|
1017
|
-
}
|
|
1018
|
-
|
|
1019
|
-
const { path, pools } = data;
|
|
1020
|
-
const length = path.length;
|
|
1021
|
-
const encodedPath = path.reduce((acc, _, i) => {
|
|
1022
|
-
if (i >= length - 1) return acc;
|
|
1023
|
-
|
|
1024
|
-
const p = this._encodePathV6(
|
|
1025
|
-
{
|
|
1026
|
-
srcToken: path[i],
|
|
1027
|
-
destToken: path[i + 1],
|
|
1028
|
-
direction: pools[i].direction,
|
|
1029
|
-
},
|
|
1030
|
-
side,
|
|
1031
|
-
data.wethAddress,
|
|
1032
|
-
).replace('0x', '');
|
|
1033
|
-
|
|
1034
|
-
return acc + p;
|
|
1035
|
-
}, '0x');
|
|
1036
|
-
|
|
1037
|
-
const metadata = hexConcat([
|
|
1038
|
-
hexZeroPad(uuidToBytes16(uuid), 16),
|
|
1039
|
-
hexZeroPad(hexlify(blockNumber), 16),
|
|
1040
|
-
]);
|
|
1041
|
-
|
|
1042
|
-
const uniData: UniswapV2ParamsDirectBase = [
|
|
1043
|
-
srcToken,
|
|
1044
|
-
destToken,
|
|
1045
|
-
fromAmount,
|
|
1046
|
-
toAmount,
|
|
1047
|
-
quotedAmount,
|
|
1048
|
-
metadata,
|
|
1049
|
-
beneficiary,
|
|
1050
|
-
encodedPath,
|
|
1051
|
-
];
|
|
1052
|
-
|
|
1053
|
-
const swapParams: UniswapV2ParamsDirect = [uniData, partnerAndFee, permit];
|
|
1054
|
-
|
|
1055
|
-
const encoder = (...params: (string | UniswapV2ParamsDirect)[]) => {
|
|
1056
|
-
return this.augustusV6Interface.encodeFunctionData(
|
|
1057
|
-
side === SwapSide.SELL
|
|
1058
|
-
? UniswapV2FunctionsV6.swap
|
|
1059
|
-
: UniswapV2FunctionsV6.buy,
|
|
1060
|
-
[...params],
|
|
1061
|
-
);
|
|
1062
|
-
};
|
|
1063
|
-
|
|
1064
|
-
return {
|
|
1065
|
-
params: swapParams,
|
|
1066
|
-
encoder,
|
|
1067
|
-
networkFee: '0',
|
|
1068
|
-
};
|
|
1069
|
-
}
|
|
1070
|
-
|
|
1071
|
-
static getDirectFunctionNameV6(): string[] {
|
|
1072
|
-
return this.directFunctionNameV6;
|
|
1073
|
-
}
|
|
1074
|
-
|
|
1075
|
-
// univ2 always had 1 pool per pair
|
|
1076
|
-
private _encodePathV6(
|
|
1077
|
-
path: {
|
|
1078
|
-
srcToken: Address;
|
|
1079
|
-
destToken: Address;
|
|
1080
|
-
direction: boolean;
|
|
1081
|
-
},
|
|
1082
|
-
side: SwapSide,
|
|
1083
|
-
weth?: Address,
|
|
1084
|
-
): string {
|
|
1085
|
-
if (path == null) {
|
|
1086
|
-
this.logger.error(
|
|
1087
|
-
`${this.dexKey}: Received invalid path=${path} for side=${side} to encode`,
|
|
1088
|
-
);
|
|
1089
|
-
return '0x';
|
|
1090
|
-
}
|
|
1091
|
-
|
|
1092
|
-
// v6 expects weth for eth in pools
|
|
1093
|
-
if (isETHAddress(path.srcToken)) {
|
|
1094
|
-
path.srcToken =
|
|
1095
|
-
weth || this.dexHelper.config.data.wrappedNativeTokenAddress;
|
|
1096
|
-
}
|
|
1097
|
-
|
|
1098
|
-
if (isETHAddress(path.destToken)) {
|
|
1099
|
-
path.destToken =
|
|
1100
|
-
weth || this.dexHelper.config.data.wrappedNativeTokenAddress;
|
|
1101
|
-
}
|
|
1102
|
-
|
|
1103
|
-
// contract expects tokens to be sorted, and direction switched in case sorting changes src/dest order
|
|
1104
|
-
const [srcTokenSorted, destTokenSorted] =
|
|
1105
|
-
BigInt(path.srcToken) > BigInt(path.destToken)
|
|
1106
|
-
? [path.destToken, path.srcToken]
|
|
1107
|
-
: [path.srcToken, path.destToken];
|
|
1108
|
-
|
|
1109
|
-
const direction = srcTokenSorted === path.srcToken ? 1 : 0;
|
|
1110
|
-
|
|
1111
|
-
const tokensEncoded = pack(
|
|
1112
|
-
['address', 'address'],
|
|
1113
|
-
[srcTokenSorted, destTokenSorted],
|
|
1114
|
-
);
|
|
1115
|
-
return tokensEncoded + '0'.repeat(47) + direction;
|
|
1116
|
-
}
|
|
1117
|
-
}
|
|
1
|
+
import { AbiCoder, Interface } from '@ethersproject/abi';
|
|
2
|
+
import { pack } from '@ethersproject/solidity';
|
|
3
|
+
import _ from 'lodash';
|
|
4
|
+
import { AsyncOrSync, DeepReadonly } from 'ts-essentials';
|
|
5
|
+
import erc20ABI from '../../abi/erc20.json';
|
|
6
|
+
import { StatefulEventSubscriber } from '../../stateful-event-subscriber';
|
|
7
|
+
import {
|
|
8
|
+
AdapterExchangeParam,
|
|
9
|
+
Address,
|
|
10
|
+
ExchangePrices,
|
|
11
|
+
PoolPrices,
|
|
12
|
+
Log,
|
|
13
|
+
Logger,
|
|
14
|
+
PoolLiquidity,
|
|
15
|
+
SimpleExchangeParam,
|
|
16
|
+
Token,
|
|
17
|
+
TxInfo,
|
|
18
|
+
TransferFeeParams,
|
|
19
|
+
DexExchangeParam,
|
|
20
|
+
} from '../../types';
|
|
21
|
+
import {
|
|
22
|
+
UniswapData,
|
|
23
|
+
UniswapDataLegacy,
|
|
24
|
+
UniswapParam,
|
|
25
|
+
UniswapPool,
|
|
26
|
+
UniswapV2Data,
|
|
27
|
+
UniswapV2Functions,
|
|
28
|
+
UniswapV2FunctionsV6,
|
|
29
|
+
UniswapV2ParamsDirect,
|
|
30
|
+
UniswapV2ParamsDirectBase,
|
|
31
|
+
UniswapV2PoolOrderedParams,
|
|
32
|
+
} from './types';
|
|
33
|
+
import { IDex } from '../idex';
|
|
34
|
+
import {
|
|
35
|
+
DEST_TOKEN_PARASWAP_TRANSFERS,
|
|
36
|
+
ETHER_ADDRESS,
|
|
37
|
+
Network,
|
|
38
|
+
NULL_ADDRESS,
|
|
39
|
+
SRC_TOKEN_PARASWAP_TRANSFERS,
|
|
40
|
+
SUBGRAPH_TIMEOUT,
|
|
41
|
+
} from '../../constants';
|
|
42
|
+
import * as CALLDATA_GAS_COST from '../../calldata-gas-cost';
|
|
43
|
+
import { SimpleExchange } from '../simple-exchange';
|
|
44
|
+
import { NumberAsString, SwapSide } from '@paraswap/core';
|
|
45
|
+
import { IDexHelper } from '../../dex-helper';
|
|
46
|
+
import {
|
|
47
|
+
getDexKeysWithNetwork,
|
|
48
|
+
isETHAddress,
|
|
49
|
+
prependWithOx,
|
|
50
|
+
getBigIntPow,
|
|
51
|
+
uuidToBytes16,
|
|
52
|
+
} from '../../utils';
|
|
53
|
+
import uniswapV2ABI from '../../abi/uniswap-v2/uniswap-v2-pool.json';
|
|
54
|
+
import uniswapV2factoryABI from '../../abi/uniswap-v2/uniswap-v2-factory.json';
|
|
55
|
+
import ParaSwapABI from '../../abi/IParaswap.json';
|
|
56
|
+
import UniswapV2ExchangeRouterABI from '../../abi/UniswapV2ExchangeRouter.json';
|
|
57
|
+
import { Contract } from 'web3-eth-contract';
|
|
58
|
+
import { UniswapV2Config, Adapters } from './config';
|
|
59
|
+
import { Uniswapv2ConstantProductPool } from './uniswap-v2-constant-product-pool';
|
|
60
|
+
import { applyTransferFee } from '../../lib/token-transfer-fee';
|
|
61
|
+
import _rebaseTokens from '../../rebase-tokens.json';
|
|
62
|
+
import { Flag, SpecialDex } from '../../executor/types';
|
|
63
|
+
import {
|
|
64
|
+
hexZeroPad,
|
|
65
|
+
hexlify,
|
|
66
|
+
solidityPack,
|
|
67
|
+
hexDataLength,
|
|
68
|
+
id,
|
|
69
|
+
hexConcat,
|
|
70
|
+
} from 'ethers/lib/utils';
|
|
71
|
+
import { BigNumber } from 'ethers';
|
|
72
|
+
|
|
73
|
+
const rebaseTokens = _rebaseTokens as { chainId: number; address: string }[];
|
|
74
|
+
|
|
75
|
+
const rebaseTokensSetsByChain = rebaseTokens.reduce<{
|
|
76
|
+
[chainId: number]: Set<string>;
|
|
77
|
+
}>((acc, curr) => {
|
|
78
|
+
if (!acc[curr.chainId]) {
|
|
79
|
+
acc[curr.chainId] = new Set();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
acc[curr.chainId].add(curr.address.toLowerCase());
|
|
83
|
+
|
|
84
|
+
return acc;
|
|
85
|
+
}, {});
|
|
86
|
+
|
|
87
|
+
const DefaultUniswapV2PoolGasCost = 90 * 1000;
|
|
88
|
+
|
|
89
|
+
export const RESERVE_LIMIT = 2n ** 112n - 1n;
|
|
90
|
+
|
|
91
|
+
const LogCallTopics = [
|
|
92
|
+
'0x1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1', // event Sync(uint112 reserve0, uint112 reserve1) // uni-V2 and most forks
|
|
93
|
+
'0xcf2aa50876cdfbb541206f89af0ee78d44a2abf8d328e37fa4917f982149848a', // event Sync(uint256 reserve0, uint256 reserve1) // commonly seen in solidly & forks
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
interface UniswapV2PoolState {
|
|
97
|
+
reserves0: string;
|
|
98
|
+
reserves1: string;
|
|
99
|
+
feeCode: number;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const uniswapV2PoolIface = new Interface(uniswapV2ABI);
|
|
103
|
+
const erc20iface = new Interface(erc20ABI);
|
|
104
|
+
const coder = new AbiCoder();
|
|
105
|
+
|
|
106
|
+
export const directUniswapFunctionName = [
|
|
107
|
+
UniswapV2Functions.swapOnUniswap,
|
|
108
|
+
UniswapV2Functions.buyOnUniswap,
|
|
109
|
+
UniswapV2Functions.swapOnUniswapFork,
|
|
110
|
+
UniswapV2Functions.buyOnUniswapFork,
|
|
111
|
+
UniswapV2Functions.swapOnUniswapV2Fork,
|
|
112
|
+
UniswapV2Functions.buyOnUniswapV2Fork,
|
|
113
|
+
];
|
|
114
|
+
|
|
115
|
+
export const directUniswapFunctionNameV6 = [
|
|
116
|
+
UniswapV2FunctionsV6.swap,
|
|
117
|
+
UniswapV2FunctionsV6.buy,
|
|
118
|
+
];
|
|
119
|
+
|
|
120
|
+
export interface UniswapV2Pair {
|
|
121
|
+
token0: Token;
|
|
122
|
+
token1: Token;
|
|
123
|
+
exchange?: Address;
|
|
124
|
+
pool?: UniswapV2EventPool;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export class UniswapV2EventPool extends StatefulEventSubscriber<UniswapV2PoolState> {
|
|
128
|
+
decoder = (log: Log) => this.iface.parseLog(log);
|
|
129
|
+
|
|
130
|
+
constructor(
|
|
131
|
+
parentName: string,
|
|
132
|
+
protected dexHelper: IDexHelper,
|
|
133
|
+
private poolAddress: Address,
|
|
134
|
+
private token0: Token,
|
|
135
|
+
private token1: Token,
|
|
136
|
+
// feeCode is ignored if DynamicFees is set to true
|
|
137
|
+
private feeCode: number,
|
|
138
|
+
logger: Logger,
|
|
139
|
+
private dynamicFees = false,
|
|
140
|
+
// feesMultiCallData is only used if dynamicFees is set to true
|
|
141
|
+
private feesMultiCallEntry?: { target: Address; callData: string },
|
|
142
|
+
private feesMultiCallDecoder?: (values: any[]) => number,
|
|
143
|
+
private iface: Interface = uniswapV2PoolIface,
|
|
144
|
+
) {
|
|
145
|
+
super(
|
|
146
|
+
parentName,
|
|
147
|
+
(token0.symbol || token0.address) +
|
|
148
|
+
'-' +
|
|
149
|
+
(token1.symbol || token1.address) +
|
|
150
|
+
' pool',
|
|
151
|
+
dexHelper,
|
|
152
|
+
logger,
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
protected processLog(
|
|
157
|
+
state: DeepReadonly<UniswapV2PoolState>,
|
|
158
|
+
log: Readonly<Log>,
|
|
159
|
+
): AsyncOrSync<DeepReadonly<UniswapV2PoolState> | null> {
|
|
160
|
+
if (!LogCallTopics.includes(log.topics[0])) return null;
|
|
161
|
+
|
|
162
|
+
const event = this.decoder(log);
|
|
163
|
+
switch (event.name) {
|
|
164
|
+
case 'Sync':
|
|
165
|
+
return {
|
|
166
|
+
reserves0: event.args.reserve0.toString(),
|
|
167
|
+
reserves1: event.args.reserve1.toString(),
|
|
168
|
+
feeCode: state.feeCode,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async generateState(
|
|
175
|
+
blockNumber: number | 'latest' = 'latest',
|
|
176
|
+
): Promise<DeepReadonly<UniswapV2PoolState>> {
|
|
177
|
+
let calldata = [
|
|
178
|
+
{
|
|
179
|
+
target: this.poolAddress,
|
|
180
|
+
callData: this.iface.encodeFunctionData('getReserves', []),
|
|
181
|
+
},
|
|
182
|
+
];
|
|
183
|
+
|
|
184
|
+
if (this.dynamicFees) {
|
|
185
|
+
calldata.push(this.feesMultiCallEntry!);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const data: { returnData: any[] } =
|
|
189
|
+
await this.dexHelper.multiContract.methods
|
|
190
|
+
.aggregate(calldata)
|
|
191
|
+
.call({}, blockNumber);
|
|
192
|
+
|
|
193
|
+
const decodedData = coder.decode(
|
|
194
|
+
['uint112', 'uint112', 'uint32'],
|
|
195
|
+
data.returnData[0],
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
return {
|
|
199
|
+
reserves0: decodedData[0].toString(),
|
|
200
|
+
reserves1: decodedData[1].toString(),
|
|
201
|
+
feeCode: this.dynamicFees
|
|
202
|
+
? this.feesMultiCallDecoder!(data.returnData[1])
|
|
203
|
+
: this.feeCode,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function encodePools(
|
|
209
|
+
pools: UniswapPool[],
|
|
210
|
+
feeFactor: number,
|
|
211
|
+
): NumberAsString[] {
|
|
212
|
+
return pools.map(({ fee, direction, address }) => {
|
|
213
|
+
return (
|
|
214
|
+
(BigInt(feeFactor - fee) << 161n) +
|
|
215
|
+
((direction ? 0n : 1n) << 160n) +
|
|
216
|
+
BigInt(address)
|
|
217
|
+
).toString();
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export class UniswapV2
|
|
222
|
+
extends SimpleExchange
|
|
223
|
+
implements IDex<UniswapV2Data, UniswapParam | UniswapV2ParamsDirect>
|
|
224
|
+
{
|
|
225
|
+
pairs: { [key: string]: UniswapV2Pair } = {};
|
|
226
|
+
feeFactor = 10000;
|
|
227
|
+
factory: Contract;
|
|
228
|
+
|
|
229
|
+
routerInterface: Interface;
|
|
230
|
+
exchangeRouterInterface: Interface;
|
|
231
|
+
|
|
232
|
+
needWrapNative = true;
|
|
233
|
+
|
|
234
|
+
logger: Logger;
|
|
235
|
+
|
|
236
|
+
static directFunctionName = directUniswapFunctionName;
|
|
237
|
+
static directFunctionNameV6 = directUniswapFunctionNameV6;
|
|
238
|
+
|
|
239
|
+
readonly hasConstantPriceLargeAmounts = false;
|
|
240
|
+
readonly isFeeOnTransferSupported: boolean = true;
|
|
241
|
+
readonly SRC_TOKEN_DEX_TRANSFERS = 1;
|
|
242
|
+
readonly DEST_TOKEN_DEX_TRANSFERS = 1;
|
|
243
|
+
|
|
244
|
+
public static dexKeysWithNetwork: { key: string; networks: Network[] }[] =
|
|
245
|
+
getDexKeysWithNetwork(UniswapV2Config);
|
|
246
|
+
|
|
247
|
+
constructor(
|
|
248
|
+
protected network: Network,
|
|
249
|
+
dexKey: string,
|
|
250
|
+
protected dexHelper: IDexHelper,
|
|
251
|
+
protected isDynamicFees = false,
|
|
252
|
+
protected factoryAddress: Address = UniswapV2Config[dexKey][network]
|
|
253
|
+
.factoryAddress,
|
|
254
|
+
protected subgraphURL: string | undefined = UniswapV2Config[dexKey] &&
|
|
255
|
+
UniswapV2Config[dexKey][network].subgraphURL,
|
|
256
|
+
protected initCode: string = UniswapV2Config[dexKey][network].initCode,
|
|
257
|
+
// feeCode is ignored when isDynamicFees is set to true
|
|
258
|
+
protected feeCode: number = UniswapV2Config[dexKey][network].feeCode,
|
|
259
|
+
protected poolGasCost: number = (UniswapV2Config[dexKey] &&
|
|
260
|
+
UniswapV2Config[dexKey][network].poolGasCost) ??
|
|
261
|
+
DefaultUniswapV2PoolGasCost,
|
|
262
|
+
protected decoderIface: Interface = uniswapV2PoolIface,
|
|
263
|
+
protected adapters = (UniswapV2Config[dexKey] &&
|
|
264
|
+
UniswapV2Config[dexKey][network].adapters) ??
|
|
265
|
+
Adapters[network],
|
|
266
|
+
protected router = (UniswapV2Config[dexKey] &&
|
|
267
|
+
UniswapV2Config[dexKey][network].router) ??
|
|
268
|
+
dexHelper.config.data.uniswapV2ExchangeRouterAddress,
|
|
269
|
+
) {
|
|
270
|
+
super(dexHelper, dexKey);
|
|
271
|
+
this.logger = dexHelper.getLogger(dexKey);
|
|
272
|
+
|
|
273
|
+
this.factory = new dexHelper.web3Provider.eth.Contract(
|
|
274
|
+
uniswapV2factoryABI as any,
|
|
275
|
+
factoryAddress,
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
this.routerInterface = new Interface(ParaSwapABI);
|
|
279
|
+
this.exchangeRouterInterface = new Interface(UniswapV2ExchangeRouterABI);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// getFeesMultiCallData should be override
|
|
283
|
+
// when isDynamicFees is set to true
|
|
284
|
+
protected getFeesMultiCallData(pair: UniswapV2Pair):
|
|
285
|
+
| undefined
|
|
286
|
+
| {
|
|
287
|
+
callEntry: { target: Address; callData: string };
|
|
288
|
+
callDecoder: (values: any[]) => number;
|
|
289
|
+
} {
|
|
290
|
+
return undefined;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
protected async addPool(
|
|
294
|
+
pair: UniswapV2Pair,
|
|
295
|
+
reserves0: string,
|
|
296
|
+
reserves1: string,
|
|
297
|
+
feeCode: number,
|
|
298
|
+
blockNumber: number,
|
|
299
|
+
) {
|
|
300
|
+
const { callEntry, callDecoder } = this.getFeesMultiCallData(pair) || {};
|
|
301
|
+
pair.pool = new UniswapV2EventPool(
|
|
302
|
+
this.dexKey,
|
|
303
|
+
this.dexHelper,
|
|
304
|
+
pair.exchange!,
|
|
305
|
+
pair.token0,
|
|
306
|
+
pair.token1,
|
|
307
|
+
feeCode,
|
|
308
|
+
this.logger,
|
|
309
|
+
this.isDynamicFees,
|
|
310
|
+
callEntry,
|
|
311
|
+
callDecoder,
|
|
312
|
+
this.decoderIface,
|
|
313
|
+
);
|
|
314
|
+
pair.pool.addressesSubscribed.push(pair.exchange!);
|
|
315
|
+
|
|
316
|
+
await pair.pool.initialize(blockNumber, {
|
|
317
|
+
state: { reserves0, reserves1, feeCode },
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
async getBuyPrice(
|
|
322
|
+
priceParams: UniswapV2PoolOrderedParams,
|
|
323
|
+
destAmount: bigint,
|
|
324
|
+
): Promise<bigint> {
|
|
325
|
+
return Uniswapv2ConstantProductPool.getBuyPrice(
|
|
326
|
+
priceParams,
|
|
327
|
+
destAmount,
|
|
328
|
+
this.feeFactor,
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
async getSellPrice(
|
|
333
|
+
priceParams: UniswapV2PoolOrderedParams,
|
|
334
|
+
srcAmount: bigint,
|
|
335
|
+
): Promise<bigint> {
|
|
336
|
+
return Uniswapv2ConstantProductPool.getSellPrice(
|
|
337
|
+
priceParams,
|
|
338
|
+
srcAmount,
|
|
339
|
+
this.feeFactor,
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
async getBuyPricePath(
|
|
344
|
+
amount: bigint,
|
|
345
|
+
params: UniswapV2PoolOrderedParams[],
|
|
346
|
+
): Promise<bigint> {
|
|
347
|
+
let price = amount;
|
|
348
|
+
for (const param of params.reverse()) {
|
|
349
|
+
price = await this.getBuyPrice(param, price);
|
|
350
|
+
}
|
|
351
|
+
return price;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
async getSellPricePath(
|
|
355
|
+
amount: bigint,
|
|
356
|
+
params: UniswapV2PoolOrderedParams[],
|
|
357
|
+
): Promise<bigint> {
|
|
358
|
+
let price = amount;
|
|
359
|
+
for (const param of params) {
|
|
360
|
+
price = await this.getSellPrice(param, price);
|
|
361
|
+
}
|
|
362
|
+
return price;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
async findPair(from: Token, to: Token) {
|
|
366
|
+
if (from.address.toLowerCase() === to.address.toLowerCase()) return null;
|
|
367
|
+
const [token0, token1] =
|
|
368
|
+
from.address.toLowerCase() < to.address.toLowerCase()
|
|
369
|
+
? [from, to]
|
|
370
|
+
: [to, from];
|
|
371
|
+
|
|
372
|
+
const key = `${token0.address.toLowerCase()}-${token1.address.toLowerCase()}`;
|
|
373
|
+
let pair = this.pairs[key];
|
|
374
|
+
if (pair) return pair;
|
|
375
|
+
const exchange = await this.factory.methods
|
|
376
|
+
.getPair(token0.address, token1.address)
|
|
377
|
+
.call();
|
|
378
|
+
if (exchange === NULL_ADDRESS) {
|
|
379
|
+
pair = { token0, token1 };
|
|
380
|
+
} else {
|
|
381
|
+
pair = { token0, token1, exchange };
|
|
382
|
+
}
|
|
383
|
+
this.pairs[key] = pair;
|
|
384
|
+
return pair;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
async getManyPoolReserves(
|
|
388
|
+
pairs: UniswapV2Pair[],
|
|
389
|
+
blockNumber: number,
|
|
390
|
+
): Promise<UniswapV2PoolState[]> {
|
|
391
|
+
try {
|
|
392
|
+
const multiCallFeeData = pairs.map(pair =>
|
|
393
|
+
this.getFeesMultiCallData(pair),
|
|
394
|
+
);
|
|
395
|
+
const calldata = pairs
|
|
396
|
+
.map((pair, i) => {
|
|
397
|
+
let calldata = [
|
|
398
|
+
{
|
|
399
|
+
target: pair.token0.address,
|
|
400
|
+
callData: erc20iface.encodeFunctionData('balanceOf', [
|
|
401
|
+
pair.exchange!,
|
|
402
|
+
]),
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
target: pair.token1.address,
|
|
406
|
+
callData: erc20iface.encodeFunctionData('balanceOf', [
|
|
407
|
+
pair.exchange!,
|
|
408
|
+
]),
|
|
409
|
+
},
|
|
410
|
+
];
|
|
411
|
+
if (this.isDynamicFees) calldata.push(multiCallFeeData[i]!.callEntry);
|
|
412
|
+
return calldata;
|
|
413
|
+
})
|
|
414
|
+
.flat();
|
|
415
|
+
|
|
416
|
+
// const data: { returnData: any[] } =
|
|
417
|
+
// await this.dexHelper.multiContract.callStatic.aggregate(calldata, {
|
|
418
|
+
// blockTag: blockNumber,
|
|
419
|
+
// });
|
|
420
|
+
|
|
421
|
+
const data: { returnData: any[] } =
|
|
422
|
+
await this.dexHelper.multiContract.methods
|
|
423
|
+
.aggregate(calldata)
|
|
424
|
+
.call({}, blockNumber);
|
|
425
|
+
|
|
426
|
+
const returnData = _.chunk(data.returnData, this.isDynamicFees ? 3 : 2);
|
|
427
|
+
return pairs.map((pair, i) => ({
|
|
428
|
+
reserves0: coder.decode(['uint256'], returnData[i][0])[0].toString(),
|
|
429
|
+
reserves1: coder.decode(['uint256'], returnData[i][1])[0].toString(),
|
|
430
|
+
feeCode: this.isDynamicFees
|
|
431
|
+
? multiCallFeeData[i]!.callDecoder(returnData[i][2])
|
|
432
|
+
: this.feeCode,
|
|
433
|
+
}));
|
|
434
|
+
} catch (e) {
|
|
435
|
+
this.logger.error(
|
|
436
|
+
`Error_getManyPoolReserves could not get reserves with error:`,
|
|
437
|
+
e,
|
|
438
|
+
);
|
|
439
|
+
return [];
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
async batchCatchUpPairs(pairs: [Token, Token][], blockNumber: number) {
|
|
444
|
+
if (!blockNumber) return;
|
|
445
|
+
const pairsToFetch: UniswapV2Pair[] = [];
|
|
446
|
+
for (const _pair of pairs) {
|
|
447
|
+
const pair = await this.findPair(_pair[0], _pair[1]);
|
|
448
|
+
if (!(pair && pair.exchange)) continue;
|
|
449
|
+
if (!pair.pool) {
|
|
450
|
+
pairsToFetch.push(pair);
|
|
451
|
+
} else if (!pair.pool.getState(blockNumber)) {
|
|
452
|
+
pairsToFetch.push(pair);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
if (!pairsToFetch.length) return;
|
|
457
|
+
|
|
458
|
+
const reserves = await this.getManyPoolReserves(pairsToFetch, blockNumber);
|
|
459
|
+
|
|
460
|
+
if (reserves.length !== pairsToFetch.length) {
|
|
461
|
+
this.logger.error(
|
|
462
|
+
`Error_getManyPoolReserves didn't get any pool reserves`,
|
|
463
|
+
);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
for (let i = 0; i < pairsToFetch.length; i++) {
|
|
467
|
+
const pairState = reserves[i];
|
|
468
|
+
const pair = pairsToFetch[i];
|
|
469
|
+
if (!pair.pool) {
|
|
470
|
+
await this.addPool(
|
|
471
|
+
pair,
|
|
472
|
+
pairState.reserves0,
|
|
473
|
+
pairState.reserves1,
|
|
474
|
+
pairState.feeCode,
|
|
475
|
+
blockNumber,
|
|
476
|
+
);
|
|
477
|
+
} else pair.pool.setState(pairState, blockNumber);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
async getPairOrderedParams(
|
|
482
|
+
from: Token,
|
|
483
|
+
to: Token,
|
|
484
|
+
blockNumber: number,
|
|
485
|
+
tokenDexTransferFee: number,
|
|
486
|
+
): Promise<UniswapV2PoolOrderedParams | null> {
|
|
487
|
+
const pair = await this.findPair(from, to);
|
|
488
|
+
if (!(pair && pair.pool && pair.exchange)) return null;
|
|
489
|
+
const pairState = pair.pool.getState(blockNumber);
|
|
490
|
+
if (!pairState) {
|
|
491
|
+
this.logger.error(
|
|
492
|
+
`Error_orderPairParams expected reserves, got none (maybe the pool doesn't exist) ${
|
|
493
|
+
from.symbol || from.address
|
|
494
|
+
} ${to.symbol || to.address}`,
|
|
495
|
+
);
|
|
496
|
+
return null;
|
|
497
|
+
}
|
|
498
|
+
const fee = (pairState.feeCode + tokenDexTransferFee).toString();
|
|
499
|
+
const pairReversed =
|
|
500
|
+
pair.token1.address.toLowerCase() === from.address.toLowerCase();
|
|
501
|
+
if (pairReversed) {
|
|
502
|
+
return {
|
|
503
|
+
tokenIn: from.address,
|
|
504
|
+
tokenOut: to.address,
|
|
505
|
+
reservesIn: pairState.reserves1,
|
|
506
|
+
reservesOut: pairState.reserves0,
|
|
507
|
+
fee,
|
|
508
|
+
direction: false,
|
|
509
|
+
exchange: pair.exchange,
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
return {
|
|
513
|
+
tokenIn: from.address,
|
|
514
|
+
tokenOut: to.address,
|
|
515
|
+
reservesIn: pairState.reserves0,
|
|
516
|
+
reservesOut: pairState.reserves1,
|
|
517
|
+
fee,
|
|
518
|
+
direction: true,
|
|
519
|
+
exchange: pair.exchange,
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
async getPoolIdentifiers(
|
|
524
|
+
_from: Token,
|
|
525
|
+
_to: Token,
|
|
526
|
+
side: SwapSide,
|
|
527
|
+
blockNumber: number,
|
|
528
|
+
): Promise<string[]> {
|
|
529
|
+
const from = this.dexHelper.config.wrapETH(_from);
|
|
530
|
+
const to = this.dexHelper.config.wrapETH(_to);
|
|
531
|
+
|
|
532
|
+
if (from.address.toLowerCase() === to.address.toLowerCase()) {
|
|
533
|
+
return [];
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
const tokenAddress = [from.address.toLowerCase(), to.address.toLowerCase()]
|
|
537
|
+
.sort((a, b) => (a > b ? 1 : -1))
|
|
538
|
+
.join('_');
|
|
539
|
+
|
|
540
|
+
const poolIdentifier = `${this.dexKey}_${tokenAddress}`;
|
|
541
|
+
return [poolIdentifier];
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
async getPricesVolume(
|
|
545
|
+
_from: Token,
|
|
546
|
+
_to: Token,
|
|
547
|
+
amounts: bigint[],
|
|
548
|
+
side: SwapSide,
|
|
549
|
+
blockNumber: number,
|
|
550
|
+
// list of pool identifiers to use for pricing, if undefined use all pools
|
|
551
|
+
limitPools?: string[],
|
|
552
|
+
transferFees: TransferFeeParams = {
|
|
553
|
+
srcFee: 0,
|
|
554
|
+
destFee: 0,
|
|
555
|
+
srcDexFee: 0,
|
|
556
|
+
destDexFee: 0,
|
|
557
|
+
},
|
|
558
|
+
): Promise<ExchangePrices<UniswapV2Data> | null> {
|
|
559
|
+
try {
|
|
560
|
+
const from = this.dexHelper.config.wrapETH(_from);
|
|
561
|
+
const to = this.dexHelper.config.wrapETH(_to);
|
|
562
|
+
|
|
563
|
+
if (from.address.toLowerCase() === to.address.toLowerCase()) {
|
|
564
|
+
return null;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
const tokenAddress = [
|
|
568
|
+
from.address.toLowerCase(),
|
|
569
|
+
to.address.toLowerCase(),
|
|
570
|
+
]
|
|
571
|
+
.sort((a, b) => (a > b ? 1 : -1))
|
|
572
|
+
.join('_');
|
|
573
|
+
|
|
574
|
+
const poolIdentifier = `${this.dexKey}_${tokenAddress}`;
|
|
575
|
+
|
|
576
|
+
if (limitPools && limitPools.every(p => p !== poolIdentifier))
|
|
577
|
+
return null;
|
|
578
|
+
|
|
579
|
+
await this.batchCatchUpPairs([[from, to]], blockNumber);
|
|
580
|
+
const isSell = side === SwapSide.SELL;
|
|
581
|
+
const pairParam = await this.getPairOrderedParams(
|
|
582
|
+
from,
|
|
583
|
+
to,
|
|
584
|
+
blockNumber,
|
|
585
|
+
transferFees.srcDexFee,
|
|
586
|
+
);
|
|
587
|
+
|
|
588
|
+
if (!pairParam) return null;
|
|
589
|
+
|
|
590
|
+
const unitAmount = getBigIntPow(isSell ? from.decimals : to.decimals);
|
|
591
|
+
|
|
592
|
+
const [unitVolumeWithFee, ...amountsWithFee] = applyTransferFee(
|
|
593
|
+
[unitAmount, ...amounts],
|
|
594
|
+
side,
|
|
595
|
+
isSell ? transferFees.srcFee : transferFees.destFee,
|
|
596
|
+
isSell ? SRC_TOKEN_PARASWAP_TRANSFERS : DEST_TOKEN_PARASWAP_TRANSFERS,
|
|
597
|
+
);
|
|
598
|
+
|
|
599
|
+
const unit = isSell
|
|
600
|
+
? await this.getSellPricePath(unitVolumeWithFee, [pairParam])
|
|
601
|
+
: await this.getBuyPricePath(unitVolumeWithFee, [pairParam]);
|
|
602
|
+
|
|
603
|
+
const prices = isSell
|
|
604
|
+
? await Promise.all(
|
|
605
|
+
amountsWithFee.map(amount =>
|
|
606
|
+
this.getSellPricePath(amount, [pairParam]),
|
|
607
|
+
),
|
|
608
|
+
)
|
|
609
|
+
: await Promise.all(
|
|
610
|
+
amountsWithFee.map(amount =>
|
|
611
|
+
this.getBuyPricePath(amount, [pairParam]),
|
|
612
|
+
),
|
|
613
|
+
);
|
|
614
|
+
|
|
615
|
+
const [unitOutWithFee, ...outputsWithFee] = applyTransferFee(
|
|
616
|
+
[unit, ...prices],
|
|
617
|
+
side,
|
|
618
|
+
// This part is confusing, because we treat differently SELL and BUY fees
|
|
619
|
+
// If Buy, we should apply transfer fee on srcToken on top of dexFee applied earlier
|
|
620
|
+
// But for Sell we should apply only one dexFee
|
|
621
|
+
isSell ? transferFees.destDexFee : transferFees.srcFee,
|
|
622
|
+
isSell ? this.DEST_TOKEN_DEX_TRANSFERS : SRC_TOKEN_PARASWAP_TRANSFERS,
|
|
623
|
+
);
|
|
624
|
+
|
|
625
|
+
// As uniswapv2 just has one pool per token pair
|
|
626
|
+
return [
|
|
627
|
+
{
|
|
628
|
+
prices: outputsWithFee,
|
|
629
|
+
unit: unitOutWithFee,
|
|
630
|
+
data: {
|
|
631
|
+
router: this.router,
|
|
632
|
+
path: [from.address.toLowerCase(), to.address.toLowerCase()],
|
|
633
|
+
factory: this.factoryAddress,
|
|
634
|
+
initCode: this.initCode,
|
|
635
|
+
feeFactor: this.feeFactor,
|
|
636
|
+
pools: [
|
|
637
|
+
{
|
|
638
|
+
address: pairParam.exchange,
|
|
639
|
+
fee: parseInt(pairParam.fee),
|
|
640
|
+
direction: pairParam.direction,
|
|
641
|
+
},
|
|
642
|
+
],
|
|
643
|
+
},
|
|
644
|
+
exchange: this.dexKey,
|
|
645
|
+
poolIdentifier,
|
|
646
|
+
gasCost: this.poolGasCost,
|
|
647
|
+
poolAddresses: [pairParam.exchange],
|
|
648
|
+
},
|
|
649
|
+
];
|
|
650
|
+
} catch (e) {
|
|
651
|
+
if (blockNumber === 0)
|
|
652
|
+
this.logger.error(
|
|
653
|
+
`Error_getPricesVolume: Aurelius block manager not yet instantiated`,
|
|
654
|
+
);
|
|
655
|
+
this.logger.error(`Error_getPrices:`, e);
|
|
656
|
+
return null;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
// Returns estimated gas cost of calldata for this DEX in multiSwap
|
|
661
|
+
getCalldataGasCost(poolPrices: PoolPrices<UniswapV2Data>): number | number[] {
|
|
662
|
+
return (
|
|
663
|
+
CALLDATA_GAS_COST.DEX_OVERHEAD +
|
|
664
|
+
CALLDATA_GAS_COST.LENGTH_SMALL +
|
|
665
|
+
// ParentStruct header
|
|
666
|
+
CALLDATA_GAS_COST.OFFSET_SMALL +
|
|
667
|
+
// ParentStruct -> weth
|
|
668
|
+
CALLDATA_GAS_COST.ADDRESS +
|
|
669
|
+
// ParentStruct -> pools[] header
|
|
670
|
+
CALLDATA_GAS_COST.OFFSET_SMALL +
|
|
671
|
+
// ParentStruct -> pools[]
|
|
672
|
+
CALLDATA_GAS_COST.LENGTH_SMALL +
|
|
673
|
+
// ParentStruct -> pools[0]
|
|
674
|
+
CALLDATA_GAS_COST.wordNonZeroBytes(22)
|
|
675
|
+
);
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
getAdapters(side: SwapSide): { name: string; index: number }[] | null {
|
|
679
|
+
return this.adapters[side];
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
async getTopPoolsForToken(
|
|
683
|
+
tokenAddress: Address,
|
|
684
|
+
count: number,
|
|
685
|
+
): Promise<PoolLiquidity[]> {
|
|
686
|
+
if (!this.subgraphURL) return [];
|
|
687
|
+
const query = `
|
|
688
|
+
query ($token: Bytes!, $count: Int) {
|
|
689
|
+
pools0: pairs(first: $count, orderBy: reserveUSD, orderDirection: desc, where: {token0: $token, reserve0_gt: 1, reserve1_gt: 1}) {
|
|
690
|
+
id
|
|
691
|
+
token0 {
|
|
692
|
+
id
|
|
693
|
+
decimals
|
|
694
|
+
}
|
|
695
|
+
token1 {
|
|
696
|
+
id
|
|
697
|
+
decimals
|
|
698
|
+
}
|
|
699
|
+
reserveUSD
|
|
700
|
+
}
|
|
701
|
+
pools1: pairs(first: $count, orderBy: reserveUSD, orderDirection: desc, where: {token1: $token, reserve0_gt: 1, reserve1_gt: 1}) {
|
|
702
|
+
id
|
|
703
|
+
token0 {
|
|
704
|
+
id
|
|
705
|
+
decimals
|
|
706
|
+
}
|
|
707
|
+
token1 {
|
|
708
|
+
id
|
|
709
|
+
decimals
|
|
710
|
+
}
|
|
711
|
+
reserveUSD
|
|
712
|
+
}
|
|
713
|
+
}`;
|
|
714
|
+
|
|
715
|
+
const { data } = await this.dexHelper.httpRequest.post(
|
|
716
|
+
this.subgraphURL,
|
|
717
|
+
{
|
|
718
|
+
query,
|
|
719
|
+
variables: { token: tokenAddress.toLowerCase(), count },
|
|
720
|
+
},
|
|
721
|
+
SUBGRAPH_TIMEOUT,
|
|
722
|
+
);
|
|
723
|
+
|
|
724
|
+
if (!(data && data.pools0 && data.pools1))
|
|
725
|
+
throw new Error("Couldn't fetch the pools from the subgraph");
|
|
726
|
+
const pools0 = _.map(data.pools0, pool => ({
|
|
727
|
+
exchange: this.dexKey,
|
|
728
|
+
address: pool.id.toLowerCase(),
|
|
729
|
+
connectorTokens: [
|
|
730
|
+
{
|
|
731
|
+
address: pool.token1.id.toLowerCase(),
|
|
732
|
+
decimals: parseInt(pool.token1.decimals),
|
|
733
|
+
},
|
|
734
|
+
],
|
|
735
|
+
liquidityUSD: parseFloat(pool.reserveUSD),
|
|
736
|
+
}));
|
|
737
|
+
|
|
738
|
+
const pools1 = _.map(data.pools1, pool => ({
|
|
739
|
+
exchange: this.dexKey,
|
|
740
|
+
address: pool.id.toLowerCase(),
|
|
741
|
+
connectorTokens: [
|
|
742
|
+
{
|
|
743
|
+
address: pool.token0.id.toLowerCase(),
|
|
744
|
+
decimals: parseInt(pool.token0.decimals),
|
|
745
|
+
},
|
|
746
|
+
],
|
|
747
|
+
liquidityUSD: parseFloat(pool.reserveUSD),
|
|
748
|
+
}));
|
|
749
|
+
|
|
750
|
+
return _.slice(
|
|
751
|
+
_.sortBy(_.concat(pools0, pools1), [pool => -1 * pool.liquidityUSD]),
|
|
752
|
+
0,
|
|
753
|
+
count,
|
|
754
|
+
);
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
protected fixPath(path: Address[], srcToken: Address, destToken: Address) {
|
|
758
|
+
return path.map((token: string, i: number) => {
|
|
759
|
+
if (
|
|
760
|
+
(i === 0 && srcToken.toLowerCase() === ETHER_ADDRESS.toLowerCase()) ||
|
|
761
|
+
(i === path.length - 1 &&
|
|
762
|
+
destToken.toLowerCase() === ETHER_ADDRESS.toLowerCase())
|
|
763
|
+
)
|
|
764
|
+
return ETHER_ADDRESS;
|
|
765
|
+
return token;
|
|
766
|
+
});
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
getWETHAddress(srcToken: Address, destToken: Address, weth?: Address) {
|
|
770
|
+
if (!isETHAddress(srcToken) && !isETHAddress(destToken))
|
|
771
|
+
return NULL_ADDRESS;
|
|
772
|
+
return weth || this.dexHelper.config.data.wrappedNativeTokenAddress;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
getAdapterParam(
|
|
776
|
+
srcToken: Address,
|
|
777
|
+
destToken: Address,
|
|
778
|
+
srcAmount: NumberAsString,
|
|
779
|
+
toAmount: NumberAsString, // required for buy case
|
|
780
|
+
data: UniswapData,
|
|
781
|
+
side: SwapSide,
|
|
782
|
+
): AdapterExchangeParam {
|
|
783
|
+
const pools = encodePools(data.pools, this.feeFactor);
|
|
784
|
+
const weth = this.getWETHAddress(srcToken, destToken, data.weth);
|
|
785
|
+
const payload = this.abiCoder.encodeParameter(
|
|
786
|
+
{
|
|
787
|
+
ParentStruct: {
|
|
788
|
+
weth: 'address',
|
|
789
|
+
pools: 'uint256[]',
|
|
790
|
+
},
|
|
791
|
+
},
|
|
792
|
+
{ pools, weth },
|
|
793
|
+
);
|
|
794
|
+
return {
|
|
795
|
+
targetExchange: data.router,
|
|
796
|
+
payload,
|
|
797
|
+
networkFee: '0',
|
|
798
|
+
};
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
async getSimpleParam(
|
|
802
|
+
src: Address,
|
|
803
|
+
dest: Address,
|
|
804
|
+
srcAmount: NumberAsString,
|
|
805
|
+
destAmount: NumberAsString,
|
|
806
|
+
data: UniswapData,
|
|
807
|
+
side: SwapSide,
|
|
808
|
+
): Promise<SimpleExchangeParam> {
|
|
809
|
+
const pools = encodePools(data.pools, this.feeFactor);
|
|
810
|
+
const weth = this.getWETHAddress(src, dest, data.weth);
|
|
811
|
+
const swapData = this.exchangeRouterInterface.encodeFunctionData(
|
|
812
|
+
side === SwapSide.SELL ? UniswapV2Functions.swap : UniswapV2Functions.buy,
|
|
813
|
+
[src, srcAmount, destAmount, weth, pools],
|
|
814
|
+
);
|
|
815
|
+
|
|
816
|
+
const hasRebaseTokenSrc = rebaseTokensSetsByChain[this.network]?.has(
|
|
817
|
+
src.toLowerCase(),
|
|
818
|
+
);
|
|
819
|
+
const hasRebaseTokenDest = rebaseTokensSetsByChain[this.network]?.has(
|
|
820
|
+
dest.toLowerCase(),
|
|
821
|
+
);
|
|
822
|
+
|
|
823
|
+
const maybeSyncCall =
|
|
824
|
+
hasRebaseTokenSrc || hasRebaseTokenDest
|
|
825
|
+
? {
|
|
826
|
+
callees: [
|
|
827
|
+
hasRebaseTokenSrc
|
|
828
|
+
? data.pools[0].address
|
|
829
|
+
: data.pools[data.pools.length - 1].address,
|
|
830
|
+
],
|
|
831
|
+
calldata: [uniswapV2PoolIface.encodeFunctionData('sync')],
|
|
832
|
+
values: ['0'],
|
|
833
|
+
}
|
|
834
|
+
: undefined;
|
|
835
|
+
|
|
836
|
+
return this.buildSimpleParamWithoutWETHConversion(
|
|
837
|
+
src,
|
|
838
|
+
srcAmount,
|
|
839
|
+
dest,
|
|
840
|
+
destAmount,
|
|
841
|
+
swapData,
|
|
842
|
+
data.router,
|
|
843
|
+
data.router,
|
|
844
|
+
'0',
|
|
845
|
+
maybeSyncCall,
|
|
846
|
+
);
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
// TODO: Rebase tokens handling?
|
|
850
|
+
getDexParam(
|
|
851
|
+
srcToken: Address,
|
|
852
|
+
destToken: Address,
|
|
853
|
+
srcAmount: NumberAsString,
|
|
854
|
+
destAmount: NumberAsString,
|
|
855
|
+
recipient: Address,
|
|
856
|
+
data: UniswapData,
|
|
857
|
+
side: SwapSide,
|
|
858
|
+
): DexExchangeParam {
|
|
859
|
+
const pools = encodePools(data.pools, this.feeFactor);
|
|
860
|
+
|
|
861
|
+
let exchangeData: string;
|
|
862
|
+
let specialDexFlag: SpecialDex;
|
|
863
|
+
let transferSrcTokenBeforeSwap: Address | undefined;
|
|
864
|
+
let targetExchange: Address;
|
|
865
|
+
let dexFuncHasRecipient: boolean;
|
|
866
|
+
|
|
867
|
+
// if (this.dexKey === 'BakerySwap') {
|
|
868
|
+
// const weth = this.getWETHAddress(srcToken, destToken, data.weth);
|
|
869
|
+
|
|
870
|
+
// exchangeData = this.exchangeRouterInterface.encodeFunctionData(
|
|
871
|
+
// UniswapV2Functions.swap,
|
|
872
|
+
// [srcToken, srcAmount, destAmount, weth, pools],
|
|
873
|
+
// );
|
|
874
|
+
// specialDexFlag = SpecialDex.DEFAULT;
|
|
875
|
+
// targetExchange = data.router;
|
|
876
|
+
// dexFuncHasRecipient = false;
|
|
877
|
+
// } else if (side === SwapSide.SELL) {
|
|
878
|
+
|
|
879
|
+
if (side === SwapSide.SELL) {
|
|
880
|
+
// 28 bytes are prepended in the Bytecode builder
|
|
881
|
+
const exchangeDataTypes = ['bytes4', 'bytes32', 'bytes32'];
|
|
882
|
+
const exchangeDataToPack = [
|
|
883
|
+
hexZeroPad(hexlify(0), 4),
|
|
884
|
+
hexZeroPad(hexlify(data.pools.length), 32), // pool count
|
|
885
|
+
hexZeroPad(hexlify(BigNumber.from(srcAmount)), 32),
|
|
886
|
+
];
|
|
887
|
+
pools.forEach(pool => {
|
|
888
|
+
exchangeDataTypes.push('bytes32');
|
|
889
|
+
exchangeDataToPack.push(hexZeroPad(hexlify(BigNumber.from(pool)), 32));
|
|
890
|
+
});
|
|
891
|
+
|
|
892
|
+
exchangeData = solidityPack(exchangeDataTypes, exchangeDataToPack);
|
|
893
|
+
specialDexFlag = SpecialDex.SWAP_ON_UNISWAP_V2_FORK;
|
|
894
|
+
transferSrcTokenBeforeSwap = data.pools[0].address;
|
|
895
|
+
targetExchange = recipient;
|
|
896
|
+
dexFuncHasRecipient = true;
|
|
897
|
+
} else {
|
|
898
|
+
const weth = this.getWETHAddress(srcToken, destToken, data.weth);
|
|
899
|
+
|
|
900
|
+
exchangeData = this.exchangeRouterInterface.encodeFunctionData(
|
|
901
|
+
UniswapV2Functions.buy,
|
|
902
|
+
[srcToken, srcAmount, destAmount, weth, pools],
|
|
903
|
+
);
|
|
904
|
+
specialDexFlag = SpecialDex.DEFAULT;
|
|
905
|
+
targetExchange = data.router;
|
|
906
|
+
dexFuncHasRecipient = false;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
return {
|
|
910
|
+
needWrapNative: this.needWrapNative,
|
|
911
|
+
specialDexSupportsInsertFromAmount: true,
|
|
912
|
+
dexFuncHasRecipient,
|
|
913
|
+
exchangeData,
|
|
914
|
+
targetExchange,
|
|
915
|
+
specialDexFlag,
|
|
916
|
+
transferSrcTokenBeforeSwap,
|
|
917
|
+
returnAmountPos: undefined,
|
|
918
|
+
};
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
// TODO: Move to new uniswapv2&forks router interface
|
|
922
|
+
getDirectParam(
|
|
923
|
+
srcToken: Address,
|
|
924
|
+
destToken: Address,
|
|
925
|
+
srcAmount: NumberAsString,
|
|
926
|
+
destAmount: NumberAsString,
|
|
927
|
+
expectedAmount: NumberAsString,
|
|
928
|
+
_data: UniswapData,
|
|
929
|
+
side: SwapSide,
|
|
930
|
+
permit: string,
|
|
931
|
+
uuid: string,
|
|
932
|
+
feePercent: NumberAsString,
|
|
933
|
+
deadline: NumberAsString,
|
|
934
|
+
partner: string,
|
|
935
|
+
beneficiary: string,
|
|
936
|
+
contractMethod?: string,
|
|
937
|
+
): TxInfo<UniswapParam> {
|
|
938
|
+
if (!contractMethod) throw new Error(`contractMethod need to be passed`);
|
|
939
|
+
if (permit !== '0x') contractMethod += 'WithPermit';
|
|
940
|
+
|
|
941
|
+
const swapParams = ((): UniswapParam => {
|
|
942
|
+
const data = _data as unknown as UniswapDataLegacy;
|
|
943
|
+
const path = this.fixPath(data.path, srcToken, destToken);
|
|
944
|
+
|
|
945
|
+
switch (contractMethod) {
|
|
946
|
+
case UniswapV2Functions.swapOnUniswap:
|
|
947
|
+
case UniswapV2Functions.buyOnUniswap:
|
|
948
|
+
return [srcAmount, destAmount, path];
|
|
949
|
+
|
|
950
|
+
case UniswapV2Functions.swapOnUniswapFork:
|
|
951
|
+
case UniswapV2Functions.buyOnUniswapFork:
|
|
952
|
+
return [
|
|
953
|
+
data.factory,
|
|
954
|
+
prependWithOx(data.initCode),
|
|
955
|
+
srcAmount,
|
|
956
|
+
destAmount,
|
|
957
|
+
path,
|
|
958
|
+
];
|
|
959
|
+
|
|
960
|
+
case UniswapV2Functions.swapOnUniswapV2Fork:
|
|
961
|
+
case UniswapV2Functions.buyOnUniswapV2Fork:
|
|
962
|
+
return [
|
|
963
|
+
srcToken,
|
|
964
|
+
srcAmount,
|
|
965
|
+
destAmount,
|
|
966
|
+
this.getWETHAddress(srcToken, destToken, _data.weth),
|
|
967
|
+
encodePools(_data.pools, this.feeFactor),
|
|
968
|
+
];
|
|
969
|
+
|
|
970
|
+
case UniswapV2Functions.swapOnUniswapV2ForkWithPermit:
|
|
971
|
+
case UniswapV2Functions.buyOnUniswapV2ForkWithPermit:
|
|
972
|
+
return [
|
|
973
|
+
srcToken,
|
|
974
|
+
srcAmount,
|
|
975
|
+
destAmount,
|
|
976
|
+
this.getWETHAddress(srcToken, destToken, _data.weth),
|
|
977
|
+
encodePools(_data.pools, this.feeFactor),
|
|
978
|
+
permit,
|
|
979
|
+
];
|
|
980
|
+
|
|
981
|
+
default:
|
|
982
|
+
throw new Error(`contractMethod=${contractMethod} is not supported`);
|
|
983
|
+
}
|
|
984
|
+
})();
|
|
985
|
+
|
|
986
|
+
const encoder = (...params: UniswapParam) =>
|
|
987
|
+
this.routerInterface.encodeFunctionData(contractMethod!, params);
|
|
988
|
+
return {
|
|
989
|
+
params: swapParams,
|
|
990
|
+
encoder,
|
|
991
|
+
networkFee: '0',
|
|
992
|
+
};
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
static getDirectFunctionName(): string[] {
|
|
996
|
+
return this.directFunctionName;
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
getDirectParamV6(
|
|
1000
|
+
srcToken: Address,
|
|
1001
|
+
destToken: Address,
|
|
1002
|
+
fromAmount: NumberAsString,
|
|
1003
|
+
toAmount: NumberAsString,
|
|
1004
|
+
quotedAmount: NumberAsString,
|
|
1005
|
+
data: UniswapV2Data,
|
|
1006
|
+
side: SwapSide,
|
|
1007
|
+
permit: string,
|
|
1008
|
+
uuid: string,
|
|
1009
|
+
partnerAndFee: string,
|
|
1010
|
+
beneficiary: string,
|
|
1011
|
+
blockNumber: number,
|
|
1012
|
+
contractMethod?: string,
|
|
1013
|
+
) {
|
|
1014
|
+
if (!contractMethod) throw new Error(`contractMethod need to be passed`);
|
|
1015
|
+
if (!UniswapV2.getDirectFunctionNameV6().includes(contractMethod!)) {
|
|
1016
|
+
throw new Error(`Invalid contract method ${contractMethod}`);
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
const { path, pools } = data;
|
|
1020
|
+
const length = path.length;
|
|
1021
|
+
const encodedPath = path.reduce((acc, _, i) => {
|
|
1022
|
+
if (i >= length - 1) return acc;
|
|
1023
|
+
|
|
1024
|
+
const p = this._encodePathV6(
|
|
1025
|
+
{
|
|
1026
|
+
srcToken: path[i],
|
|
1027
|
+
destToken: path[i + 1],
|
|
1028
|
+
direction: pools[i].direction,
|
|
1029
|
+
},
|
|
1030
|
+
side,
|
|
1031
|
+
data.wethAddress,
|
|
1032
|
+
).replace('0x', '');
|
|
1033
|
+
|
|
1034
|
+
return acc + p;
|
|
1035
|
+
}, '0x');
|
|
1036
|
+
|
|
1037
|
+
const metadata = hexConcat([
|
|
1038
|
+
hexZeroPad(uuidToBytes16(uuid), 16),
|
|
1039
|
+
hexZeroPad(hexlify(blockNumber), 16),
|
|
1040
|
+
]);
|
|
1041
|
+
|
|
1042
|
+
const uniData: UniswapV2ParamsDirectBase = [
|
|
1043
|
+
srcToken,
|
|
1044
|
+
destToken,
|
|
1045
|
+
fromAmount,
|
|
1046
|
+
toAmount,
|
|
1047
|
+
quotedAmount,
|
|
1048
|
+
metadata,
|
|
1049
|
+
beneficiary,
|
|
1050
|
+
encodedPath,
|
|
1051
|
+
];
|
|
1052
|
+
|
|
1053
|
+
const swapParams: UniswapV2ParamsDirect = [uniData, partnerAndFee, permit];
|
|
1054
|
+
|
|
1055
|
+
const encoder = (...params: (string | UniswapV2ParamsDirect)[]) => {
|
|
1056
|
+
return this.augustusV6Interface.encodeFunctionData(
|
|
1057
|
+
side === SwapSide.SELL
|
|
1058
|
+
? UniswapV2FunctionsV6.swap
|
|
1059
|
+
: UniswapV2FunctionsV6.buy,
|
|
1060
|
+
[...params],
|
|
1061
|
+
);
|
|
1062
|
+
};
|
|
1063
|
+
|
|
1064
|
+
return {
|
|
1065
|
+
params: swapParams,
|
|
1066
|
+
encoder,
|
|
1067
|
+
networkFee: '0',
|
|
1068
|
+
};
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
static getDirectFunctionNameV6(): string[] {
|
|
1072
|
+
return this.directFunctionNameV6;
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
// univ2 always had 1 pool per pair
|
|
1076
|
+
private _encodePathV6(
|
|
1077
|
+
path: {
|
|
1078
|
+
srcToken: Address;
|
|
1079
|
+
destToken: Address;
|
|
1080
|
+
direction: boolean;
|
|
1081
|
+
},
|
|
1082
|
+
side: SwapSide,
|
|
1083
|
+
weth?: Address,
|
|
1084
|
+
): string {
|
|
1085
|
+
if (path == null) {
|
|
1086
|
+
this.logger.error(
|
|
1087
|
+
`${this.dexKey}: Received invalid path=${path} for side=${side} to encode`,
|
|
1088
|
+
);
|
|
1089
|
+
return '0x';
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
// v6 expects weth for eth in pools
|
|
1093
|
+
if (isETHAddress(path.srcToken)) {
|
|
1094
|
+
path.srcToken =
|
|
1095
|
+
weth || this.dexHelper.config.data.wrappedNativeTokenAddress;
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
if (isETHAddress(path.destToken)) {
|
|
1099
|
+
path.destToken =
|
|
1100
|
+
weth || this.dexHelper.config.data.wrappedNativeTokenAddress;
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
// contract expects tokens to be sorted, and direction switched in case sorting changes src/dest order
|
|
1104
|
+
const [srcTokenSorted, destTokenSorted] =
|
|
1105
|
+
BigInt(path.srcToken) > BigInt(path.destToken)
|
|
1106
|
+
? [path.destToken, path.srcToken]
|
|
1107
|
+
: [path.srcToken, path.destToken];
|
|
1108
|
+
|
|
1109
|
+
const direction = srcTokenSorted === path.srcToken ? 1 : 0;
|
|
1110
|
+
|
|
1111
|
+
const tokensEncoded = pack(
|
|
1112
|
+
['address', 'address'],
|
|
1113
|
+
[srcTokenSorted, destTokenSorted],
|
|
1114
|
+
);
|
|
1115
|
+
return tokensEncoded + '0'.repeat(47) + direction;
|
|
1116
|
+
}
|
|
1117
|
+
}
|