@paraswap/dex-lib 2.42.26 → 2.42.27
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/build/abi/smardex/all/smardex-router.json +648 -0
- package/build/abi/smardex/layer-1/smardex-factory.json +242 -0
- package/build/abi/smardex/layer-1/smardex-pool.json +506 -0
- package/build/abi/smardex/layer-2/smardex-factory.json +185 -0
- package/build/abi/smardex/layer-2/smardex-pool.json +578 -0
- package/build/config.js +20 -10
- package/build/config.js.map +1 -1
- package/build/dex/index.js +2 -0
- package/build/dex/index.js.map +1 -1
- package/build/dex/smardex/config.d.ts +4 -0
- package/build/dex/smardex/config.js +64 -0
- package/build/dex/smardex/config.js.map +1 -0
- package/build/dex/smardex/constants.d.ts +18 -0
- package/build/dex/smardex/constants.js +33 -0
- package/build/dex/smardex/constants.js.map +1 -0
- package/build/dex/smardex/sdk/constants.d.ts +6 -0
- package/build/dex/smardex/sdk/constants.js +13 -0
- package/build/dex/smardex/sdk/constants.js.map +1 -0
- package/build/dex/smardex/sdk/core.d.ts +113 -0
- package/build/dex/smardex/sdk/core.js +499 -0
- package/build/dex/smardex/sdk/core.js.map +1 -0
- package/build/dex/smardex/sdk/errors.d.ts +15 -0
- package/build/dex/smardex/sdk/errors.js +22 -0
- package/build/dex/smardex/sdk/errors.js.map +1 -0
- package/build/dex/smardex/sdk/types.d.ts +62 -0
- package/build/dex/smardex/sdk/types.js +3 -0
- package/build/dex/smardex/sdk/types.js.map +1 -0
- package/build/dex/smardex/sdk/utils.d.ts +44 -0
- package/build/dex/smardex/sdk/utils.js +133 -0
- package/build/dex/smardex/sdk/utils.js.map +1 -0
- package/build/dex/smardex/smardex-event-pool.d.ts +28 -0
- package/build/dex/smardex/smardex-event-pool.js +119 -0
- package/build/dex/smardex/smardex-event-pool.js.map +1 -0
- package/build/dex/smardex/smardex.d.ts +65 -0
- package/build/dex/smardex/smardex.js +519 -0
- package/build/dex/smardex/smardex.js.map +1 -0
- package/build/dex/smardex/types.d.ts +55 -0
- package/build/dex/smardex/types.js +3 -0
- package/build/dex/smardex/types.js.map +1 -0
- package/build/types.d.ts +1 -0
- package/package.json +3 -2
- package/src/abi/smardex/all/smardex-router.json +648 -0
- package/src/abi/smardex/layer-1/smardex-factory.json +242 -0
- package/src/abi/smardex/layer-1/smardex-pool.json +506 -0
- package/src/abi/smardex/layer-2/smardex-factory.json +185 -0
- package/src/abi/smardex/layer-2/smardex-pool.json +578 -0
- package/src/config.ts +21 -10
- package/src/dex/index.ts +2 -0
- package/src/dex/smardex/config.ts +67 -0
- package/src/dex/smardex/constants.ts +35 -0
- package/src/dex/smardex/sdk/constants.ts +10 -0
- package/src/dex/smardex/sdk/core.ts +832 -0
- package/src/dex/smardex/sdk/errors.ts +18 -0
- package/src/dex/smardex/sdk/types.ts +68 -0
- package/src/dex/smardex/sdk/utils.ts +156 -0
- package/src/dex/smardex/smardex-e2e.test.ts +275 -0
- package/src/dex/smardex/smardex-event-pool.ts +164 -0
- package/src/dex/smardex/smardex-events.test.ts +297 -0
- package/src/dex/smardex/smardex-integration.test.ts +258 -0
- package/src/dex/smardex/smardex.ts +770 -0
- package/src/dex/smardex/types.ts +69 -0
- package/src/types.ts +1 -0
- package/tests/constants-e2e.ts +26 -1
- package/.vscode/launch.json +0 -54
- package/.vscode/settings.json +0 -2
- package/.vscode/tasks.json +0 -15
|
@@ -0,0 +1,770 @@
|
|
|
1
|
+
import { AbiCoder, Interface } from '@ethersproject/abi';
|
|
2
|
+
import { Contract } from 'web3-eth-contract';
|
|
3
|
+
import _ from 'lodash';
|
|
4
|
+
import {
|
|
5
|
+
DEST_TOKEN_PARASWAP_TRANSFERS,
|
|
6
|
+
ETHER_ADDRESS,
|
|
7
|
+
NULL_ADDRESS,
|
|
8
|
+
Network,
|
|
9
|
+
SRC_TOKEN_PARASWAP_TRANSFERS,
|
|
10
|
+
SwapSide,
|
|
11
|
+
} from '../../constants';
|
|
12
|
+
import {
|
|
13
|
+
AdapterExchangeParam,
|
|
14
|
+
Address,
|
|
15
|
+
ExchangePrices,
|
|
16
|
+
Logger,
|
|
17
|
+
NumberAsString,
|
|
18
|
+
PoolLiquidity,
|
|
19
|
+
PoolPrices,
|
|
20
|
+
SimpleExchangeParam,
|
|
21
|
+
Token,
|
|
22
|
+
TransferFeeParams,
|
|
23
|
+
TxInfo,
|
|
24
|
+
} from '../../types';
|
|
25
|
+
import { IDexHelper } from '../../dex-helper/index';
|
|
26
|
+
import {
|
|
27
|
+
SmardexFees,
|
|
28
|
+
SmardexPair,
|
|
29
|
+
SmardexPool,
|
|
30
|
+
SmardexPoolOrderedParams,
|
|
31
|
+
SmardexPoolState,
|
|
32
|
+
} from './types';
|
|
33
|
+
import { getBigIntPow, getDexKeysWithNetwork, isETHAddress } from '../../utils';
|
|
34
|
+
import SmardexFactoryLayerOneABI from '../../abi/smardex/layer-1/smardex-factory.json';
|
|
35
|
+
import SmardexFactoryLayerTwoABI from '../../abi/smardex/layer-2/smardex-factory.json';
|
|
36
|
+
|
|
37
|
+
import SmardexRouterABI from '../../abi/smardex/all/smardex-router.json';
|
|
38
|
+
import { SimpleExchange } from '../simple-exchange';
|
|
39
|
+
import {
|
|
40
|
+
SmardexRouterFunctions,
|
|
41
|
+
directSmardexFunctionName,
|
|
42
|
+
DefaultSmardexPoolGasCost,
|
|
43
|
+
SUBGRAPH_TIMEOUT,
|
|
44
|
+
FEES_LAYER_ONE,
|
|
45
|
+
} from '../smardex/constants';
|
|
46
|
+
import { SmardexData, SmardexParam } from '../smardex/types';
|
|
47
|
+
import { IDex } from '../..';
|
|
48
|
+
import * as CALLDATA_GAS_COST from '../../calldata-gas-cost';
|
|
49
|
+
import { Adapters, SmardexConfig } from './config';
|
|
50
|
+
import ParaSwapABI from '../../abi/IParaswap.json';
|
|
51
|
+
import { applyTransferFee } from '../../lib/token-transfer-fee';
|
|
52
|
+
import { computeAmountIn, computeAmountOut } from './sdk/core';
|
|
53
|
+
import { SmardexEventPool } from './smardex-event-pool';
|
|
54
|
+
import SmardexPoolLayerOneABI from '../../abi/smardex/layer-1/smardex-pool.json';
|
|
55
|
+
import SmardexPoolLayerTwoABI from '../../abi/smardex/layer-2/smardex-pool.json';
|
|
56
|
+
|
|
57
|
+
const smardexPoolL1 = new Interface(SmardexPoolLayerOneABI);
|
|
58
|
+
const smardexPoolL2 = new Interface(SmardexPoolLayerTwoABI);
|
|
59
|
+
|
|
60
|
+
const coder = new AbiCoder();
|
|
61
|
+
|
|
62
|
+
function encodePools(pools: SmardexPool[]): NumberAsString[] {
|
|
63
|
+
return pools.map(({ fee, direction, address }) => {
|
|
64
|
+
return (
|
|
65
|
+
(BigInt(fee) << 161n) +
|
|
66
|
+
((direction ? 0n : 1n) << 160n) +
|
|
67
|
+
BigInt(address)
|
|
68
|
+
).toString();
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export class Smardex
|
|
73
|
+
extends SimpleExchange
|
|
74
|
+
implements IDex<SmardexData, SmardexParam>
|
|
75
|
+
{
|
|
76
|
+
pairs: { [key: string]: SmardexPair } = {};
|
|
77
|
+
factory: Contract;
|
|
78
|
+
|
|
79
|
+
routerInterface: Interface;
|
|
80
|
+
exchangeRouterInterface: Interface;
|
|
81
|
+
static directFunctionName = directSmardexFunctionName;
|
|
82
|
+
|
|
83
|
+
factoryAddress: string;
|
|
84
|
+
routerAddress: string;
|
|
85
|
+
|
|
86
|
+
protected subgraphURL: string | undefined;
|
|
87
|
+
protected initCode: string;
|
|
88
|
+
|
|
89
|
+
logger: Logger;
|
|
90
|
+
readonly hasConstantPriceLargeAmounts = false;
|
|
91
|
+
readonly isFeeOnTransferSupported: boolean = true;
|
|
92
|
+
readonly SRC_TOKEN_DEX_TRANSFERS = 1;
|
|
93
|
+
readonly DEST_TOKEN_DEX_TRANSFERS = 1;
|
|
94
|
+
|
|
95
|
+
public static dexKeysWithNetwork: { key: string; networks: Network[] }[] =
|
|
96
|
+
getDexKeysWithNetwork(SmardexConfig);
|
|
97
|
+
|
|
98
|
+
constructor(
|
|
99
|
+
protected network: Network,
|
|
100
|
+
dexKey: string,
|
|
101
|
+
public dexHelper: IDexHelper,
|
|
102
|
+
protected adapters = Adapters[network] || {},
|
|
103
|
+
) {
|
|
104
|
+
super(dexHelper, dexKey);
|
|
105
|
+
this.logger = dexHelper.getLogger(dexKey);
|
|
106
|
+
const config = SmardexConfig[dexKey];
|
|
107
|
+
this.routerAddress = config[network].router!;
|
|
108
|
+
this.factoryAddress = config[network].factoryAddress;
|
|
109
|
+
this.subgraphURL = config[network].subgraphURL;
|
|
110
|
+
this.initCode = config[network].initCode;
|
|
111
|
+
const factoryAbi = this.isLayer1()
|
|
112
|
+
? SmardexFactoryLayerOneABI
|
|
113
|
+
: SmardexFactoryLayerTwoABI;
|
|
114
|
+
this.factory = new dexHelper.web3Provider.eth.Contract(
|
|
115
|
+
factoryAbi as any,
|
|
116
|
+
this.factoryAddress,
|
|
117
|
+
);
|
|
118
|
+
this.routerInterface = new Interface(ParaSwapABI);
|
|
119
|
+
this.exchangeRouterInterface = new Interface(SmardexRouterABI);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
getAdapters(side: SwapSide): { name: string; index: number }[] | null {
|
|
123
|
+
return this.adapters[side] ? this.adapters[side] : null;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async getPoolIdentifiers(
|
|
127
|
+
_from: Token,
|
|
128
|
+
_to: Token,
|
|
129
|
+
side: SwapSide,
|
|
130
|
+
blockNumber: number,
|
|
131
|
+
): Promise<string[]> {
|
|
132
|
+
const from = this.dexHelper.config.wrapETH(_from);
|
|
133
|
+
const to = this.dexHelper.config.wrapETH(_to);
|
|
134
|
+
|
|
135
|
+
if (from.address.toLowerCase() === to.address.toLowerCase()) {
|
|
136
|
+
return [];
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const tokenAddress = [from.address.toLowerCase(), to.address.toLowerCase()]
|
|
140
|
+
.sort((a, b) => (BigInt(a) > BigInt(b) ? 1 : -1))
|
|
141
|
+
.join('_');
|
|
142
|
+
|
|
143
|
+
const poolIdentifier = `${this.dexKey}_${tokenAddress}`;
|
|
144
|
+
return [poolIdentifier];
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async getPricesVolume(
|
|
148
|
+
_from: Token,
|
|
149
|
+
_to: Token,
|
|
150
|
+
amounts: bigint[],
|
|
151
|
+
side: SwapSide,
|
|
152
|
+
blockNumber: number,
|
|
153
|
+
// list of pool identifiers to use for pricing, if undefined use all pools
|
|
154
|
+
limitPools?: string[],
|
|
155
|
+
transferFees: TransferFeeParams = {
|
|
156
|
+
srcFee: 0,
|
|
157
|
+
destFee: 0,
|
|
158
|
+
srcDexFee: 0,
|
|
159
|
+
destDexFee: 0,
|
|
160
|
+
},
|
|
161
|
+
): Promise<ExchangePrices<SmardexData> | null> {
|
|
162
|
+
try {
|
|
163
|
+
const from = this.dexHelper.config.wrapETH(_from);
|
|
164
|
+
const to = this.dexHelper.config.wrapETH(_to);
|
|
165
|
+
|
|
166
|
+
if (from.address.toLowerCase() === to.address.toLowerCase()) {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const tokenAddress = [
|
|
171
|
+
from.address.toLowerCase(),
|
|
172
|
+
to.address.toLowerCase(),
|
|
173
|
+
]
|
|
174
|
+
.sort((a, b) => (BigInt(a) > BigInt(b) ? 1 : -1))
|
|
175
|
+
.join('_');
|
|
176
|
+
|
|
177
|
+
const poolIdentifier = `${this.dexKey}_${tokenAddress}`;
|
|
178
|
+
|
|
179
|
+
if (limitPools && limitPools.every(p => p !== poolIdentifier))
|
|
180
|
+
return null;
|
|
181
|
+
|
|
182
|
+
await this.batchCatchUpPairs([[from, to]], blockNumber);
|
|
183
|
+
const isSell = side === SwapSide.SELL;
|
|
184
|
+
const pairParam = await this.getPairOrderedParams(
|
|
185
|
+
from,
|
|
186
|
+
to,
|
|
187
|
+
blockNumber,
|
|
188
|
+
transferFees.srcDexFee,
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
if (!pairParam) return null;
|
|
192
|
+
|
|
193
|
+
const unitAmount = getBigIntPow(isSell ? from.decimals : to.decimals);
|
|
194
|
+
|
|
195
|
+
// SmarDex does not support Fees on Transfer Tokens
|
|
196
|
+
const [unitVolumeWithFee, ...amountsWithFee] = applyTransferFee(
|
|
197
|
+
[unitAmount, ...amounts],
|
|
198
|
+
side,
|
|
199
|
+
isSell ? transferFees.srcFee : transferFees.destFee,
|
|
200
|
+
isSell ? SRC_TOKEN_PARASWAP_TRANSFERS : DEST_TOKEN_PARASWAP_TRANSFERS,
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
const unit = isSell
|
|
204
|
+
? await this.getSellPricePath(unitVolumeWithFee, [pairParam])
|
|
205
|
+
: await this.getBuyPricePath(unitVolumeWithFee, [pairParam]);
|
|
206
|
+
|
|
207
|
+
const prices = isSell
|
|
208
|
+
? await Promise.all(
|
|
209
|
+
amountsWithFee.map(amount =>
|
|
210
|
+
this.getSellPricePath(amount, [pairParam]),
|
|
211
|
+
),
|
|
212
|
+
)
|
|
213
|
+
: await Promise.all(
|
|
214
|
+
amountsWithFee.map(amount =>
|
|
215
|
+
this.getBuyPricePath(amount, [pairParam]),
|
|
216
|
+
),
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
const [unitOutWithFee, ...outputsWithFee] = applyTransferFee(
|
|
220
|
+
[unit, ...prices],
|
|
221
|
+
side,
|
|
222
|
+
// This part is confusing, because we treat differently SELL and BUY fees
|
|
223
|
+
// If Buy, we should apply transfer fee on srcToken on top of dexFee applied earlier
|
|
224
|
+
// But for Sell we should apply only one dexFee
|
|
225
|
+
isSell ? transferFees.destDexFee : transferFees.srcFee,
|
|
226
|
+
isSell ? this.DEST_TOKEN_DEX_TRANSFERS : SRC_TOKEN_PARASWAP_TRANSFERS,
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
// As uniswapv2 just has one pool per token pair
|
|
230
|
+
return [
|
|
231
|
+
{
|
|
232
|
+
prices: outputsWithFee,
|
|
233
|
+
unit: unitOutWithFee,
|
|
234
|
+
data: {
|
|
235
|
+
deadline: Math.floor(new Date().getTime()) + 120,
|
|
236
|
+
receiver: this.augustusAddress,
|
|
237
|
+
router: this.routerAddress,
|
|
238
|
+
path: [from.address.toLowerCase(), to.address.toLowerCase()],
|
|
239
|
+
factory: this.factoryAddress,
|
|
240
|
+
initCode: this.initCode,
|
|
241
|
+
pools: [
|
|
242
|
+
{
|
|
243
|
+
address: pairParam.exchange,
|
|
244
|
+
fee: 0, // Smardex does not support Fees on Transfer Tokens
|
|
245
|
+
direction:
|
|
246
|
+
pairParam.fromToken.toLocaleLowerCase() ===
|
|
247
|
+
pairParam.token0.toLocaleLowerCase(),
|
|
248
|
+
},
|
|
249
|
+
],
|
|
250
|
+
},
|
|
251
|
+
exchange: this.dexKey,
|
|
252
|
+
poolIdentifier,
|
|
253
|
+
gasCost: DefaultSmardexPoolGasCost,
|
|
254
|
+
poolAddresses: [pairParam.exchange],
|
|
255
|
+
},
|
|
256
|
+
];
|
|
257
|
+
} catch (e) {
|
|
258
|
+
if (blockNumber === 0)
|
|
259
|
+
this.logger.error(
|
|
260
|
+
`Error_getPricesVolume: Aurelius block manager not yet instantiated`,
|
|
261
|
+
);
|
|
262
|
+
this.logger.error(`Error_getPrices:`, e);
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
async getBuyPricePath(
|
|
268
|
+
amount: bigint,
|
|
269
|
+
params: SmardexPoolOrderedParams[],
|
|
270
|
+
): Promise<bigint> {
|
|
271
|
+
let price = amount;
|
|
272
|
+
for (const param of params.reverse()) {
|
|
273
|
+
price = await this.getBuyPrice(param, price);
|
|
274
|
+
}
|
|
275
|
+
return price;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
async getSellPricePath(
|
|
279
|
+
amount: bigint,
|
|
280
|
+
params: SmardexPoolOrderedParams[],
|
|
281
|
+
): Promise<bigint> {
|
|
282
|
+
let price = amount;
|
|
283
|
+
for (const param of params) {
|
|
284
|
+
price = await this.getSellPrice(param, price);
|
|
285
|
+
}
|
|
286
|
+
return price;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
async getBuyPrice(
|
|
290
|
+
priceParams: SmardexPoolOrderedParams,
|
|
291
|
+
destAmount: bigint,
|
|
292
|
+
): Promise<bigint> {
|
|
293
|
+
const amountIn = computeAmountIn(
|
|
294
|
+
priceParams.token0,
|
|
295
|
+
priceParams.token1,
|
|
296
|
+
priceParams.reserves0,
|
|
297
|
+
priceParams.reserves1,
|
|
298
|
+
priceParams.fictiveReserves0,
|
|
299
|
+
priceParams.fictiveReserves1,
|
|
300
|
+
destAmount,
|
|
301
|
+
priceParams.toToken,
|
|
302
|
+
+priceParams.priceAverageLastTimestamp,
|
|
303
|
+
priceParams.priceAverage0,
|
|
304
|
+
priceParams.priceAverage1,
|
|
305
|
+
priceParams.feesLP,
|
|
306
|
+
priceParams.feesPool,
|
|
307
|
+
).amount;
|
|
308
|
+
|
|
309
|
+
return BigInt(amountIn.toString());
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
async getSellPrice(
|
|
313
|
+
priceParams: SmardexPoolOrderedParams,
|
|
314
|
+
srcAmount: bigint,
|
|
315
|
+
): Promise<bigint> {
|
|
316
|
+
const amountOut = computeAmountOut(
|
|
317
|
+
priceParams.token0,
|
|
318
|
+
priceParams.token1,
|
|
319
|
+
priceParams.reserves0,
|
|
320
|
+
priceParams.reserves1,
|
|
321
|
+
priceParams.fictiveReserves0,
|
|
322
|
+
priceParams.fictiveReserves1,
|
|
323
|
+
srcAmount,
|
|
324
|
+
priceParams.fromToken,
|
|
325
|
+
+priceParams.priceAverageLastTimestamp,
|
|
326
|
+
priceParams.priceAverage0,
|
|
327
|
+
priceParams.priceAverage1,
|
|
328
|
+
priceParams.feesLP,
|
|
329
|
+
priceParams.feesPool,
|
|
330
|
+
).amount;
|
|
331
|
+
|
|
332
|
+
return BigInt(amountOut.toString());
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Returns estimated gas cost of calldata for this DEX in multiSwap
|
|
336
|
+
getCalldataGasCost(_poolPrices: PoolPrices<SmardexData>): number | number[] {
|
|
337
|
+
return (
|
|
338
|
+
CALLDATA_GAS_COST.DEX_OVERHEAD +
|
|
339
|
+
CALLDATA_GAS_COST.LENGTH_SMALL +
|
|
340
|
+
// ParentStruct header
|
|
341
|
+
CALLDATA_GAS_COST.OFFSET_SMALL +
|
|
342
|
+
// ParentStruct -> weth
|
|
343
|
+
CALLDATA_GAS_COST.ADDRESS +
|
|
344
|
+
// ParentStruct -> pools[] header
|
|
345
|
+
CALLDATA_GAS_COST.OFFSET_SMALL +
|
|
346
|
+
// ParentStruct -> pools[]
|
|
347
|
+
CALLDATA_GAS_COST.LENGTH_SMALL +
|
|
348
|
+
// ParentStruct -> pools[0]
|
|
349
|
+
CALLDATA_GAS_COST.wordNonZeroBytes(22)
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
async findPair(from: Token, to: Token) {
|
|
354
|
+
if (from.address.toLowerCase() === to.address.toLowerCase()) return null;
|
|
355
|
+
const [token0, token1] =
|
|
356
|
+
BigInt(from.address.toLowerCase()) < BigInt(to.address.toLowerCase())
|
|
357
|
+
? [from, to]
|
|
358
|
+
: [to, from];
|
|
359
|
+
|
|
360
|
+
const key = `${token0.address.toLowerCase()}-${token1.address.toLowerCase()}`;
|
|
361
|
+
let pair = this.pairs[key];
|
|
362
|
+
if (pair) return pair;
|
|
363
|
+
const exchange = await this.factory.methods
|
|
364
|
+
.getPair(token0.address, token1.address)
|
|
365
|
+
.call();
|
|
366
|
+
if (exchange === NULL_ADDRESS) {
|
|
367
|
+
pair = { token0, token1 };
|
|
368
|
+
} else {
|
|
369
|
+
pair = { token0, token1, exchange };
|
|
370
|
+
}
|
|
371
|
+
this.pairs[key] = pair;
|
|
372
|
+
return pair;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
async batchCatchUpPairs(pairs: [Token, Token][], blockNumber: number) {
|
|
376
|
+
if (!blockNumber) return;
|
|
377
|
+
const pairsToFetch: SmardexPair[] = [];
|
|
378
|
+
for (const _pair of pairs) {
|
|
379
|
+
const pair = await this.findPair(_pair[0], _pair[1]);
|
|
380
|
+
if (!(pair && pair.exchange)) continue;
|
|
381
|
+
if (!pair.pool) {
|
|
382
|
+
pairsToFetch.push(pair);
|
|
383
|
+
} else if (!pair.pool.getState(blockNumber)) {
|
|
384
|
+
pairsToFetch.push(pair);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
if (!pairsToFetch.length) return;
|
|
389
|
+
|
|
390
|
+
const reserves = await this.getManyPoolReserves(pairsToFetch, blockNumber);
|
|
391
|
+
|
|
392
|
+
if (reserves.length !== pairsToFetch.length) {
|
|
393
|
+
this.logger.error(
|
|
394
|
+
`Error_getManyPoolReserves didn't get any pool reserves`,
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
for (let i = 0; i < pairsToFetch.length; i++) {
|
|
399
|
+
const pairState = reserves[i];
|
|
400
|
+
const pair = pairsToFetch[i];
|
|
401
|
+
if (!pair.pool) {
|
|
402
|
+
await this.addPool(
|
|
403
|
+
pair,
|
|
404
|
+
pairState.reserves0,
|
|
405
|
+
pairState.reserves1,
|
|
406
|
+
pairState.fictiveReserves0,
|
|
407
|
+
pairState.fictiveReserves1,
|
|
408
|
+
pairState.priceAverage0,
|
|
409
|
+
pairState.priceAverage1,
|
|
410
|
+
pairState.feesLP,
|
|
411
|
+
pairState.feesPool,
|
|
412
|
+
blockNumber,
|
|
413
|
+
pairState.priceAverageLastTimestamp,
|
|
414
|
+
);
|
|
415
|
+
} else pair.pool.setState(pairState, blockNumber);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// On Smardex the fees are upgradable on layer 2.
|
|
420
|
+
public getFeesMultiCallData(pairAddress: string) {
|
|
421
|
+
if (this.isLayer1()) {
|
|
422
|
+
return null;
|
|
423
|
+
}
|
|
424
|
+
const callEntry = {
|
|
425
|
+
target: pairAddress,
|
|
426
|
+
callData: smardexPoolL2.encodeFunctionData('getPairFees'),
|
|
427
|
+
};
|
|
428
|
+
const callDecoder = (values: any[]): SmardexFees => {
|
|
429
|
+
const feesData = smardexPoolL2.decodeFunctionResult(
|
|
430
|
+
'getPairFees',
|
|
431
|
+
values,
|
|
432
|
+
);
|
|
433
|
+
return {
|
|
434
|
+
feesLP: feesData.feesLP_.toBigInt(),
|
|
435
|
+
feesPool: feesData.feesPool_.toBigInt(),
|
|
436
|
+
};
|
|
437
|
+
};
|
|
438
|
+
return {
|
|
439
|
+
callEntry,
|
|
440
|
+
callDecoder,
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
protected async addPool(
|
|
445
|
+
pair: SmardexPair,
|
|
446
|
+
reserves0: string,
|
|
447
|
+
reserves1: string,
|
|
448
|
+
fictiveReserves0: string,
|
|
449
|
+
fictiveReserves1: string,
|
|
450
|
+
priceAverage0: string,
|
|
451
|
+
priceAverage1: string,
|
|
452
|
+
feesLp: bigint,
|
|
453
|
+
feesPool: bigint,
|
|
454
|
+
blockNumber: number,
|
|
455
|
+
priceAverageLastTimestamp: number,
|
|
456
|
+
) {
|
|
457
|
+
const multiCallFeeData = this.getFeesMultiCallData(pair.exchange!);
|
|
458
|
+
pair.pool = new SmardexEventPool(
|
|
459
|
+
this.isLayer1() ? smardexPoolL1 : smardexPoolL2,
|
|
460
|
+
this.dexHelper,
|
|
461
|
+
pair.exchange!,
|
|
462
|
+
pair.token0,
|
|
463
|
+
pair.token1,
|
|
464
|
+
this.logger,
|
|
465
|
+
// For layer 2 we need to fetch the fees
|
|
466
|
+
multiCallFeeData?.callEntry,
|
|
467
|
+
multiCallFeeData?.callDecoder,
|
|
468
|
+
this.isLayer1(),
|
|
469
|
+
);
|
|
470
|
+
pair.pool.addressesSubscribed.push(pair.exchange!);
|
|
471
|
+
|
|
472
|
+
await pair.pool.initialize(blockNumber, {
|
|
473
|
+
state: {
|
|
474
|
+
reserves0,
|
|
475
|
+
reserves1,
|
|
476
|
+
fictiveReserves0,
|
|
477
|
+
fictiveReserves1,
|
|
478
|
+
priceAverage0,
|
|
479
|
+
priceAverage1,
|
|
480
|
+
feesLP: feesLp,
|
|
481
|
+
feesPool,
|
|
482
|
+
priceAverageLastTimestamp,
|
|
483
|
+
},
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
async getManyPoolReserves(
|
|
488
|
+
pairs: SmardexPair[],
|
|
489
|
+
blockNumber: number,
|
|
490
|
+
): Promise<SmardexPoolState[]> {
|
|
491
|
+
try {
|
|
492
|
+
const multiCallFeeData = pairs.map(pair =>
|
|
493
|
+
this.getFeesMultiCallData(pair.exchange!),
|
|
494
|
+
);
|
|
495
|
+
const calldata = pairs
|
|
496
|
+
.map((pair, i) => {
|
|
497
|
+
let calldata = [
|
|
498
|
+
{
|
|
499
|
+
target: pair.exchange!,
|
|
500
|
+
callData: smardexPoolL1.encodeFunctionData('getReserves'),
|
|
501
|
+
},
|
|
502
|
+
{
|
|
503
|
+
target: pair.exchange!,
|
|
504
|
+
callData: smardexPoolL1.encodeFunctionData('getFictiveReserves'),
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
target: pair.exchange!,
|
|
508
|
+
callData: smardexPoolL1.encodeFunctionData('getPriceAverage'),
|
|
509
|
+
},
|
|
510
|
+
];
|
|
511
|
+
// Fetch fees only on layer 2
|
|
512
|
+
!this.isLayer1() && calldata.push(multiCallFeeData[i]!.callEntry);
|
|
513
|
+
return calldata;
|
|
514
|
+
})
|
|
515
|
+
.flat();
|
|
516
|
+
|
|
517
|
+
const data: { returnData: any[] } =
|
|
518
|
+
await this.dexHelper.multiContract.methods
|
|
519
|
+
.aggregate(calldata)
|
|
520
|
+
.call({}, blockNumber);
|
|
521
|
+
|
|
522
|
+
const returnData = _.chunk(data.returnData, 4);
|
|
523
|
+
return pairs.map((_pair: SmardexPair, i) => ({
|
|
524
|
+
reserves0: coder
|
|
525
|
+
.decode(['uint256', 'uint256'], returnData[i][0])[0]
|
|
526
|
+
.toString(),
|
|
527
|
+
reserves1: coder
|
|
528
|
+
.decode(['uint256', 'uint256'], returnData[i][0])[1]
|
|
529
|
+
.toString(),
|
|
530
|
+
fictiveReserves0: coder
|
|
531
|
+
.decode(['uint256', 'uint256'], returnData[i][1])[0]
|
|
532
|
+
.toString(),
|
|
533
|
+
fictiveReserves1: coder
|
|
534
|
+
.decode(['uint256', 'uint256'], returnData[i][1])[1]
|
|
535
|
+
.toString(),
|
|
536
|
+
priceAverage0: coder
|
|
537
|
+
.decode(['uint256', 'uint256', 'uint256'], returnData[i][2])[0]
|
|
538
|
+
.toString(),
|
|
539
|
+
priceAverage1: coder
|
|
540
|
+
.decode(['uint256', 'uint256', 'uint256'], returnData[i][2])[1]
|
|
541
|
+
.toString(),
|
|
542
|
+
priceAverageLastTimestamp: coder
|
|
543
|
+
.decode(['uint256', 'uint256', 'uint256'], returnData[i][2])[2]
|
|
544
|
+
.toString(),
|
|
545
|
+
feesLP: this.isLayer1()
|
|
546
|
+
? FEES_LAYER_ONE.feesLP
|
|
547
|
+
: multiCallFeeData[i]!.callDecoder(returnData[i][3]).feesLP,
|
|
548
|
+
feesPool: this.isLayer1()
|
|
549
|
+
? FEES_LAYER_ONE.feesPool
|
|
550
|
+
: multiCallFeeData[i]!.callDecoder(returnData[i][3]).feesPool,
|
|
551
|
+
}));
|
|
552
|
+
} catch (e) {
|
|
553
|
+
this.logger.error(
|
|
554
|
+
`Error_getManyPoolReserves could not get reserves with error:`,
|
|
555
|
+
e,
|
|
556
|
+
);
|
|
557
|
+
return [];
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
protected fixPath(path: Address[], srcToken: Address, destToken: Address) {
|
|
562
|
+
return path.map((token: string, i: number) => {
|
|
563
|
+
if (
|
|
564
|
+
(i === 0 && srcToken.toLowerCase() === ETHER_ADDRESS.toLowerCase()) ||
|
|
565
|
+
(i === path.length - 1 &&
|
|
566
|
+
destToken.toLowerCase() === ETHER_ADDRESS.toLowerCase())
|
|
567
|
+
)
|
|
568
|
+
return ETHER_ADDRESS;
|
|
569
|
+
return token;
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
async getPairOrderedParams(
|
|
574
|
+
from: Token,
|
|
575
|
+
to: Token,
|
|
576
|
+
blockNumber: number,
|
|
577
|
+
tokenDexTransferFee: number,
|
|
578
|
+
): Promise<SmardexPoolOrderedParams | null> {
|
|
579
|
+
const pair = await this.findPair(from, to);
|
|
580
|
+
if (!(pair && pair.pool && pair.exchange)) return null;
|
|
581
|
+
const pairState = pair.pool.getState(blockNumber);
|
|
582
|
+
if (!pairState) {
|
|
583
|
+
this.logger.error(
|
|
584
|
+
`Error_orderPairParams expected reserves, got none (maybe the pool doesn't exist) ${
|
|
585
|
+
from.symbol || from.address
|
|
586
|
+
} ${to.symbol || to.address}`,
|
|
587
|
+
);
|
|
588
|
+
return null;
|
|
589
|
+
}
|
|
590
|
+
// const fee = (pairState.feesPool + tokenDexTransferFee).toString();
|
|
591
|
+
|
|
592
|
+
return {
|
|
593
|
+
fromToken: from.address,
|
|
594
|
+
toToken: to.address,
|
|
595
|
+
token0: pair.token0.address,
|
|
596
|
+
token1: pair.token1.address,
|
|
597
|
+
reserves0: BigInt(pairState.reserves0),
|
|
598
|
+
reserves1: BigInt(pairState.reserves1),
|
|
599
|
+
fictiveReserves0: BigInt(pairState.fictiveReserves0),
|
|
600
|
+
fictiveReserves1: BigInt(pairState.fictiveReserves1),
|
|
601
|
+
priceAverage0: BigInt(pairState.priceAverage0),
|
|
602
|
+
priceAverage1: BigInt(pairState.priceAverage1),
|
|
603
|
+
priceAverageLastTimestamp: pairState.priceAverageLastTimestamp,
|
|
604
|
+
exchange: pair.exchange,
|
|
605
|
+
feesLP: BigInt(pairState.feesLP),
|
|
606
|
+
feesPool: BigInt(pairState.feesPool),
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
getWETHAddress(srcToken: Address, destToken: Address, weth?: Address) {
|
|
611
|
+
if (!isETHAddress(srcToken) && !isETHAddress(destToken))
|
|
612
|
+
return NULL_ADDRESS;
|
|
613
|
+
return weth || this.dexHelper.config.data.wrappedNativeTokenAddress;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
getAdapterParam(
|
|
617
|
+
srcToken: Address,
|
|
618
|
+
destToken: Address,
|
|
619
|
+
srcAmount: NumberAsString,
|
|
620
|
+
toAmount: NumberAsString,
|
|
621
|
+
data: SmardexData,
|
|
622
|
+
side: SwapSide,
|
|
623
|
+
): AdapterExchangeParam {
|
|
624
|
+
const payload = this.abiCoder.encodeParameter(
|
|
625
|
+
{
|
|
626
|
+
ParentStruct: {
|
|
627
|
+
path: 'address[]',
|
|
628
|
+
receiver: 'address',
|
|
629
|
+
},
|
|
630
|
+
},
|
|
631
|
+
{
|
|
632
|
+
path: data.path,
|
|
633
|
+
receiver: data.receiver,
|
|
634
|
+
},
|
|
635
|
+
);
|
|
636
|
+
return {
|
|
637
|
+
targetExchange: data.router,
|
|
638
|
+
payload,
|
|
639
|
+
networkFee: '0',
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
async getSimpleParam(
|
|
644
|
+
src: Address,
|
|
645
|
+
dest: Address,
|
|
646
|
+
srcAmount: NumberAsString,
|
|
647
|
+
destAmount: NumberAsString,
|
|
648
|
+
data: SmardexData,
|
|
649
|
+
side: SwapSide,
|
|
650
|
+
): Promise<SimpleExchangeParam> {
|
|
651
|
+
let routerMethod: any;
|
|
652
|
+
let routerArgs: any;
|
|
653
|
+
if (side === SwapSide.SELL) {
|
|
654
|
+
routerMethod = isETHAddress(src)
|
|
655
|
+
? SmardexRouterFunctions.sellExactEth
|
|
656
|
+
: SmardexRouterFunctions.swapExactIn;
|
|
657
|
+
routerMethod = isETHAddress(dest)
|
|
658
|
+
? SmardexRouterFunctions.sellExactToken
|
|
659
|
+
: routerMethod;
|
|
660
|
+
|
|
661
|
+
routerArgs = isETHAddress(src)
|
|
662
|
+
? [destAmount, data.path, data.receiver, data.deadline]
|
|
663
|
+
: [srcAmount, destAmount, data.path, data.receiver, data.deadline];
|
|
664
|
+
} else {
|
|
665
|
+
routerMethod = isETHAddress(src)
|
|
666
|
+
? SmardexRouterFunctions.buyExactToken
|
|
667
|
+
: SmardexRouterFunctions.swapExactOut;
|
|
668
|
+
routerMethod = isETHAddress(dest)
|
|
669
|
+
? SmardexRouterFunctions.buyExactEth
|
|
670
|
+
: routerMethod;
|
|
671
|
+
|
|
672
|
+
routerArgs = isETHAddress(src)
|
|
673
|
+
? [destAmount, data.path, data.receiver, data.deadline]
|
|
674
|
+
: [destAmount, srcAmount, data.path, data.receiver, data.deadline];
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
const swapData = this.exchangeRouterInterface.encodeFunctionData(
|
|
678
|
+
routerMethod,
|
|
679
|
+
routerArgs,
|
|
680
|
+
);
|
|
681
|
+
return this.buildSimpleParamWithoutWETHConversion(
|
|
682
|
+
src,
|
|
683
|
+
srcAmount,
|
|
684
|
+
dest,
|
|
685
|
+
destAmount,
|
|
686
|
+
swapData,
|
|
687
|
+
data.router,
|
|
688
|
+
);
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
isLayer1(): boolean {
|
|
692
|
+
return this.network === Network.MAINNET;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
async getTopPoolsForToken(
|
|
696
|
+
tokenAddress: Address,
|
|
697
|
+
limit: number,
|
|
698
|
+
): Promise<PoolLiquidity[]> {
|
|
699
|
+
if (!this.subgraphURL) return [];
|
|
700
|
+
const query = `
|
|
701
|
+
query ($token: Bytes!, $limit: Int) {
|
|
702
|
+
pools0: pairs(first: $limit, orderBy: reserveUSD, orderDirection: desc, where: {token0: $token, reserve0_gt: 1, reserve1_gt: 1}) {
|
|
703
|
+
id
|
|
704
|
+
token0 {
|
|
705
|
+
id
|
|
706
|
+
decimals
|
|
707
|
+
}
|
|
708
|
+
token1 {
|
|
709
|
+
id
|
|
710
|
+
decimals
|
|
711
|
+
}
|
|
712
|
+
reserveUSD
|
|
713
|
+
}
|
|
714
|
+
pools1: pairs(first: $limit, orderBy: reserveUSD, orderDirection: desc, where: {token1: $token, reserve0_gt: 1, reserve1_gt: 1}) {
|
|
715
|
+
id
|
|
716
|
+
token0 {
|
|
717
|
+
id
|
|
718
|
+
decimals
|
|
719
|
+
}
|
|
720
|
+
token1 {
|
|
721
|
+
id
|
|
722
|
+
decimals
|
|
723
|
+
}
|
|
724
|
+
reserveUSD
|
|
725
|
+
}
|
|
726
|
+
}`;
|
|
727
|
+
|
|
728
|
+
const { data } = await this.dexHelper.httpRequest.post(
|
|
729
|
+
this.subgraphURL,
|
|
730
|
+
{
|
|
731
|
+
query,
|
|
732
|
+
variables: { token: tokenAddress.toLowerCase(), limit },
|
|
733
|
+
},
|
|
734
|
+
SUBGRAPH_TIMEOUT,
|
|
735
|
+
{ 'x-api-key': this.dexHelper.config.data.smardexSubgraphAuthToken! },
|
|
736
|
+
);
|
|
737
|
+
|
|
738
|
+
if (!(data && data.pools0 && data.pools1))
|
|
739
|
+
throw new Error("Couldn't fetch the pools from the subgraph");
|
|
740
|
+
const pools0 = _.map(data.pools0, pool => ({
|
|
741
|
+
exchange: this.dexKey,
|
|
742
|
+
address: pool.id.toLowerCase(),
|
|
743
|
+
connectorTokens: [
|
|
744
|
+
{
|
|
745
|
+
address: pool.token1.id.toLowerCase(),
|
|
746
|
+
decimals: parseInt(pool.token1.decimals),
|
|
747
|
+
},
|
|
748
|
+
],
|
|
749
|
+
liquidityUSD: parseFloat(pool.reserveUSD),
|
|
750
|
+
}));
|
|
751
|
+
|
|
752
|
+
const pools1 = _.map(data.pools1, pool => ({
|
|
753
|
+
exchange: this.dexKey,
|
|
754
|
+
address: pool.id.toLowerCase(),
|
|
755
|
+
connectorTokens: [
|
|
756
|
+
{
|
|
757
|
+
address: pool.token0.id.toLowerCase(),
|
|
758
|
+
decimals: parseInt(pool.token0.decimals),
|
|
759
|
+
},
|
|
760
|
+
],
|
|
761
|
+
liquidityUSD: parseFloat(pool.reserveUSD),
|
|
762
|
+
}));
|
|
763
|
+
|
|
764
|
+
return _.slice(
|
|
765
|
+
_.sortBy(_.concat(pools0, pools1), [pool => -1 * pool.liquidityUSD]),
|
|
766
|
+
0,
|
|
767
|
+
limit,
|
|
768
|
+
);
|
|
769
|
+
}
|
|
770
|
+
}
|