@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,687 +1,687 @@
|
|
|
1
|
-
import { UniswapV2 } from '../uniswap-v2/uniswap-v2';
|
|
2
|
-
import {
|
|
3
|
-
Network,
|
|
4
|
-
NULL_ADDRESS,
|
|
5
|
-
SUBGRAPH_TIMEOUT,
|
|
6
|
-
DEST_TOKEN_PARASWAP_TRANSFERS,
|
|
7
|
-
SRC_TOKEN_PARASWAP_TRANSFERS,
|
|
8
|
-
} from '../../constants';
|
|
9
|
-
import {
|
|
10
|
-
AdapterExchangeParam,
|
|
11
|
-
Address,
|
|
12
|
-
DexExchangeParam,
|
|
13
|
-
ExchangePrices,
|
|
14
|
-
PoolLiquidity,
|
|
15
|
-
SimpleExchangeParam,
|
|
16
|
-
Token,
|
|
17
|
-
TransferFeeParams,
|
|
18
|
-
} from '../../types';
|
|
19
|
-
import { IDexHelper } from '../../dex-helper';
|
|
20
|
-
import erc20ABI from '../../abi/erc20.json';
|
|
21
|
-
import { UniswapData, UniswapV2Data } from '../uniswap-v2/types';
|
|
22
|
-
import { getBigIntPow, getDexKeysWithNetwork } from '../../utils';
|
|
23
|
-
import solidlyFactoryABI from '../../abi/solidly/SolidlyFactory.json';
|
|
24
|
-
import solidlyPair from '../../abi/solidly/SolidlyPair.json';
|
|
25
|
-
import _ from 'lodash';
|
|
26
|
-
import { NumberAsString, SwapSide } from '@paraswap/core';
|
|
27
|
-
import { Interface, AbiCoder } from '@ethersproject/abi';
|
|
28
|
-
import { SolidlyStablePool } from './solidly-stable-pool';
|
|
29
|
-
import { Uniswapv2ConstantProductPool } from '../uniswap-v2/uniswap-v2-constant-product-pool';
|
|
30
|
-
import {
|
|
31
|
-
PoolState,
|
|
32
|
-
SolidlyData,
|
|
33
|
-
SolidlyPair,
|
|
34
|
-
SolidlyPool,
|
|
35
|
-
SolidlyPoolOrderedParams,
|
|
36
|
-
} from './types';
|
|
37
|
-
import { SolidlyConfig, Adapters } from './config';
|
|
38
|
-
import { applyTransferFee } from '../../lib/token-transfer-fee';
|
|
39
|
-
import {
|
|
40
|
-
hexDataLength,
|
|
41
|
-
hexZeroPad,
|
|
42
|
-
hexlify,
|
|
43
|
-
id,
|
|
44
|
-
solidityPack,
|
|
45
|
-
} from 'ethers/lib/utils';
|
|
46
|
-
import { BigNumber } from 'ethers';
|
|
47
|
-
import { Flag, SpecialDex } from '../../executor/types';
|
|
48
|
-
|
|
49
|
-
const erc20Iface = new Interface(erc20ABI);
|
|
50
|
-
const solidlyPairIface = new Interface(solidlyPair);
|
|
51
|
-
const defaultAbiCoder = new AbiCoder();
|
|
52
|
-
|
|
53
|
-
function encodePools(
|
|
54
|
-
pools: SolidlyPool[],
|
|
55
|
-
feeFactor: number,
|
|
56
|
-
): NumberAsString[] {
|
|
57
|
-
return pools.map(({ fee, direction, address }) => {
|
|
58
|
-
return (
|
|
59
|
-
(BigInt(feeFactor - fee) << 161n) +
|
|
60
|
-
((direction ? 0n : 1n) << 160n) +
|
|
61
|
-
BigInt(address)
|
|
62
|
-
).toString();
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export class Solidly extends UniswapV2 {
|
|
67
|
-
pairs: { [key: string]: SolidlyPair } = {};
|
|
68
|
-
stableFee?: number;
|
|
69
|
-
volatileFee?: number;
|
|
70
|
-
|
|
71
|
-
readonly isFeeOnTransferSupported: boolean = true;
|
|
72
|
-
readonly SRC_TOKEN_DEX_TRANSFERS = 1;
|
|
73
|
-
readonly DEST_TOKEN_DEX_TRANSFERS = 1;
|
|
74
|
-
|
|
75
|
-
public static dexKeysWithNetwork: { key: string; networks: Network[] }[] =
|
|
76
|
-
getDexKeysWithNetwork(
|
|
77
|
-
_.omit(SolidlyConfig, [
|
|
78
|
-
'Velodrome',
|
|
79
|
-
'VelodromeV2',
|
|
80
|
-
'Aerodrome',
|
|
81
|
-
'SpiritSwapV2',
|
|
82
|
-
'SolidlyV2',
|
|
83
|
-
'Thena',
|
|
84
|
-
'Chronos',
|
|
85
|
-
'Ramses',
|
|
86
|
-
'Equalizer',
|
|
87
|
-
'Velocimeter',
|
|
88
|
-
'Usdfi',
|
|
89
|
-
'PharaohV1',
|
|
90
|
-
]),
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
constructor(
|
|
94
|
-
protected network: Network,
|
|
95
|
-
dexKey: string,
|
|
96
|
-
protected dexHelper: IDexHelper,
|
|
97
|
-
isDynamicFees = false,
|
|
98
|
-
factoryAddress?: Address,
|
|
99
|
-
subgraphURL?: string,
|
|
100
|
-
initCode?: string,
|
|
101
|
-
feeCode?: number,
|
|
102
|
-
poolGasCost?: number,
|
|
103
|
-
routerAddress?: Address,
|
|
104
|
-
) {
|
|
105
|
-
super(
|
|
106
|
-
network,
|
|
107
|
-
dexKey,
|
|
108
|
-
dexHelper,
|
|
109
|
-
isDynamicFees,
|
|
110
|
-
factoryAddress !== undefined
|
|
111
|
-
? factoryAddress
|
|
112
|
-
: SolidlyConfig[dexKey][network].factoryAddress,
|
|
113
|
-
subgraphURL === ''
|
|
114
|
-
? undefined
|
|
115
|
-
: subgraphURL !== undefined
|
|
116
|
-
? subgraphURL
|
|
117
|
-
: SolidlyConfig[dexKey][network].subgraphURL,
|
|
118
|
-
initCode !== undefined
|
|
119
|
-
? initCode
|
|
120
|
-
: SolidlyConfig[dexKey][network].initCode,
|
|
121
|
-
feeCode !== undefined ? feeCode : SolidlyConfig[dexKey][network].feeCode,
|
|
122
|
-
poolGasCost !== undefined
|
|
123
|
-
? poolGasCost
|
|
124
|
-
: SolidlyConfig[dexKey][network].poolGasCost,
|
|
125
|
-
solidlyPairIface,
|
|
126
|
-
Adapters[network] || undefined,
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
this.stableFee = SolidlyConfig[dexKey][network].stableFee;
|
|
130
|
-
this.volatileFee = SolidlyConfig[dexKey][network].volatileFee;
|
|
131
|
-
|
|
132
|
-
this.factory = new dexHelper.web3Provider.eth.Contract(
|
|
133
|
-
solidlyFactoryABI as any,
|
|
134
|
-
factoryAddress !== undefined
|
|
135
|
-
? factoryAddress
|
|
136
|
-
: SolidlyConfig[dexKey][network].factoryAddress,
|
|
137
|
-
);
|
|
138
|
-
|
|
139
|
-
this.router =
|
|
140
|
-
routerAddress !== undefined
|
|
141
|
-
? routerAddress
|
|
142
|
-
: SolidlyConfig[dexKey][network].router || '';
|
|
143
|
-
|
|
144
|
-
this.feeFactor = SolidlyConfig[dexKey][network].feeFactor || this.feeFactor;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
async findSolidlyPair(from: Token, to: Token, stable: boolean) {
|
|
148
|
-
if (from.address.toLowerCase() === to.address.toLowerCase()) return null;
|
|
149
|
-
const [token0, token1] =
|
|
150
|
-
from.address.toLowerCase() < to.address.toLowerCase()
|
|
151
|
-
? [from, to]
|
|
152
|
-
: [to, from];
|
|
153
|
-
|
|
154
|
-
const typePostfix = this.poolPostfix(stable);
|
|
155
|
-
const key = `${token0.address.toLowerCase()}-${token1.address.toLowerCase()}-${typePostfix}`;
|
|
156
|
-
let pair = this.pairs[key];
|
|
157
|
-
if (pair) return pair;
|
|
158
|
-
|
|
159
|
-
let exchange = await this.factory.methods
|
|
160
|
-
// Solidly has additional boolean parameter "StablePool"
|
|
161
|
-
// At first we look for uniswap-like volatile pool
|
|
162
|
-
.getPair(token0.address, token1.address, stable)
|
|
163
|
-
.call();
|
|
164
|
-
|
|
165
|
-
if (exchange === NULL_ADDRESS) {
|
|
166
|
-
pair = { token0, token1, stable };
|
|
167
|
-
} else {
|
|
168
|
-
pair = { token0, token1, exchange, stable };
|
|
169
|
-
}
|
|
170
|
-
this.pairs[key] = pair;
|
|
171
|
-
return pair;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
async batchCatchUpPairs(pairs: [Token, Token][], blockNumber: number) {
|
|
175
|
-
if (!blockNumber) return;
|
|
176
|
-
const pairsToFetch: SolidlyPair[] = [];
|
|
177
|
-
for (const _pair of pairs) {
|
|
178
|
-
for (const stable of [false, true]) {
|
|
179
|
-
const pair = await this.findSolidlyPair(_pair[0], _pair[1], stable);
|
|
180
|
-
if (!(pair && pair.exchange)) continue;
|
|
181
|
-
if (!pair.pool) {
|
|
182
|
-
pairsToFetch.push(pair);
|
|
183
|
-
} else if (!pair.pool.getState(blockNumber)) {
|
|
184
|
-
pairsToFetch.push(pair);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
if (!pairsToFetch.length) return;
|
|
190
|
-
|
|
191
|
-
const reserves = await this.getManyPoolReserves(pairsToFetch, blockNumber);
|
|
192
|
-
|
|
193
|
-
if (reserves.length !== pairsToFetch.length) {
|
|
194
|
-
this.logger.error(
|
|
195
|
-
`Error_getManyPoolReserves didn't get any pool reserves`,
|
|
196
|
-
);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
for (let i = 0; i < pairsToFetch.length; i++) {
|
|
200
|
-
const pairState = reserves[i];
|
|
201
|
-
const pair = pairsToFetch[i];
|
|
202
|
-
if (!pair.pool) {
|
|
203
|
-
await this.addPool(
|
|
204
|
-
pair,
|
|
205
|
-
pairState.reserves0,
|
|
206
|
-
pairState.reserves1,
|
|
207
|
-
pairState.feeCode,
|
|
208
|
-
blockNumber,
|
|
209
|
-
);
|
|
210
|
-
} else pair.pool.setState(pairState, blockNumber);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
async getManyPoolReserves(
|
|
215
|
-
pairs: SolidlyPair[],
|
|
216
|
-
blockNumber: number,
|
|
217
|
-
): Promise<PoolState[]> {
|
|
218
|
-
try {
|
|
219
|
-
const multiCallFeeData = pairs.map(pair =>
|
|
220
|
-
this.getFeesMultiCallData(pair),
|
|
221
|
-
);
|
|
222
|
-
const calldata = pairs
|
|
223
|
-
.map((pair, i) => {
|
|
224
|
-
let calldata = [
|
|
225
|
-
{
|
|
226
|
-
target: pair.token0.address,
|
|
227
|
-
callData: erc20Iface.encodeFunctionData('balanceOf', [
|
|
228
|
-
pair.exchange!,
|
|
229
|
-
]),
|
|
230
|
-
},
|
|
231
|
-
{
|
|
232
|
-
target: pair.token1.address,
|
|
233
|
-
callData: erc20Iface.encodeFunctionData('balanceOf', [
|
|
234
|
-
pair.exchange!,
|
|
235
|
-
]),
|
|
236
|
-
},
|
|
237
|
-
];
|
|
238
|
-
if (this.isDynamicFees) calldata.push(multiCallFeeData[i]!.callEntry);
|
|
239
|
-
return calldata;
|
|
240
|
-
})
|
|
241
|
-
.flat();
|
|
242
|
-
|
|
243
|
-
const data: { returnData: any[] } =
|
|
244
|
-
await this.dexHelper.multiContract.methods
|
|
245
|
-
.aggregate(calldata)
|
|
246
|
-
.call({}, blockNumber);
|
|
247
|
-
|
|
248
|
-
const returnData = _.chunk(data.returnData, this.isDynamicFees ? 3 : 2);
|
|
249
|
-
|
|
250
|
-
return pairs.map((pair, i) => ({
|
|
251
|
-
reserves0: defaultAbiCoder
|
|
252
|
-
.decode(['uint256'], returnData[i][0])[0]
|
|
253
|
-
.toString(),
|
|
254
|
-
reserves1: defaultAbiCoder
|
|
255
|
-
.decode(['uint256'], returnData[i][1])[0]
|
|
256
|
-
.toString(),
|
|
257
|
-
feeCode: this.isDynamicFees
|
|
258
|
-
? multiCallFeeData[i]!.callDecoder(returnData[i][2])
|
|
259
|
-
: (pair.stable ? this.stableFee : this.volatileFee) || this.feeCode,
|
|
260
|
-
}));
|
|
261
|
-
} catch (e) {
|
|
262
|
-
this.logger.error(
|
|
263
|
-
`Error_getManyPoolReserves could not get reserves with error:`,
|
|
264
|
-
e,
|
|
265
|
-
);
|
|
266
|
-
return [];
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
async getSellPrice(
|
|
271
|
-
priceParams: SolidlyPoolOrderedParams,
|
|
272
|
-
srcAmount: bigint,
|
|
273
|
-
): Promise<bigint> {
|
|
274
|
-
return priceParams.stable
|
|
275
|
-
? SolidlyStablePool.getSellPrice(priceParams, srcAmount, this.feeFactor)
|
|
276
|
-
: Uniswapv2ConstantProductPool.getSellPrice(
|
|
277
|
-
priceParams,
|
|
278
|
-
srcAmount,
|
|
279
|
-
this.feeFactor,
|
|
280
|
-
);
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
async getBuyPrice(
|
|
284
|
-
priceParams: SolidlyPoolOrderedParams,
|
|
285
|
-
srcAmount: bigint,
|
|
286
|
-
): Promise<bigint> {
|
|
287
|
-
if (priceParams.stable) throw new Error(`Buy not supported`);
|
|
288
|
-
return Uniswapv2ConstantProductPool.getBuyPrice(
|
|
289
|
-
priceParams,
|
|
290
|
-
srcAmount,
|
|
291
|
-
this.feeFactor,
|
|
292
|
-
);
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
async getPricesVolume(
|
|
296
|
-
_from: Token,
|
|
297
|
-
_to: Token,
|
|
298
|
-
amounts: bigint[],
|
|
299
|
-
side: SwapSide,
|
|
300
|
-
blockNumber: number,
|
|
301
|
-
// list of pool identifiers to use for pricing, if undefined use all pools
|
|
302
|
-
limitPools?: string[],
|
|
303
|
-
transferFees: TransferFeeParams = {
|
|
304
|
-
srcFee: 0,
|
|
305
|
-
destFee: 0,
|
|
306
|
-
srcDexFee: 0,
|
|
307
|
-
destDexFee: 0,
|
|
308
|
-
},
|
|
309
|
-
): Promise<ExchangePrices<UniswapV2Data> | null> {
|
|
310
|
-
try {
|
|
311
|
-
if (side === SwapSide.BUY) return null; // Buy side not implemented yet
|
|
312
|
-
const from = this.dexHelper.config.wrapETH(_from);
|
|
313
|
-
const to = this.dexHelper.config.wrapETH(_to);
|
|
314
|
-
|
|
315
|
-
if (from.address.toLowerCase() === to.address.toLowerCase()) {
|
|
316
|
-
return null;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
const tokenAddress = [
|
|
320
|
-
from.address.toLowerCase(),
|
|
321
|
-
to.address.toLowerCase(),
|
|
322
|
-
]
|
|
323
|
-
.sort((a, b) => (a > b ? 1 : -1))
|
|
324
|
-
.join('_');
|
|
325
|
-
|
|
326
|
-
await this.batchCatchUpPairs([[from, to]], blockNumber);
|
|
327
|
-
|
|
328
|
-
const resultPromises = [false, true].map(async stable => {
|
|
329
|
-
// We don't support fee on transfer for stable pools yet
|
|
330
|
-
if (
|
|
331
|
-
stable &&
|
|
332
|
-
(transferFees.srcFee !== 0 || transferFees.srcDexFee !== 0)
|
|
333
|
-
) {
|
|
334
|
-
return null;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
const poolIdentifier =
|
|
338
|
-
`${this.dexKey}_${tokenAddress}` + this.poolPostfix(stable);
|
|
339
|
-
|
|
340
|
-
if (limitPools && limitPools.every(p => p !== poolIdentifier))
|
|
341
|
-
return null;
|
|
342
|
-
|
|
343
|
-
const isSell = side === SwapSide.SELL;
|
|
344
|
-
const pairParam = await this.getSolidlyPairOrderedParams(
|
|
345
|
-
from,
|
|
346
|
-
to,
|
|
347
|
-
blockNumber,
|
|
348
|
-
stable,
|
|
349
|
-
transferFees.srcDexFee,
|
|
350
|
-
);
|
|
351
|
-
|
|
352
|
-
if (!pairParam) return null;
|
|
353
|
-
|
|
354
|
-
const unitAmount = getBigIntPow(
|
|
355
|
-
// @ts-expect-error Buy side is not implemented yet
|
|
356
|
-
side === SwapSide.BUY ? to.decimals : from.decimals,
|
|
357
|
-
);
|
|
358
|
-
|
|
359
|
-
const [unitVolumeWithFee, ...amountsWithFee] = applyTransferFee(
|
|
360
|
-
[unitAmount, ...amounts],
|
|
361
|
-
side,
|
|
362
|
-
isSell ? transferFees.srcFee : transferFees.destFee,
|
|
363
|
-
isSell ? SRC_TOKEN_PARASWAP_TRANSFERS : DEST_TOKEN_PARASWAP_TRANSFERS,
|
|
364
|
-
);
|
|
365
|
-
|
|
366
|
-
const unit =
|
|
367
|
-
// @ts-expect-error Buy side is not implemented yet
|
|
368
|
-
side === SwapSide.BUY
|
|
369
|
-
? await this.getBuyPricePath(unitVolumeWithFee, [pairParam])
|
|
370
|
-
: await this.getSellPricePath(unitVolumeWithFee, [pairParam]);
|
|
371
|
-
|
|
372
|
-
const prices =
|
|
373
|
-
// @ts-expect-error Buy side is not implemented yet
|
|
374
|
-
side === SwapSide.BUY
|
|
375
|
-
? await Promise.all(
|
|
376
|
-
amountsWithFee.map(amount =>
|
|
377
|
-
amount === 0n
|
|
378
|
-
? 0n
|
|
379
|
-
: this.getBuyPricePath(amount, [pairParam]),
|
|
380
|
-
),
|
|
381
|
-
)
|
|
382
|
-
: await Promise.all(
|
|
383
|
-
amountsWithFee.map(amount =>
|
|
384
|
-
amount === 0n
|
|
385
|
-
? 0n
|
|
386
|
-
: this.getSellPricePath(amount, [pairParam]),
|
|
387
|
-
),
|
|
388
|
-
);
|
|
389
|
-
|
|
390
|
-
const [unitOutWithFee, ...outputsWithFee] = applyTransferFee(
|
|
391
|
-
[unit, ...prices],
|
|
392
|
-
side,
|
|
393
|
-
// This part is confusing, because we treat differently SELL and BUY fees
|
|
394
|
-
// If Buy, we should apply transfer fee on srcToken on top of dexFee applied earlier
|
|
395
|
-
// But for Sell we should apply only one dexFee
|
|
396
|
-
isSell ? transferFees.destDexFee : transferFees.srcFee,
|
|
397
|
-
isSell ? this.DEST_TOKEN_DEX_TRANSFERS : SRC_TOKEN_PARASWAP_TRANSFERS,
|
|
398
|
-
);
|
|
399
|
-
|
|
400
|
-
return {
|
|
401
|
-
prices: outputsWithFee,
|
|
402
|
-
unit: unitOutWithFee,
|
|
403
|
-
data: {
|
|
404
|
-
router: this.router,
|
|
405
|
-
path: [from.address.toLowerCase(), to.address.toLowerCase()],
|
|
406
|
-
factory: this.factoryAddress,
|
|
407
|
-
initCode: this.initCode,
|
|
408
|
-
feeFactor: this.feeFactor,
|
|
409
|
-
isFeeTokenInRoute: Object.values(transferFees).some(f => f !== 0),
|
|
410
|
-
pools: [
|
|
411
|
-
{
|
|
412
|
-
address: pairParam.exchange,
|
|
413
|
-
fee: parseInt(pairParam.fee),
|
|
414
|
-
direction: pairParam.direction,
|
|
415
|
-
},
|
|
416
|
-
],
|
|
417
|
-
},
|
|
418
|
-
exchange: this.dexKey,
|
|
419
|
-
poolIdentifier,
|
|
420
|
-
gasCost: this.poolGasCost,
|
|
421
|
-
poolAddresses: [pairParam.exchange],
|
|
422
|
-
};
|
|
423
|
-
});
|
|
424
|
-
|
|
425
|
-
const resultPools = (await Promise.all(
|
|
426
|
-
resultPromises,
|
|
427
|
-
)) as ExchangePrices<UniswapV2Data>;
|
|
428
|
-
const resultPoolsFiltered = resultPools.filter(item => !!item); // filter null elements
|
|
429
|
-
return resultPoolsFiltered.length > 0 ? resultPoolsFiltered : null;
|
|
430
|
-
} catch (e) {
|
|
431
|
-
if (blockNumber === 0)
|
|
432
|
-
this.logger.error(
|
|
433
|
-
`Error_getPricesVolume: Aurelius block manager not yet instantiated`,
|
|
434
|
-
);
|
|
435
|
-
this.logger.error(`Error_getPrices:`, e);
|
|
436
|
-
return null;
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
async getTopPoolsForToken(
|
|
441
|
-
tokenAddress: Address,
|
|
442
|
-
count: number,
|
|
443
|
-
): Promise<PoolLiquidity[]> {
|
|
444
|
-
if (!this.subgraphURL) return [];
|
|
445
|
-
|
|
446
|
-
let stableFieldKey = '';
|
|
447
|
-
|
|
448
|
-
if (this.dexKey.toLowerCase() === 'solidly') {
|
|
449
|
-
stableFieldKey = 'stable';
|
|
450
|
-
} else if (this.dexKey.toLowerCase() !== 'solidlyv2') {
|
|
451
|
-
stableFieldKey = 'isStable';
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
const query = `query ($token: Bytes!, $count: Int) {
|
|
455
|
-
pools0: pairs(first: $count, orderBy: reserveUSD, orderDirection: desc, where: {token0: $token, reserve0_gt: 1, reserve1_gt: 1}) {
|
|
456
|
-
id
|
|
457
|
-
${stableFieldKey}
|
|
458
|
-
token0 {
|
|
459
|
-
id
|
|
460
|
-
decimals
|
|
461
|
-
}
|
|
462
|
-
token1 {
|
|
463
|
-
id
|
|
464
|
-
decimals
|
|
465
|
-
}
|
|
466
|
-
reserveUSD
|
|
467
|
-
}
|
|
468
|
-
pools1: pairs(first: $count, orderBy: reserveUSD, orderDirection: desc, where: {token1: $token, reserve0_gt: 1, reserve1_gt: 1}) {
|
|
469
|
-
id
|
|
470
|
-
${stableFieldKey}
|
|
471
|
-
token0 {
|
|
472
|
-
id
|
|
473
|
-
decimals
|
|
474
|
-
}
|
|
475
|
-
token1 {
|
|
476
|
-
id
|
|
477
|
-
decimals
|
|
478
|
-
}
|
|
479
|
-
reserveUSD
|
|
480
|
-
}
|
|
481
|
-
}`;
|
|
482
|
-
|
|
483
|
-
const { data } = await this.dexHelper.httpRequest.post(
|
|
484
|
-
this.subgraphURL,
|
|
485
|
-
{
|
|
486
|
-
query,
|
|
487
|
-
variables: { token: tokenAddress.toLowerCase(), count },
|
|
488
|
-
},
|
|
489
|
-
SUBGRAPH_TIMEOUT,
|
|
490
|
-
);
|
|
491
|
-
|
|
492
|
-
if (!(data && data.pools0 && data.pools1))
|
|
493
|
-
throw new Error("Couldn't fetch the pools from the subgraph");
|
|
494
|
-
const pools0 = _.map(data.pools0, pool => ({
|
|
495
|
-
exchange: this.dexKey,
|
|
496
|
-
stable: pool[stableFieldKey],
|
|
497
|
-
address: pool.id.toLowerCase(),
|
|
498
|
-
connectorTokens: [
|
|
499
|
-
{
|
|
500
|
-
address: pool.token1.id.toLowerCase(),
|
|
501
|
-
decimals: parseInt(pool.token1.decimals),
|
|
502
|
-
},
|
|
503
|
-
],
|
|
504
|
-
liquidityUSD: parseFloat(pool.reserveUSD),
|
|
505
|
-
}));
|
|
506
|
-
|
|
507
|
-
const pools1 = _.map(data.pools1, pool => ({
|
|
508
|
-
exchange: this.dexKey,
|
|
509
|
-
stable: pool[stableFieldKey],
|
|
510
|
-
address: pool.id.toLowerCase(),
|
|
511
|
-
connectorTokens: [
|
|
512
|
-
{
|
|
513
|
-
address: pool.token0.id.toLowerCase(),
|
|
514
|
-
decimals: parseInt(pool.token0.decimals),
|
|
515
|
-
},
|
|
516
|
-
],
|
|
517
|
-
liquidityUSD: parseFloat(pool.reserveUSD),
|
|
518
|
-
}));
|
|
519
|
-
|
|
520
|
-
return _.slice(
|
|
521
|
-
_.sortBy(_.concat(pools0, pools1), [pool => -1 * pool.liquidityUSD]),
|
|
522
|
-
0,
|
|
523
|
-
count,
|
|
524
|
-
);
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
// Same as at uniswap-v2-pool.json, but extended with decimals and stable
|
|
528
|
-
async getSolidlyPairOrderedParams(
|
|
529
|
-
from: Token,
|
|
530
|
-
to: Token,
|
|
531
|
-
blockNumber: number,
|
|
532
|
-
stable: boolean,
|
|
533
|
-
tokenDexTransferFee: number,
|
|
534
|
-
): Promise<SolidlyPoolOrderedParams | null> {
|
|
535
|
-
const pair = await this.findSolidlyPair(from, to, stable);
|
|
536
|
-
if (!(pair && pair.pool && pair.exchange)) return null;
|
|
537
|
-
const pairState = pair.pool.getState(blockNumber);
|
|
538
|
-
|
|
539
|
-
if (!pairState) {
|
|
540
|
-
this.logger.error(
|
|
541
|
-
`Error_orderPairParams expected reserves, got none (maybe the pool doesn't exist) ${
|
|
542
|
-
from.symbol || from.address
|
|
543
|
-
} ${to.symbol || to.address}`,
|
|
544
|
-
);
|
|
545
|
-
return null;
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
const fee = (pairState.feeCode + tokenDexTransferFee).toString();
|
|
549
|
-
const pairReversed =
|
|
550
|
-
pair.token1.address.toLowerCase() === from.address.toLowerCase();
|
|
551
|
-
if (pairReversed) {
|
|
552
|
-
return {
|
|
553
|
-
tokenIn: from.address,
|
|
554
|
-
tokenOut: to.address,
|
|
555
|
-
reservesIn: pairState.reserves1,
|
|
556
|
-
reservesOut: pairState.reserves0,
|
|
557
|
-
fee,
|
|
558
|
-
direction: false,
|
|
559
|
-
exchange: pair.exchange,
|
|
560
|
-
decimalsIn: from.decimals,
|
|
561
|
-
decimalsOut: to.decimals,
|
|
562
|
-
stable,
|
|
563
|
-
};
|
|
564
|
-
}
|
|
565
|
-
return {
|
|
566
|
-
tokenIn: from.address,
|
|
567
|
-
tokenOut: to.address,
|
|
568
|
-
reservesIn: pairState.reserves0,
|
|
569
|
-
reservesOut: pairState.reserves1,
|
|
570
|
-
fee,
|
|
571
|
-
direction: true,
|
|
572
|
-
exchange: pair.exchange,
|
|
573
|
-
decimalsIn: from.decimals,
|
|
574
|
-
decimalsOut: to.decimals,
|
|
575
|
-
stable,
|
|
576
|
-
};
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
async getPoolIdentifiers(
|
|
580
|
-
_from: Token,
|
|
581
|
-
_to: Token,
|
|
582
|
-
side: SwapSide,
|
|
583
|
-
blockNumber: number,
|
|
584
|
-
): Promise<string[]> {
|
|
585
|
-
if (side === SwapSide.BUY) return [];
|
|
586
|
-
|
|
587
|
-
const from = this.dexHelper.config.wrapETH(_from);
|
|
588
|
-
const to = this.dexHelper.config.wrapETH(_to);
|
|
589
|
-
|
|
590
|
-
if (from.address.toLowerCase() === to.address.toLowerCase()) {
|
|
591
|
-
return [];
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
const tokenAddress = [from.address.toLowerCase(), to.address.toLowerCase()]
|
|
595
|
-
.sort((a, b) => (a > b ? 1 : -1))
|
|
596
|
-
.join('_');
|
|
597
|
-
|
|
598
|
-
const poolIdentifier = `${this.dexKey}_${tokenAddress}`;
|
|
599
|
-
const poolIdentifierUniswap = poolIdentifier + this.poolPostfix(false);
|
|
600
|
-
const poolIdentifierStable = poolIdentifier + this.poolPostfix(true);
|
|
601
|
-
return [poolIdentifierUniswap, poolIdentifierStable];
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
poolPostfix(stable: boolean) {
|
|
605
|
-
return stable ? 'S' : 'U';
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
async getSimpleParam(
|
|
609
|
-
src: Address,
|
|
610
|
-
dest: Address,
|
|
611
|
-
srcAmount: NumberAsString,
|
|
612
|
-
destAmount: NumberAsString,
|
|
613
|
-
data: UniswapData,
|
|
614
|
-
side: SwapSide,
|
|
615
|
-
): Promise<SimpleExchangeParam> {
|
|
616
|
-
if (side === SwapSide.BUY) throw new Error(`Buy not supported`);
|
|
617
|
-
return super.getSimpleParam(src, dest, srcAmount, destAmount, data, side);
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
getAdapterParam(
|
|
621
|
-
srcToken: Address,
|
|
622
|
-
destToken: Address,
|
|
623
|
-
srcAmount: NumberAsString,
|
|
624
|
-
toAmount: NumberAsString, // required for buy case
|
|
625
|
-
data: SolidlyData,
|
|
626
|
-
side: SwapSide,
|
|
627
|
-
): AdapterExchangeParam {
|
|
628
|
-
if (side === SwapSide.BUY) throw new Error(`Buy not supported`);
|
|
629
|
-
const pools = encodePools(data.pools, this.feeFactor);
|
|
630
|
-
const weth = this.getWETHAddress(srcToken, destToken, data.wethAddress);
|
|
631
|
-
const payload = this.abiCoder.encodeParameter(
|
|
632
|
-
{
|
|
633
|
-
ParentStruct: {
|
|
634
|
-
weth: 'address',
|
|
635
|
-
pools: 'uint256[]',
|
|
636
|
-
isFeeTokenInRoute: 'bool',
|
|
637
|
-
},
|
|
638
|
-
},
|
|
639
|
-
{ weth, pools, isFeeTokenInRoute: data.isFeeTokenInRoute },
|
|
640
|
-
);
|
|
641
|
-
return {
|
|
642
|
-
targetExchange: data.router,
|
|
643
|
-
payload,
|
|
644
|
-
networkFee: '0',
|
|
645
|
-
};
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
getDexParam(
|
|
649
|
-
srcToken: Address,
|
|
650
|
-
destToken: Address,
|
|
651
|
-
srcAmount: NumberAsString,
|
|
652
|
-
destAmount: NumberAsString,
|
|
653
|
-
recipient: Address,
|
|
654
|
-
data: SolidlyData,
|
|
655
|
-
side: SwapSide,
|
|
656
|
-
): DexExchangeParam {
|
|
657
|
-
if (side === SwapSide.BUY) throw new Error(`Buy not supported`);
|
|
658
|
-
let exchangeDataTypes = ['bytes4', 'bytes32'];
|
|
659
|
-
|
|
660
|
-
let exchangeDataToPack = [
|
|
661
|
-
hexZeroPad(hexlify(0), 4),
|
|
662
|
-
hexZeroPad(hexlify(data.pools.length), 32),
|
|
663
|
-
];
|
|
664
|
-
|
|
665
|
-
const pools = encodePools(data.pools, this.feeFactor);
|
|
666
|
-
pools.forEach(pool => {
|
|
667
|
-
exchangeDataTypes.push('bytes32');
|
|
668
|
-
exchangeDataToPack.push(hexZeroPad(hexlify(BigNumber.from(pool)), 32));
|
|
669
|
-
});
|
|
670
|
-
|
|
671
|
-
const exchangeData = solidityPack(exchangeDataTypes, exchangeDataToPack);
|
|
672
|
-
|
|
673
|
-
return {
|
|
674
|
-
needWrapNative: this.needWrapNative,
|
|
675
|
-
dexFuncHasRecipient: true,
|
|
676
|
-
exchangeData,
|
|
677
|
-
targetExchange: recipient,
|
|
678
|
-
specialDexFlag: data.isFeeTokenInRoute
|
|
679
|
-
? SpecialDex.SWAP_ON_DYSTOPIA_UNISWAP_V2_FORK_WITH_FEE
|
|
680
|
-
: SpecialDex.SWAP_ON_DYSTOPIA_UNISWAP_V2_FORK,
|
|
681
|
-
transferSrcTokenBeforeSwap: data.isFeeTokenInRoute
|
|
682
|
-
? undefined
|
|
683
|
-
: data.pools[0].address,
|
|
684
|
-
returnAmountPos: undefined,
|
|
685
|
-
};
|
|
686
|
-
}
|
|
687
|
-
}
|
|
1
|
+
import { UniswapV2 } from '../uniswap-v2/uniswap-v2';
|
|
2
|
+
import {
|
|
3
|
+
Network,
|
|
4
|
+
NULL_ADDRESS,
|
|
5
|
+
SUBGRAPH_TIMEOUT,
|
|
6
|
+
DEST_TOKEN_PARASWAP_TRANSFERS,
|
|
7
|
+
SRC_TOKEN_PARASWAP_TRANSFERS,
|
|
8
|
+
} from '../../constants';
|
|
9
|
+
import {
|
|
10
|
+
AdapterExchangeParam,
|
|
11
|
+
Address,
|
|
12
|
+
DexExchangeParam,
|
|
13
|
+
ExchangePrices,
|
|
14
|
+
PoolLiquidity,
|
|
15
|
+
SimpleExchangeParam,
|
|
16
|
+
Token,
|
|
17
|
+
TransferFeeParams,
|
|
18
|
+
} from '../../types';
|
|
19
|
+
import { IDexHelper } from '../../dex-helper';
|
|
20
|
+
import erc20ABI from '../../abi/erc20.json';
|
|
21
|
+
import { UniswapData, UniswapV2Data } from '../uniswap-v2/types';
|
|
22
|
+
import { getBigIntPow, getDexKeysWithNetwork } from '../../utils';
|
|
23
|
+
import solidlyFactoryABI from '../../abi/solidly/SolidlyFactory.json';
|
|
24
|
+
import solidlyPair from '../../abi/solidly/SolidlyPair.json';
|
|
25
|
+
import _ from 'lodash';
|
|
26
|
+
import { NumberAsString, SwapSide } from '@paraswap/core';
|
|
27
|
+
import { Interface, AbiCoder } from '@ethersproject/abi';
|
|
28
|
+
import { SolidlyStablePool } from './solidly-stable-pool';
|
|
29
|
+
import { Uniswapv2ConstantProductPool } from '../uniswap-v2/uniswap-v2-constant-product-pool';
|
|
30
|
+
import {
|
|
31
|
+
PoolState,
|
|
32
|
+
SolidlyData,
|
|
33
|
+
SolidlyPair,
|
|
34
|
+
SolidlyPool,
|
|
35
|
+
SolidlyPoolOrderedParams,
|
|
36
|
+
} from './types';
|
|
37
|
+
import { SolidlyConfig, Adapters } from './config';
|
|
38
|
+
import { applyTransferFee } from '../../lib/token-transfer-fee';
|
|
39
|
+
import {
|
|
40
|
+
hexDataLength,
|
|
41
|
+
hexZeroPad,
|
|
42
|
+
hexlify,
|
|
43
|
+
id,
|
|
44
|
+
solidityPack,
|
|
45
|
+
} from 'ethers/lib/utils';
|
|
46
|
+
import { BigNumber } from 'ethers';
|
|
47
|
+
import { Flag, SpecialDex } from '../../executor/types';
|
|
48
|
+
|
|
49
|
+
const erc20Iface = new Interface(erc20ABI);
|
|
50
|
+
const solidlyPairIface = new Interface(solidlyPair);
|
|
51
|
+
const defaultAbiCoder = new AbiCoder();
|
|
52
|
+
|
|
53
|
+
function encodePools(
|
|
54
|
+
pools: SolidlyPool[],
|
|
55
|
+
feeFactor: number,
|
|
56
|
+
): NumberAsString[] {
|
|
57
|
+
return pools.map(({ fee, direction, address }) => {
|
|
58
|
+
return (
|
|
59
|
+
(BigInt(feeFactor - fee) << 161n) +
|
|
60
|
+
((direction ? 0n : 1n) << 160n) +
|
|
61
|
+
BigInt(address)
|
|
62
|
+
).toString();
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export class Solidly extends UniswapV2 {
|
|
67
|
+
pairs: { [key: string]: SolidlyPair } = {};
|
|
68
|
+
stableFee?: number;
|
|
69
|
+
volatileFee?: number;
|
|
70
|
+
|
|
71
|
+
readonly isFeeOnTransferSupported: boolean = true;
|
|
72
|
+
readonly SRC_TOKEN_DEX_TRANSFERS = 1;
|
|
73
|
+
readonly DEST_TOKEN_DEX_TRANSFERS = 1;
|
|
74
|
+
|
|
75
|
+
public static dexKeysWithNetwork: { key: string; networks: Network[] }[] =
|
|
76
|
+
getDexKeysWithNetwork(
|
|
77
|
+
_.omit(SolidlyConfig, [
|
|
78
|
+
'Velodrome',
|
|
79
|
+
'VelodromeV2',
|
|
80
|
+
'Aerodrome',
|
|
81
|
+
'SpiritSwapV2',
|
|
82
|
+
'SolidlyV2',
|
|
83
|
+
'Thena',
|
|
84
|
+
'Chronos',
|
|
85
|
+
'Ramses',
|
|
86
|
+
'Equalizer',
|
|
87
|
+
'Velocimeter',
|
|
88
|
+
'Usdfi',
|
|
89
|
+
'PharaohV1',
|
|
90
|
+
]),
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
constructor(
|
|
94
|
+
protected network: Network,
|
|
95
|
+
dexKey: string,
|
|
96
|
+
protected dexHelper: IDexHelper,
|
|
97
|
+
isDynamicFees = false,
|
|
98
|
+
factoryAddress?: Address,
|
|
99
|
+
subgraphURL?: string,
|
|
100
|
+
initCode?: string,
|
|
101
|
+
feeCode?: number,
|
|
102
|
+
poolGasCost?: number,
|
|
103
|
+
routerAddress?: Address,
|
|
104
|
+
) {
|
|
105
|
+
super(
|
|
106
|
+
network,
|
|
107
|
+
dexKey,
|
|
108
|
+
dexHelper,
|
|
109
|
+
isDynamicFees,
|
|
110
|
+
factoryAddress !== undefined
|
|
111
|
+
? factoryAddress
|
|
112
|
+
: SolidlyConfig[dexKey][network].factoryAddress,
|
|
113
|
+
subgraphURL === ''
|
|
114
|
+
? undefined
|
|
115
|
+
: subgraphURL !== undefined
|
|
116
|
+
? subgraphURL
|
|
117
|
+
: SolidlyConfig[dexKey][network].subgraphURL,
|
|
118
|
+
initCode !== undefined
|
|
119
|
+
? initCode
|
|
120
|
+
: SolidlyConfig[dexKey][network].initCode,
|
|
121
|
+
feeCode !== undefined ? feeCode : SolidlyConfig[dexKey][network].feeCode,
|
|
122
|
+
poolGasCost !== undefined
|
|
123
|
+
? poolGasCost
|
|
124
|
+
: SolidlyConfig[dexKey][network].poolGasCost,
|
|
125
|
+
solidlyPairIface,
|
|
126
|
+
Adapters[network] || undefined,
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
this.stableFee = SolidlyConfig[dexKey][network].stableFee;
|
|
130
|
+
this.volatileFee = SolidlyConfig[dexKey][network].volatileFee;
|
|
131
|
+
|
|
132
|
+
this.factory = new dexHelper.web3Provider.eth.Contract(
|
|
133
|
+
solidlyFactoryABI as any,
|
|
134
|
+
factoryAddress !== undefined
|
|
135
|
+
? factoryAddress
|
|
136
|
+
: SolidlyConfig[dexKey][network].factoryAddress,
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
this.router =
|
|
140
|
+
routerAddress !== undefined
|
|
141
|
+
? routerAddress
|
|
142
|
+
: SolidlyConfig[dexKey][network].router || '';
|
|
143
|
+
|
|
144
|
+
this.feeFactor = SolidlyConfig[dexKey][network].feeFactor || this.feeFactor;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async findSolidlyPair(from: Token, to: Token, stable: boolean) {
|
|
148
|
+
if (from.address.toLowerCase() === to.address.toLowerCase()) return null;
|
|
149
|
+
const [token0, token1] =
|
|
150
|
+
from.address.toLowerCase() < to.address.toLowerCase()
|
|
151
|
+
? [from, to]
|
|
152
|
+
: [to, from];
|
|
153
|
+
|
|
154
|
+
const typePostfix = this.poolPostfix(stable);
|
|
155
|
+
const key = `${token0.address.toLowerCase()}-${token1.address.toLowerCase()}-${typePostfix}`;
|
|
156
|
+
let pair = this.pairs[key];
|
|
157
|
+
if (pair) return pair;
|
|
158
|
+
|
|
159
|
+
let exchange = await this.factory.methods
|
|
160
|
+
// Solidly has additional boolean parameter "StablePool"
|
|
161
|
+
// At first we look for uniswap-like volatile pool
|
|
162
|
+
.getPair(token0.address, token1.address, stable)
|
|
163
|
+
.call();
|
|
164
|
+
|
|
165
|
+
if (exchange === NULL_ADDRESS) {
|
|
166
|
+
pair = { token0, token1, stable };
|
|
167
|
+
} else {
|
|
168
|
+
pair = { token0, token1, exchange, stable };
|
|
169
|
+
}
|
|
170
|
+
this.pairs[key] = pair;
|
|
171
|
+
return pair;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async batchCatchUpPairs(pairs: [Token, Token][], blockNumber: number) {
|
|
175
|
+
if (!blockNumber) return;
|
|
176
|
+
const pairsToFetch: SolidlyPair[] = [];
|
|
177
|
+
for (const _pair of pairs) {
|
|
178
|
+
for (const stable of [false, true]) {
|
|
179
|
+
const pair = await this.findSolidlyPair(_pair[0], _pair[1], stable);
|
|
180
|
+
if (!(pair && pair.exchange)) continue;
|
|
181
|
+
if (!pair.pool) {
|
|
182
|
+
pairsToFetch.push(pair);
|
|
183
|
+
} else if (!pair.pool.getState(blockNumber)) {
|
|
184
|
+
pairsToFetch.push(pair);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (!pairsToFetch.length) return;
|
|
190
|
+
|
|
191
|
+
const reserves = await this.getManyPoolReserves(pairsToFetch, blockNumber);
|
|
192
|
+
|
|
193
|
+
if (reserves.length !== pairsToFetch.length) {
|
|
194
|
+
this.logger.error(
|
|
195
|
+
`Error_getManyPoolReserves didn't get any pool reserves`,
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
for (let i = 0; i < pairsToFetch.length; i++) {
|
|
200
|
+
const pairState = reserves[i];
|
|
201
|
+
const pair = pairsToFetch[i];
|
|
202
|
+
if (!pair.pool) {
|
|
203
|
+
await this.addPool(
|
|
204
|
+
pair,
|
|
205
|
+
pairState.reserves0,
|
|
206
|
+
pairState.reserves1,
|
|
207
|
+
pairState.feeCode,
|
|
208
|
+
blockNumber,
|
|
209
|
+
);
|
|
210
|
+
} else pair.pool.setState(pairState, blockNumber);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
async getManyPoolReserves(
|
|
215
|
+
pairs: SolidlyPair[],
|
|
216
|
+
blockNumber: number,
|
|
217
|
+
): Promise<PoolState[]> {
|
|
218
|
+
try {
|
|
219
|
+
const multiCallFeeData = pairs.map(pair =>
|
|
220
|
+
this.getFeesMultiCallData(pair),
|
|
221
|
+
);
|
|
222
|
+
const calldata = pairs
|
|
223
|
+
.map((pair, i) => {
|
|
224
|
+
let calldata = [
|
|
225
|
+
{
|
|
226
|
+
target: pair.token0.address,
|
|
227
|
+
callData: erc20Iface.encodeFunctionData('balanceOf', [
|
|
228
|
+
pair.exchange!,
|
|
229
|
+
]),
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
target: pair.token1.address,
|
|
233
|
+
callData: erc20Iface.encodeFunctionData('balanceOf', [
|
|
234
|
+
pair.exchange!,
|
|
235
|
+
]),
|
|
236
|
+
},
|
|
237
|
+
];
|
|
238
|
+
if (this.isDynamicFees) calldata.push(multiCallFeeData[i]!.callEntry);
|
|
239
|
+
return calldata;
|
|
240
|
+
})
|
|
241
|
+
.flat();
|
|
242
|
+
|
|
243
|
+
const data: { returnData: any[] } =
|
|
244
|
+
await this.dexHelper.multiContract.methods
|
|
245
|
+
.aggregate(calldata)
|
|
246
|
+
.call({}, blockNumber);
|
|
247
|
+
|
|
248
|
+
const returnData = _.chunk(data.returnData, this.isDynamicFees ? 3 : 2);
|
|
249
|
+
|
|
250
|
+
return pairs.map((pair, i) => ({
|
|
251
|
+
reserves0: defaultAbiCoder
|
|
252
|
+
.decode(['uint256'], returnData[i][0])[0]
|
|
253
|
+
.toString(),
|
|
254
|
+
reserves1: defaultAbiCoder
|
|
255
|
+
.decode(['uint256'], returnData[i][1])[0]
|
|
256
|
+
.toString(),
|
|
257
|
+
feeCode: this.isDynamicFees
|
|
258
|
+
? multiCallFeeData[i]!.callDecoder(returnData[i][2])
|
|
259
|
+
: (pair.stable ? this.stableFee : this.volatileFee) || this.feeCode,
|
|
260
|
+
}));
|
|
261
|
+
} catch (e) {
|
|
262
|
+
this.logger.error(
|
|
263
|
+
`Error_getManyPoolReserves could not get reserves with error:`,
|
|
264
|
+
e,
|
|
265
|
+
);
|
|
266
|
+
return [];
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
async getSellPrice(
|
|
271
|
+
priceParams: SolidlyPoolOrderedParams,
|
|
272
|
+
srcAmount: bigint,
|
|
273
|
+
): Promise<bigint> {
|
|
274
|
+
return priceParams.stable
|
|
275
|
+
? SolidlyStablePool.getSellPrice(priceParams, srcAmount, this.feeFactor)
|
|
276
|
+
: Uniswapv2ConstantProductPool.getSellPrice(
|
|
277
|
+
priceParams,
|
|
278
|
+
srcAmount,
|
|
279
|
+
this.feeFactor,
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
async getBuyPrice(
|
|
284
|
+
priceParams: SolidlyPoolOrderedParams,
|
|
285
|
+
srcAmount: bigint,
|
|
286
|
+
): Promise<bigint> {
|
|
287
|
+
if (priceParams.stable) throw new Error(`Buy not supported`);
|
|
288
|
+
return Uniswapv2ConstantProductPool.getBuyPrice(
|
|
289
|
+
priceParams,
|
|
290
|
+
srcAmount,
|
|
291
|
+
this.feeFactor,
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
async getPricesVolume(
|
|
296
|
+
_from: Token,
|
|
297
|
+
_to: Token,
|
|
298
|
+
amounts: bigint[],
|
|
299
|
+
side: SwapSide,
|
|
300
|
+
blockNumber: number,
|
|
301
|
+
// list of pool identifiers to use for pricing, if undefined use all pools
|
|
302
|
+
limitPools?: string[],
|
|
303
|
+
transferFees: TransferFeeParams = {
|
|
304
|
+
srcFee: 0,
|
|
305
|
+
destFee: 0,
|
|
306
|
+
srcDexFee: 0,
|
|
307
|
+
destDexFee: 0,
|
|
308
|
+
},
|
|
309
|
+
): Promise<ExchangePrices<UniswapV2Data> | null> {
|
|
310
|
+
try {
|
|
311
|
+
if (side === SwapSide.BUY) return null; // Buy side not implemented yet
|
|
312
|
+
const from = this.dexHelper.config.wrapETH(_from);
|
|
313
|
+
const to = this.dexHelper.config.wrapETH(_to);
|
|
314
|
+
|
|
315
|
+
if (from.address.toLowerCase() === to.address.toLowerCase()) {
|
|
316
|
+
return null;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const tokenAddress = [
|
|
320
|
+
from.address.toLowerCase(),
|
|
321
|
+
to.address.toLowerCase(),
|
|
322
|
+
]
|
|
323
|
+
.sort((a, b) => (a > b ? 1 : -1))
|
|
324
|
+
.join('_');
|
|
325
|
+
|
|
326
|
+
await this.batchCatchUpPairs([[from, to]], blockNumber);
|
|
327
|
+
|
|
328
|
+
const resultPromises = [false, true].map(async stable => {
|
|
329
|
+
// We don't support fee on transfer for stable pools yet
|
|
330
|
+
if (
|
|
331
|
+
stable &&
|
|
332
|
+
(transferFees.srcFee !== 0 || transferFees.srcDexFee !== 0)
|
|
333
|
+
) {
|
|
334
|
+
return null;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const poolIdentifier =
|
|
338
|
+
`${this.dexKey}_${tokenAddress}` + this.poolPostfix(stable);
|
|
339
|
+
|
|
340
|
+
if (limitPools && limitPools.every(p => p !== poolIdentifier))
|
|
341
|
+
return null;
|
|
342
|
+
|
|
343
|
+
const isSell = side === SwapSide.SELL;
|
|
344
|
+
const pairParam = await this.getSolidlyPairOrderedParams(
|
|
345
|
+
from,
|
|
346
|
+
to,
|
|
347
|
+
blockNumber,
|
|
348
|
+
stable,
|
|
349
|
+
transferFees.srcDexFee,
|
|
350
|
+
);
|
|
351
|
+
|
|
352
|
+
if (!pairParam) return null;
|
|
353
|
+
|
|
354
|
+
const unitAmount = getBigIntPow(
|
|
355
|
+
// @ts-expect-error Buy side is not implemented yet
|
|
356
|
+
side === SwapSide.BUY ? to.decimals : from.decimals,
|
|
357
|
+
);
|
|
358
|
+
|
|
359
|
+
const [unitVolumeWithFee, ...amountsWithFee] = applyTransferFee(
|
|
360
|
+
[unitAmount, ...amounts],
|
|
361
|
+
side,
|
|
362
|
+
isSell ? transferFees.srcFee : transferFees.destFee,
|
|
363
|
+
isSell ? SRC_TOKEN_PARASWAP_TRANSFERS : DEST_TOKEN_PARASWAP_TRANSFERS,
|
|
364
|
+
);
|
|
365
|
+
|
|
366
|
+
const unit =
|
|
367
|
+
// @ts-expect-error Buy side is not implemented yet
|
|
368
|
+
side === SwapSide.BUY
|
|
369
|
+
? await this.getBuyPricePath(unitVolumeWithFee, [pairParam])
|
|
370
|
+
: await this.getSellPricePath(unitVolumeWithFee, [pairParam]);
|
|
371
|
+
|
|
372
|
+
const prices =
|
|
373
|
+
// @ts-expect-error Buy side is not implemented yet
|
|
374
|
+
side === SwapSide.BUY
|
|
375
|
+
? await Promise.all(
|
|
376
|
+
amountsWithFee.map(amount =>
|
|
377
|
+
amount === 0n
|
|
378
|
+
? 0n
|
|
379
|
+
: this.getBuyPricePath(amount, [pairParam]),
|
|
380
|
+
),
|
|
381
|
+
)
|
|
382
|
+
: await Promise.all(
|
|
383
|
+
amountsWithFee.map(amount =>
|
|
384
|
+
amount === 0n
|
|
385
|
+
? 0n
|
|
386
|
+
: this.getSellPricePath(amount, [pairParam]),
|
|
387
|
+
),
|
|
388
|
+
);
|
|
389
|
+
|
|
390
|
+
const [unitOutWithFee, ...outputsWithFee] = applyTransferFee(
|
|
391
|
+
[unit, ...prices],
|
|
392
|
+
side,
|
|
393
|
+
// This part is confusing, because we treat differently SELL and BUY fees
|
|
394
|
+
// If Buy, we should apply transfer fee on srcToken on top of dexFee applied earlier
|
|
395
|
+
// But for Sell we should apply only one dexFee
|
|
396
|
+
isSell ? transferFees.destDexFee : transferFees.srcFee,
|
|
397
|
+
isSell ? this.DEST_TOKEN_DEX_TRANSFERS : SRC_TOKEN_PARASWAP_TRANSFERS,
|
|
398
|
+
);
|
|
399
|
+
|
|
400
|
+
return {
|
|
401
|
+
prices: outputsWithFee,
|
|
402
|
+
unit: unitOutWithFee,
|
|
403
|
+
data: {
|
|
404
|
+
router: this.router,
|
|
405
|
+
path: [from.address.toLowerCase(), to.address.toLowerCase()],
|
|
406
|
+
factory: this.factoryAddress,
|
|
407
|
+
initCode: this.initCode,
|
|
408
|
+
feeFactor: this.feeFactor,
|
|
409
|
+
isFeeTokenInRoute: Object.values(transferFees).some(f => f !== 0),
|
|
410
|
+
pools: [
|
|
411
|
+
{
|
|
412
|
+
address: pairParam.exchange,
|
|
413
|
+
fee: parseInt(pairParam.fee),
|
|
414
|
+
direction: pairParam.direction,
|
|
415
|
+
},
|
|
416
|
+
],
|
|
417
|
+
},
|
|
418
|
+
exchange: this.dexKey,
|
|
419
|
+
poolIdentifier,
|
|
420
|
+
gasCost: this.poolGasCost,
|
|
421
|
+
poolAddresses: [pairParam.exchange],
|
|
422
|
+
};
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
const resultPools = (await Promise.all(
|
|
426
|
+
resultPromises,
|
|
427
|
+
)) as ExchangePrices<UniswapV2Data>;
|
|
428
|
+
const resultPoolsFiltered = resultPools.filter(item => !!item); // filter null elements
|
|
429
|
+
return resultPoolsFiltered.length > 0 ? resultPoolsFiltered : null;
|
|
430
|
+
} catch (e) {
|
|
431
|
+
if (blockNumber === 0)
|
|
432
|
+
this.logger.error(
|
|
433
|
+
`Error_getPricesVolume: Aurelius block manager not yet instantiated`,
|
|
434
|
+
);
|
|
435
|
+
this.logger.error(`Error_getPrices:`, e);
|
|
436
|
+
return null;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
async getTopPoolsForToken(
|
|
441
|
+
tokenAddress: Address,
|
|
442
|
+
count: number,
|
|
443
|
+
): Promise<PoolLiquidity[]> {
|
|
444
|
+
if (!this.subgraphURL) return [];
|
|
445
|
+
|
|
446
|
+
let stableFieldKey = '';
|
|
447
|
+
|
|
448
|
+
if (this.dexKey.toLowerCase() === 'solidly') {
|
|
449
|
+
stableFieldKey = 'stable';
|
|
450
|
+
} else if (this.dexKey.toLowerCase() !== 'solidlyv2') {
|
|
451
|
+
stableFieldKey = 'isStable';
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
const query = `query ($token: Bytes!, $count: Int) {
|
|
455
|
+
pools0: pairs(first: $count, orderBy: reserveUSD, orderDirection: desc, where: {token0: $token, reserve0_gt: 1, reserve1_gt: 1}) {
|
|
456
|
+
id
|
|
457
|
+
${stableFieldKey}
|
|
458
|
+
token0 {
|
|
459
|
+
id
|
|
460
|
+
decimals
|
|
461
|
+
}
|
|
462
|
+
token1 {
|
|
463
|
+
id
|
|
464
|
+
decimals
|
|
465
|
+
}
|
|
466
|
+
reserveUSD
|
|
467
|
+
}
|
|
468
|
+
pools1: pairs(first: $count, orderBy: reserveUSD, orderDirection: desc, where: {token1: $token, reserve0_gt: 1, reserve1_gt: 1}) {
|
|
469
|
+
id
|
|
470
|
+
${stableFieldKey}
|
|
471
|
+
token0 {
|
|
472
|
+
id
|
|
473
|
+
decimals
|
|
474
|
+
}
|
|
475
|
+
token1 {
|
|
476
|
+
id
|
|
477
|
+
decimals
|
|
478
|
+
}
|
|
479
|
+
reserveUSD
|
|
480
|
+
}
|
|
481
|
+
}`;
|
|
482
|
+
|
|
483
|
+
const { data } = await this.dexHelper.httpRequest.post(
|
|
484
|
+
this.subgraphURL,
|
|
485
|
+
{
|
|
486
|
+
query,
|
|
487
|
+
variables: { token: tokenAddress.toLowerCase(), count },
|
|
488
|
+
},
|
|
489
|
+
SUBGRAPH_TIMEOUT,
|
|
490
|
+
);
|
|
491
|
+
|
|
492
|
+
if (!(data && data.pools0 && data.pools1))
|
|
493
|
+
throw new Error("Couldn't fetch the pools from the subgraph");
|
|
494
|
+
const pools0 = _.map(data.pools0, pool => ({
|
|
495
|
+
exchange: this.dexKey,
|
|
496
|
+
stable: pool[stableFieldKey],
|
|
497
|
+
address: pool.id.toLowerCase(),
|
|
498
|
+
connectorTokens: [
|
|
499
|
+
{
|
|
500
|
+
address: pool.token1.id.toLowerCase(),
|
|
501
|
+
decimals: parseInt(pool.token1.decimals),
|
|
502
|
+
},
|
|
503
|
+
],
|
|
504
|
+
liquidityUSD: parseFloat(pool.reserveUSD),
|
|
505
|
+
}));
|
|
506
|
+
|
|
507
|
+
const pools1 = _.map(data.pools1, pool => ({
|
|
508
|
+
exchange: this.dexKey,
|
|
509
|
+
stable: pool[stableFieldKey],
|
|
510
|
+
address: pool.id.toLowerCase(),
|
|
511
|
+
connectorTokens: [
|
|
512
|
+
{
|
|
513
|
+
address: pool.token0.id.toLowerCase(),
|
|
514
|
+
decimals: parseInt(pool.token0.decimals),
|
|
515
|
+
},
|
|
516
|
+
],
|
|
517
|
+
liquidityUSD: parseFloat(pool.reserveUSD),
|
|
518
|
+
}));
|
|
519
|
+
|
|
520
|
+
return _.slice(
|
|
521
|
+
_.sortBy(_.concat(pools0, pools1), [pool => -1 * pool.liquidityUSD]),
|
|
522
|
+
0,
|
|
523
|
+
count,
|
|
524
|
+
);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// Same as at uniswap-v2-pool.json, but extended with decimals and stable
|
|
528
|
+
async getSolidlyPairOrderedParams(
|
|
529
|
+
from: Token,
|
|
530
|
+
to: Token,
|
|
531
|
+
blockNumber: number,
|
|
532
|
+
stable: boolean,
|
|
533
|
+
tokenDexTransferFee: number,
|
|
534
|
+
): Promise<SolidlyPoolOrderedParams | null> {
|
|
535
|
+
const pair = await this.findSolidlyPair(from, to, stable);
|
|
536
|
+
if (!(pair && pair.pool && pair.exchange)) return null;
|
|
537
|
+
const pairState = pair.pool.getState(blockNumber);
|
|
538
|
+
|
|
539
|
+
if (!pairState) {
|
|
540
|
+
this.logger.error(
|
|
541
|
+
`Error_orderPairParams expected reserves, got none (maybe the pool doesn't exist) ${
|
|
542
|
+
from.symbol || from.address
|
|
543
|
+
} ${to.symbol || to.address}`,
|
|
544
|
+
);
|
|
545
|
+
return null;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
const fee = (pairState.feeCode + tokenDexTransferFee).toString();
|
|
549
|
+
const pairReversed =
|
|
550
|
+
pair.token1.address.toLowerCase() === from.address.toLowerCase();
|
|
551
|
+
if (pairReversed) {
|
|
552
|
+
return {
|
|
553
|
+
tokenIn: from.address,
|
|
554
|
+
tokenOut: to.address,
|
|
555
|
+
reservesIn: pairState.reserves1,
|
|
556
|
+
reservesOut: pairState.reserves0,
|
|
557
|
+
fee,
|
|
558
|
+
direction: false,
|
|
559
|
+
exchange: pair.exchange,
|
|
560
|
+
decimalsIn: from.decimals,
|
|
561
|
+
decimalsOut: to.decimals,
|
|
562
|
+
stable,
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
return {
|
|
566
|
+
tokenIn: from.address,
|
|
567
|
+
tokenOut: to.address,
|
|
568
|
+
reservesIn: pairState.reserves0,
|
|
569
|
+
reservesOut: pairState.reserves1,
|
|
570
|
+
fee,
|
|
571
|
+
direction: true,
|
|
572
|
+
exchange: pair.exchange,
|
|
573
|
+
decimalsIn: from.decimals,
|
|
574
|
+
decimalsOut: to.decimals,
|
|
575
|
+
stable,
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
async getPoolIdentifiers(
|
|
580
|
+
_from: Token,
|
|
581
|
+
_to: Token,
|
|
582
|
+
side: SwapSide,
|
|
583
|
+
blockNumber: number,
|
|
584
|
+
): Promise<string[]> {
|
|
585
|
+
if (side === SwapSide.BUY) return [];
|
|
586
|
+
|
|
587
|
+
const from = this.dexHelper.config.wrapETH(_from);
|
|
588
|
+
const to = this.dexHelper.config.wrapETH(_to);
|
|
589
|
+
|
|
590
|
+
if (from.address.toLowerCase() === to.address.toLowerCase()) {
|
|
591
|
+
return [];
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
const tokenAddress = [from.address.toLowerCase(), to.address.toLowerCase()]
|
|
595
|
+
.sort((a, b) => (a > b ? 1 : -1))
|
|
596
|
+
.join('_');
|
|
597
|
+
|
|
598
|
+
const poolIdentifier = `${this.dexKey}_${tokenAddress}`;
|
|
599
|
+
const poolIdentifierUniswap = poolIdentifier + this.poolPostfix(false);
|
|
600
|
+
const poolIdentifierStable = poolIdentifier + this.poolPostfix(true);
|
|
601
|
+
return [poolIdentifierUniswap, poolIdentifierStable];
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
poolPostfix(stable: boolean) {
|
|
605
|
+
return stable ? 'S' : 'U';
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
async getSimpleParam(
|
|
609
|
+
src: Address,
|
|
610
|
+
dest: Address,
|
|
611
|
+
srcAmount: NumberAsString,
|
|
612
|
+
destAmount: NumberAsString,
|
|
613
|
+
data: UniswapData,
|
|
614
|
+
side: SwapSide,
|
|
615
|
+
): Promise<SimpleExchangeParam> {
|
|
616
|
+
if (side === SwapSide.BUY) throw new Error(`Buy not supported`);
|
|
617
|
+
return super.getSimpleParam(src, dest, srcAmount, destAmount, data, side);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
getAdapterParam(
|
|
621
|
+
srcToken: Address,
|
|
622
|
+
destToken: Address,
|
|
623
|
+
srcAmount: NumberAsString,
|
|
624
|
+
toAmount: NumberAsString, // required for buy case
|
|
625
|
+
data: SolidlyData,
|
|
626
|
+
side: SwapSide,
|
|
627
|
+
): AdapterExchangeParam {
|
|
628
|
+
if (side === SwapSide.BUY) throw new Error(`Buy not supported`);
|
|
629
|
+
const pools = encodePools(data.pools, this.feeFactor);
|
|
630
|
+
const weth = this.getWETHAddress(srcToken, destToken, data.wethAddress);
|
|
631
|
+
const payload = this.abiCoder.encodeParameter(
|
|
632
|
+
{
|
|
633
|
+
ParentStruct: {
|
|
634
|
+
weth: 'address',
|
|
635
|
+
pools: 'uint256[]',
|
|
636
|
+
isFeeTokenInRoute: 'bool',
|
|
637
|
+
},
|
|
638
|
+
},
|
|
639
|
+
{ weth, pools, isFeeTokenInRoute: data.isFeeTokenInRoute },
|
|
640
|
+
);
|
|
641
|
+
return {
|
|
642
|
+
targetExchange: data.router,
|
|
643
|
+
payload,
|
|
644
|
+
networkFee: '0',
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
getDexParam(
|
|
649
|
+
srcToken: Address,
|
|
650
|
+
destToken: Address,
|
|
651
|
+
srcAmount: NumberAsString,
|
|
652
|
+
destAmount: NumberAsString,
|
|
653
|
+
recipient: Address,
|
|
654
|
+
data: SolidlyData,
|
|
655
|
+
side: SwapSide,
|
|
656
|
+
): DexExchangeParam {
|
|
657
|
+
if (side === SwapSide.BUY) throw new Error(`Buy not supported`);
|
|
658
|
+
let exchangeDataTypes = ['bytes4', 'bytes32'];
|
|
659
|
+
|
|
660
|
+
let exchangeDataToPack = [
|
|
661
|
+
hexZeroPad(hexlify(0), 4),
|
|
662
|
+
hexZeroPad(hexlify(data.pools.length), 32),
|
|
663
|
+
];
|
|
664
|
+
|
|
665
|
+
const pools = encodePools(data.pools, this.feeFactor);
|
|
666
|
+
pools.forEach(pool => {
|
|
667
|
+
exchangeDataTypes.push('bytes32');
|
|
668
|
+
exchangeDataToPack.push(hexZeroPad(hexlify(BigNumber.from(pool)), 32));
|
|
669
|
+
});
|
|
670
|
+
|
|
671
|
+
const exchangeData = solidityPack(exchangeDataTypes, exchangeDataToPack);
|
|
672
|
+
|
|
673
|
+
return {
|
|
674
|
+
needWrapNative: this.needWrapNative,
|
|
675
|
+
dexFuncHasRecipient: true,
|
|
676
|
+
exchangeData,
|
|
677
|
+
targetExchange: recipient,
|
|
678
|
+
specialDexFlag: data.isFeeTokenInRoute
|
|
679
|
+
? SpecialDex.SWAP_ON_DYSTOPIA_UNISWAP_V2_FORK_WITH_FEE
|
|
680
|
+
: SpecialDex.SWAP_ON_DYSTOPIA_UNISWAP_V2_FORK,
|
|
681
|
+
transferSrcTokenBeforeSwap: data.isFeeTokenInRoute
|
|
682
|
+
? undefined
|
|
683
|
+
: data.pools[0].address,
|
|
684
|
+
returnAmountPos: undefined,
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
}
|