@paraswap/dex-lib 4.6.21 → 4.6.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/build/abi/PendleRouterStatic.json +19 -0
  2. package/build/abi/apex-defi/ApexDefiFactory.abi.json +1749 -0
  3. package/build/abi/apex-defi/ApexDefiRouter.abi.json +1120 -0
  4. package/build/abi/apex-defi/ApexDefiToken.abi.json +229 -0
  5. package/build/abi/apex-defi/ApexDefiWrapper.abi.json +92 -0
  6. package/build/abi/apex-defi/ApexDefiWrapperFactory.abi.json +1107 -0
  7. package/build/abi/ekubo/router.json +416 -398
  8. package/build/dex/aave-pt-to-underlying/aave-pt-to-underlying.d.ts +2 -3
  9. package/build/dex/aave-pt-to-underlying/aave-pt-to-underlying.js +53 -26
  10. package/build/dex/aave-pt-to-underlying/aave-pt-to-underlying.js.map +1 -1
  11. package/build/dex/aave-pt-to-underlying/config.js +3 -0
  12. package/build/dex/aave-pt-to-underlying/config.js.map +1 -1
  13. package/build/dex/aave-pt-to-underlying/types.d.ts +1 -0
  14. package/build/dex/apex-defi/apex-defi-factory.d.ts +26 -0
  15. package/build/dex/apex-defi/apex-defi-factory.js +53 -0
  16. package/build/dex/apex-defi/apex-defi-factory.js.map +1 -0
  17. package/build/dex/apex-defi/apex-defi-pool.d.ts +55 -0
  18. package/build/dex/apex-defi/apex-defi-pool.js +247 -0
  19. package/build/dex/apex-defi/apex-defi-pool.js.map +1 -0
  20. package/build/dex/apex-defi/apex-defi-wrapper-factory.d.ts +57 -0
  21. package/build/dex/apex-defi/apex-defi-wrapper-factory.js +250 -0
  22. package/build/dex/apex-defi/apex-defi-wrapper-factory.js.map +1 -0
  23. package/build/dex/apex-defi/apex-defi.d.ts +97 -0
  24. package/build/dex/apex-defi/apex-defi.js +1021 -0
  25. package/build/dex/apex-defi/apex-defi.js.map +1 -0
  26. package/build/dex/apex-defi/config.d.ts +4 -0
  27. package/build/dex/apex-defi/config.js +138 -0
  28. package/build/dex/apex-defi/config.js.map +1 -0
  29. package/build/dex/apex-defi/types.d.ts +32 -0
  30. package/build/dex/apex-defi/types.js +3 -0
  31. package/build/dex/apex-defi/types.js.map +1 -0
  32. package/build/dex/apex-defi/utils.d.ts +46 -0
  33. package/build/dex/apex-defi/utils.js +133 -0
  34. package/build/dex/apex-defi/utils.js.map +1 -0
  35. package/build/dex/ekubo/config.d.ts +2 -1
  36. package/build/dex/ekubo/config.js +6 -4
  37. package/build/dex/ekubo/config.js.map +1 -1
  38. package/build/dex/ekubo/ekubo.d.ts +13 -12
  39. package/build/dex/ekubo/ekubo.js +282 -234
  40. package/build/dex/ekubo/ekubo.js.map +1 -1
  41. package/build/dex/ekubo/pools/base.d.ts +6 -2
  42. package/build/dex/ekubo/pools/base.js +120 -113
  43. package/build/dex/ekubo/pools/base.js.map +1 -1
  44. package/build/dex/ekubo/pools/full-range.d.ts +6 -5
  45. package/build/dex/ekubo/pools/full-range.js +50 -40
  46. package/build/dex/ekubo/pools/full-range.js.map +1 -1
  47. package/build/dex/ekubo/pools/math/swap.js +3 -3
  48. package/build/dex/ekubo/pools/math/swap.js.map +1 -1
  49. package/build/dex/ekubo/pools/math/tick.d.ts +1 -0
  50. package/build/dex/ekubo/pools/math/tick.js +9 -4
  51. package/build/dex/ekubo/pools/math/tick.js.map +1 -1
  52. package/build/dex/ekubo/pools/oracle.d.ts +5 -6
  53. package/build/dex/ekubo/pools/oracle.js +8 -10
  54. package/build/dex/ekubo/pools/oracle.js.map +1 -1
  55. package/build/dex/ekubo/pools/twamm.d.ts +5 -3
  56. package/build/dex/ekubo/pools/twamm.js +102 -94
  57. package/build/dex/ekubo/pools/twamm.js.map +1 -1
  58. package/build/dex/ekubo/pools/utils.d.ts +8 -7
  59. package/build/dex/ekubo/pools/utils.js +31 -18
  60. package/build/dex/ekubo/pools/utils.js.map +1 -1
  61. package/build/dex/ekubo/types.d.ts +1 -0
  62. package/build/dex/ekubo/utils.d.ts +0 -1
  63. package/build/dex/ekubo/utils.js +0 -4
  64. package/build/dex/ekubo/utils.js.map +1 -1
  65. package/build/dex/yo/config.d.ts +3 -0
  66. package/build/dex/yo/config.js +21 -0
  67. package/build/dex/yo/config.js.map +1 -0
  68. package/build/dex/yo/types.d.ts +13 -0
  69. package/build/dex/yo/types.js +3 -0
  70. package/build/dex/yo/types.js.map +1 -0
  71. package/build/dex/yo/yo-pool.d.ts +13 -0
  72. package/build/dex/yo/yo-pool.js +26 -0
  73. package/build/dex/yo/yo-pool.js.map +1 -0
  74. package/build/dex/yo/yo.d.ts +39 -0
  75. package/build/dex/yo/yo.js +248 -0
  76. package/build/dex/yo/yo.js.map +1 -0
  77. package/package.json +1 -1
@@ -51,12 +51,14 @@ const utils_3 = require("ethers/lib/utils");
51
51
  const router_json_1 = __importDefault(require("../../abi/ekubo/router.json"));
52
52
  const full_range_1 = require("./pools/full-range");
53
53
  const constants_2 = require("./pools/math/constants");
54
- const price_1 = require("./pools/math/price");
54
+ const sqrt_ratio_1 = require("./pools/math/sqrt-ratio");
55
55
  const swap_1 = require("./pools/math/swap");
56
56
  const tick_1 = require("./pools/math/tick");
57
57
  const oracle_1 = require("./pools/oracle");
58
58
  const twamm_1 = require("./pools/twamm");
59
59
  const utils_4 = require("./pools/utils");
60
+ const mev_resist_1 = require("./pools/mev-resist");
61
+ const utils_5 = require("../../lib/tokens/utils");
60
62
  const FALLBACK_POOL_PARAMETERS = [
61
63
  {
62
64
  fee: 1844674407370955n,
@@ -79,15 +81,6 @@ const FALLBACK_POOL_PARAMETERS = [
79
81
  tickSpacing: 95310,
80
82
  },
81
83
  ];
82
- const tokenPairSchema = joi_1.default.object({
83
- topPools: joi_1.default.array().items(joi_1.default.object({
84
- fee: joi_1.default.string(),
85
- tick_spacing: joi_1.default.number(),
86
- extension: joi_1.default.string(),
87
- tvl0_total: joi_1.default.string(),
88
- tvl1_total: joi_1.default.string(),
89
- })),
90
- });
91
84
  const allPoolsSchema = joi_1.default.array().items(joi_1.default.object({
92
85
  core_address: joi_1.default.string(),
93
86
  token0: joi_1.default.string(),
@@ -107,9 +100,9 @@ class Ekubo extends simple_exchange_1.SimpleExchange {
107
100
  hasConstantPriceLargeAmounts = false;
108
101
  needWrapNative = false;
109
102
  isFeeOnTransferSupported = false;
110
- static dexKeysWithNetwork = (0, utils_1.getDexKeysWithNetwork)(config_1.EkuboConfig);
111
- poolKeys = [];
103
+ static dexKeysWithNetwork = (0, utils_1.getDexKeysWithNetwork)(config_1.EKUBO_CONFIG);
112
104
  pools = new Map();
105
+ poolKeysSynced = false;
113
106
  logger;
114
107
  config;
115
108
  routerIface;
@@ -117,61 +110,146 @@ class Ekubo extends simple_exchange_1.SimpleExchange {
117
110
  supportedExtensions;
118
111
  interval;
119
112
  // Caches the number of decimals for TVL computation purposes
120
- /*private readonly decimals: Record<string, number> = {
121
- [ETHER_ADDRESS]: 18,
122
- };*/
113
+ decimals = {
114
+ [constants_1.ETHER_ADDRESS]: 18,
115
+ };
123
116
  constructor(network, dexKey, dexHelper) {
124
117
  super(dexHelper, dexKey);
125
118
  this.network = network;
126
119
  this.dexKey = dexKey;
127
120
  this.dexHelper = dexHelper;
128
121
  this.logger = dexHelper.getLogger(dexKey);
129
- this.config = config_1.EkuboConfig[dexKey][network];
122
+ this.config = config_1.EKUBO_CONFIG[dexKey][network];
130
123
  this.contracts = (0, utils_2.contractsFromDexParams)(this.config, dexHelper.provider);
131
124
  this.routerIface = new abi_1.Interface(router_json_1.default);
132
- // 0 are vanilla pools
133
125
  this.supportedExtensions = [
134
- 0n,
126
+ 0n, // Vanilla pools
135
127
  BigInt(this.config.oracle),
136
128
  BigInt(this.config.twamm),
129
+ BigInt(this.config.mevResist),
137
130
  ];
138
131
  }
139
- // Periodically schedules fetching pool keys from the Ekubo API and filling in details with the quote data fetcher
140
132
  async initializePricing(blockNumber) {
141
- await this.updatePoolMap(blockNumber);
142
- this.interval = setInterval(async () => {
143
- await this.updatePoolMap(await this.dexHelper.provider.getBlockNumber());
144
- }, POOL_MAP_UPDATE_INTERVAL_MS);
145
- }
146
- // LEGACY
147
- getAdapters(_side) {
148
- return null;
133
+ await this.updatePools(blockNumber, true);
134
+ // Periodically schedules fetching pool keys from the Ekubo API and filling in details with the quote data fetcher
135
+ this.interval = setInterval(async () => this.updatePools(await this.dexHelper.provider.getBlockNumber(), true), POOL_MAP_UPDATE_INTERVAL_MS);
149
136
  }
150
- async getPoolIdentifiers(srcToken, destToken, _side, _blockNumber) {
151
- const [token0, token1] = (0, utils_2.convertAndSortTokens)(srcToken, destToken);
137
+ async updatePools(blockNumber, subscribe) {
152
138
  let poolKeys;
153
- if (this.poolKeys === null) {
154
- poolKeys = FALLBACK_POOL_PARAMETERS.flatMap(params => [
155
- new utils_4.PoolKey(token0, token1, new utils_4.PoolConfig(params.tickSpacing, params.fee, 0n)),
156
- new utils_4.PoolKey(token0, token1, new utils_4.PoolConfig(0, params.fee, BigInt(this.config.twamm))),
157
- ]);
158
- if ([token0, token1].includes(utils_2.NATIVE_TOKEN_ADDRESS)) {
159
- poolKeys.push(new utils_4.PoolKey(token0, token1, new utils_4.PoolConfig(tick_1.FULL_RANGE_TICK_SPACING, 0n, BigInt(this.config.oracle))));
139
+ try {
140
+ [poolKeys, this.poolKeysSynced] = [await this.fetchAllPoolKeys(), true];
141
+ }
142
+ catch (err) {
143
+ this.logger.error(`Fetching pool keys from Ekubo API failed: ${err}`);
144
+ [poolKeys, this.poolKeysSynced] = [[], false];
145
+ if (subscribe) {
146
+ return;
160
147
  }
161
148
  }
162
- else {
163
- poolKeys = this.poolKeys.filter(poolKey => poolKey.token0 === token0 && poolKey.token1 === token1);
149
+ const untrackedPoolKeys = poolKeys.filter(poolKey => !this.pools.has(poolKey.stringId));
150
+ const [twammPoolKeys, otherPoolKeys] = untrackedPoolKeys.reduce(([twammPoolKeys, otherPoolKeys], poolKey) => {
151
+ if (poolKey.config.extension === BigInt(this.config.twamm)) {
152
+ twammPoolKeys.push(poolKey);
153
+ }
154
+ else {
155
+ otherPoolKeys.push(poolKey);
156
+ }
157
+ return [twammPoolKeys, otherPoolKeys];
158
+ }, [[], []]);
159
+ const promises = [];
160
+ if (!subscribe) {
161
+ promises.push(...this.pools.values().map(pool => pool.updateState(blockNumber).catch(err => {
162
+ this.logger.error(`Updating state of pool ${pool.key.stringId} failed: ${err}`);
163
+ })));
164
+ }
165
+ const commonArgs = [
166
+ this.dexKey,
167
+ this.dexHelper,
168
+ this.logger,
169
+ this.contracts,
170
+ ];
171
+ const addPool = async (constructor, initialState, poolKey) => {
172
+ const pool = new constructor(...commonArgs, poolKey);
173
+ if (subscribe) {
174
+ await pool.initialize(blockNumber, { state: initialState });
175
+ }
176
+ else {
177
+ pool.setState(initialState ?? (await pool.generateState(blockNumber)), blockNumber);
178
+ }
179
+ this.pools.set(poolKey.stringId, pool);
180
+ };
181
+ for (let batchStart = 0; batchStart < otherPoolKeys.length; batchStart += MAX_BATCH_SIZE) {
182
+ const batch = otherPoolKeys.slice(batchStart, batchStart + MAX_BATCH_SIZE);
183
+ promises.push(this.contracts.core.dataFetcher.getQuoteData(batch.map(poolKey => poolKey.toAbi()), MIN_TICK_SPACINGS_PER_POOL, {
184
+ blockTag: blockNumber,
185
+ })
186
+ .then(async (fetchedData) => {
187
+ await Promise.all(fetchedData.map(async (data, i) => {
188
+ const poolKey = otherPoolKeys[batchStart + i];
189
+ const extension = poolKey.config.extension;
190
+ try {
191
+ switch (extension) {
192
+ case 0n: {
193
+ if (poolKey.config.tickSpacing === 0) {
194
+ await addPool(full_range_1.FullRangePool, full_range_1.FullRangePoolState.fromQuoter(data), poolKey);
195
+ }
196
+ else {
197
+ await addPool(base_1.BasePool, base_1.BasePoolState.fromQuoter(data), poolKey);
198
+ }
199
+ break;
200
+ }
201
+ case BigInt(this.config.oracle): {
202
+ await addPool(oracle_1.OraclePool, full_range_1.FullRangePoolState.fromQuoter(data), poolKey);
203
+ break;
204
+ }
205
+ case BigInt(this.config.mevResist): {
206
+ await addPool(mev_resist_1.MevResistPool, base_1.BasePoolState.fromQuoter(data), poolKey);
207
+ break;
208
+ }
209
+ default:
210
+ throw new Error(`Unknown pool extension ${(0, utils_3.hexZeroPad)((0, utils_3.hexlify)(extension), 20)}`);
211
+ }
212
+ }
213
+ catch (err) {
214
+ this.logger.error(`Failed to construct pool ${poolKey.stringId}: ${err}`);
215
+ }
216
+ }));
217
+ })
218
+ .catch((err) => {
219
+ this.logger.error(`Fetching batch failed. Pool keys: ${batch.map(poolKey => poolKey.stringId)}. Error: ${err}`);
220
+ }));
164
221
  }
165
- const ids = [];
166
- for (const poolKey of poolKeys) {
167
- if (this.pools.has(poolKey.string_id)) {
168
- ids.push(poolKey.string_id);
222
+ promises.push(...twammPoolKeys.map(async (poolKey) => {
223
+ // The TWAMM data fetcher doesn't allow fetching state for multiple pools at once, so we just let `generateState` work to avoid duplicating logic
224
+ try {
225
+ await addPool(twamm_1.TwammPool, undefined, poolKey);
226
+ }
227
+ catch (err) {
228
+ this.logger.error(`Failed to construct pool ${poolKey.stringId}: ${err}`);
229
+ }
230
+ }));
231
+ await Promise.all(promises);
232
+ }
233
+ async getPoolIdentifiers(srcToken, destToken, _side, _blockNumber) {
234
+ const [token0, token1] = (0, utils_2.convertAndSortTokens)(srcToken, destToken);
235
+ const stringIds = new Set(this.pools
236
+ .entries()
237
+ .filter(([_, pool]) => pool.key.token0 === token0 && pool.key.token1 === token1)
238
+ .map(([stringId, _]) => stringId));
239
+ if (!this.poolKeysSynced) {
240
+ for (const params of FALLBACK_POOL_PARAMETERS) {
241
+ stringIds
242
+ .add(new utils_4.PoolKey(token0, token1, new utils_4.PoolConfig(0n, params.fee, params.tickSpacing)).stringId)
243
+ .add(new utils_4.PoolKey(token0, token1, new utils_4.PoolConfig(BigInt(this.config.twamm), params.fee, 0)).stringId);
244
+ }
245
+ if ([token0, token1].includes(utils_2.NATIVE_TOKEN_ADDRESS)) {
246
+ stringIds.add(new utils_4.PoolKey(token0, token1, new utils_4.PoolConfig(BigInt(this.config.oracle), 0n, tick_1.FULL_RANGE_TICK_SPACING)).stringId);
169
247
  }
170
248
  }
171
- return ids;
249
+ return Array.from(stringIds);
172
250
  }
173
251
  async getPricesVolume(srcToken, destToken, amounts, side, blockNumber, limitPools) {
174
- const pools = this.getInitializedPools(srcToken, destToken, limitPools);
252
+ const pools = await this.getPools(srcToken, destToken, blockNumber, limitPools);
175
253
  const isExactOut = side === constants_1.SwapSide.BUY;
176
254
  const amountToken = isExactOut ? destToken : srcToken;
177
255
  const amountTokenAddress = (0, utils_2.convertParaSwapToEkubo)(amountToken.address);
@@ -180,15 +258,15 @@ class Ekubo extends simple_exchange_1.SimpleExchange {
180
258
  const exchangePrices = [];
181
259
  // eslint-disable-next-line no-restricted-syntax
182
260
  poolLoop: for (const pool of pools) {
183
- const poolId = pool.key.string_id;
261
+ const poolId = pool.key.stringId;
184
262
  try {
185
263
  const quotes = [];
186
264
  const skipAheadMap = {};
187
265
  for (const amount of [unitAmount, ...amounts]) {
188
266
  const inputAmount = isExactOut ? -amount : amount;
189
267
  const quote = pool.quote(inputAmount, amountTokenAddress, blockNumber);
190
- if (isExactOut && quote.consumedAmount !== inputAmount) {
191
- this.logger.debug(`Pool ${poolId} doesn't have enough liquidity to support exact-out swap of ${amount} ${amountToken.symbol ?? amountToken.address}`);
268
+ if (quote.consumedAmount !== inputAmount) {
269
+ this.logger.debug(`Pool ${poolId} doesn't have enough liquidity to support swap of ${amount} ${amountToken.symbol ?? amountToken.address}`);
192
270
  // There doesn't seem to be a way to skip just this one price.
193
271
  // Anyway, this pool is probably not the right one if it has such thin liquidity.
194
272
  continue poolLoop;
@@ -217,87 +295,6 @@ class Ekubo extends simple_exchange_1.SimpleExchange {
217
295
  }
218
296
  return exchangePrices;
219
297
  }
220
- // LEGACY
221
- getCalldataGasCost(_poolPrices) {
222
- return CALLDATA_GAS_COST.DEX_NO_PAYLOAD;
223
- }
224
- // LEGACY
225
- getAdapterParam(_srcToken, _destToken, _srcAmount, _destAmount, _data, _side) {
226
- return {
227
- targetExchange: this.dexKey,
228
- payload: '',
229
- networkFee: '0',
230
- };
231
- }
232
- async updatePoolState() { }
233
- async getTopPoolsForToken(tokenAddress, limit) {
234
- return [];
235
- // // The integration tests skip initializePricing, hence this check
236
- // if (this.pools.size === 0) {
237
- // await this.updatePoolMap(await this.dexHelper.provider.getBlockNumber());
238
- // }
239
- // const token = convertParaSwapToEkubo(tokenAddress);
240
- // const settledPromises = await Promise.allSettled(
241
- // Array.from(this.pools.entries()).map(async ([poolId, pool]) => {
242
- // const tokenPair = [pool.key.token0, pool.key.token1];
243
- // if (!tokenPair.includes(token)) {
244
- // return null;
245
- // }
246
- // const tvlRes = pool.computeTvl();
247
- // if (tvlRes === null) {
248
- // throw new Error(`failed to compute TVL for pool ${poolId}`);
249
- // }
250
- // const [info0, info1] = await Promise.all(
251
- // tokenPair.map((ekuboToken, i) =>
252
- // (async () => {
253
- // const paraswapToken = convertEkuboToParaSwap(ekuboToken);
254
- // const decimals = await this.getDecimals(paraswapToken);
255
- // const token = {
256
- // address: paraswapToken,
257
- // decimals,
258
- // };
259
- // return {
260
- // token,
261
- // tvl: await this.dexHelper.getTokenUSDPrice(token, tvlRes[i]),
262
- // };
263
- // })(),
264
- // ),
265
- // );
266
- // return {
267
- // exchange: this.dexKey,
268
- // address: this.config.core,
269
- // connectorTokens: [
270
- // (info0.token.address !== tokenAddress ? info0 : info1).token,
271
- // ],
272
- // liquidityUSD: info0.tvl + info1.tvl,
273
- // };
274
- // }),
275
- // );
276
- // const poolLiquidities = settledPromises.flatMap(res => {
277
- // if (res.status === 'rejected') {
278
- // this.logger.error('TVL computation failed:', res.reason);
279
- // return [];
280
- // }
281
- // return res.value ? [res.value] : [];
282
- // });
283
- // poolLiquidities
284
- // .sort((a, b) => b.liquidityUSD - a.liquidityUSD)
285
- // .splice(limit, Infinity);
286
- // return poolLiquidities;
287
- }
288
- // private async getDecimals(paraswapToken: string): Promise<number> {
289
- // const cached = this.decimals[paraswapToken];
290
- // if (typeof cached === 'number') {
291
- // return cached;
292
- // }
293
- // const decimals: number = await new Contract(
294
- // paraswapToken,
295
- // erc20Iface,
296
- // this.dexHelper.provider,
297
- // ).decimals();
298
- // this.decimals[paraswapToken] = decimals;
299
- // return decimals;
300
- // }
301
298
  getDexParam(_srcToken, _destToken, srcAmount, destAmount, recipient, data, side, _context, _executorAddress) {
302
299
  const amount = BigInt(side === constants_1.SwapSide.BUY ? `-${destAmount}` : srcAmount);
303
300
  const amountStr = (side === constants_1.SwapSide.SELL ? srcAmount : destAmount).toString();
@@ -308,8 +305,8 @@ class Ekubo extends simple_exchange_1.SimpleExchange {
308
305
  data.isToken1,
309
306
  ethers_1.BigNumber.from(amount),
310
307
  (0, swap_1.isPriceIncreasing)(amount, data.isToken1)
311
- ? price_1.MAX_SQRT_RATIO_FLOAT
312
- : price_1.MIN_SQRT_RATIO_FLOAT,
308
+ ? sqrt_ratio_1.MAX_SQRT_RATIO_FLOAT
309
+ : sqrt_ratio_1.MIN_SQRT_RATIO_FLOAT,
313
310
  ethers_1.BigNumber.from(data.skipAhead[amountStr] ?? 0),
314
311
  constants_2.MIN_I256,
315
312
  recipient,
@@ -319,115 +316,77 @@ class Ekubo extends simple_exchange_1.SimpleExchange {
319
316
  returnAmountPos: undefined,
320
317
  };
321
318
  }
322
- releaseResources() {
323
- if (this.interval) {
324
- clearInterval(this.interval);
325
- this.interval = undefined;
326
- }
319
+ async updatePoolState() {
320
+ return this.updatePools(await this.dexHelper.provider.getBlockNumber(), false);
327
321
  }
328
- getInitializedPools(tokenA, tokenB, limitPools) {
329
- const [token0, token1] = (0, utils_2.convertAndSortTokens)(tokenA, tokenB);
330
- const unfilteredPools = typeof limitPools === 'undefined'
331
- ? Array.from(this.pools.values())
332
- : limitPools.flatMap(poolId => {
333
- const pool = this.pools.get(poolId);
334
- if (typeof pool === 'undefined') {
335
- this.logger.warn(`Pool ${poolId} requested but not found`);
336
- return [];
322
+ async getTopPoolsForToken(tokenAddress, limit) {
323
+ const poolsTokenTvls = (await Promise.all(this.pools.values().map(async (pool) => {
324
+ try {
325
+ const tokenPair = [
326
+ (0, utils_2.convertEkuboToParaSwap)(pool.key.token0),
327
+ (0, utils_2.convertEkuboToParaSwap)(pool.key.token1),
328
+ ];
329
+ if (!tokenPair.includes(tokenAddress)) {
330
+ return null;
337
331
  }
338
- return [pool];
339
- });
340
- return unfilteredPools.filter(pool => pool.key.token0 === token0 && pool.key.token1 === token1);
341
- }
342
- async updatePoolMap(blockNumber) {
343
- try {
344
- this.poolKeys = await this.fetchAllPoolKeys();
345
- }
346
- catch (err) {
347
- this.logger.error(`Updating pool map from Ekubo API failed: ${err}`);
348
- this.poolKeys = null;
349
- return;
350
- }
351
- const uninitializedPoolKeys = this.poolKeys.filter(poolKey => !this.pools.has(poolKey.string_id));
352
- const promises = this.initializePools(uninitializedPoolKeys, blockNumber);
353
- (await Promise.allSettled(promises)).flatMap(res => {
354
- if (res.status === 'rejected') {
355
- this.logger.error(`Fetching batch failed. Pool keys: ${res.reason.batch}. Error: ${res.reason.err}`);
332
+ const tvls = pool.computeTvl();
333
+ const [token0Tvl, token1Tvl] = await Promise.all(tokenPair.map(async (tokenAddress, i) => {
334
+ const decimals = await this.getDecimals(tokenAddress);
335
+ if (decimals === null) {
336
+ return null;
337
+ }
338
+ return {
339
+ tvl: tvls[i],
340
+ address: tokenAddress,
341
+ decimals,
342
+ };
343
+ }));
344
+ if (token0Tvl === null || token1Tvl === null) {
345
+ return null;
346
+ }
347
+ return {
348
+ pool,
349
+ token0Tvl,
350
+ token1Tvl,
351
+ };
356
352
  }
353
+ catch (err) {
354
+ this.logger.error(`TVL computation for pool ${pool.key.stringId} failed: ${err}`);
355
+ return null;
356
+ }
357
+ }))).filter(res => res !== null);
358
+ const usdTvls = await this.dexHelper.getUsdTokenAmounts(poolsTokenTvls.flatMap(({ token0Tvl, token1Tvl }) => [
359
+ [token0Tvl.address, token0Tvl.tvl],
360
+ [token1Tvl.address, token1Tvl.tvl],
361
+ ]));
362
+ const poolLiquidities = poolsTokenTvls.map(({ token0Tvl, token1Tvl }, i) => {
363
+ const [token0UsdTvl, token1UsdTvl] = usdTvls.slice(i * 2, i * 2 + 2);
364
+ const [connector, thisLiquidityUSD, connectorLiquidityUsd] = token0Tvl.address === tokenAddress
365
+ ? [token1Tvl, token0UsdTvl, token1UsdTvl]
366
+ : [token0Tvl, token1UsdTvl, token0UsdTvl];
367
+ return {
368
+ exchange: this.dexKey,
369
+ address: this.config.core,
370
+ connectorTokens: [
371
+ {
372
+ address: connector.address,
373
+ decimals: connector.decimals,
374
+ liquidityUSD: connectorLiquidityUsd,
375
+ },
376
+ ],
377
+ liquidityUSD: thisLiquidityUSD,
378
+ };
357
379
  });
380
+ poolLiquidities
381
+ .sort((a, b) => b.liquidityUSD - a.liquidityUSD)
382
+ .splice(limit, Infinity);
383
+ return poolLiquidities;
358
384
  }
359
- initializePools(poolKeys, blockNumber) {
360
- const promises = [];
361
- const [normalPoolKeys, twammPoolKeys] = poolKeys.reduce(([normalPoolKeys, twammPoolKeys], poolKey) => {
362
- if (poolKey.config.extension == BigInt(this.config.twamm)) {
363
- twammPoolKeys.push(poolKey);
364
- }
365
- else {
366
- normalPoolKeys.push(poolKey);
367
- }
368
- return [normalPoolKeys, twammPoolKeys];
369
- }, [[], []]);
370
- const commonArgs = [
371
- this.dexKey,
372
- this.dexHelper,
373
- this.logger,
374
- this.contracts,
375
- ];
376
- function constructAndInitialize(constructor, initialState, poolKey) {
377
- const pool = new constructor(...commonArgs, poolKey);
378
- // This is fulfilled immediately
379
- pool.initialize(blockNumber, { state: initialState });
380
- return pool;
381
- }
382
- for (let batchStart = 0; batchStart < normalPoolKeys.length; batchStart += MAX_BATCH_SIZE) {
383
- const batch = normalPoolKeys.slice(batchStart, batchStart + MAX_BATCH_SIZE);
384
- promises.push((async () => {
385
- const fetchedData = await this.contracts.core.dataFetcher.getQuoteData(batch.map(poolKey => poolKey.toAbi()), MIN_TICK_SPACINGS_PER_POOL, {
386
- blockTag: blockNumber,
387
- });
388
- return fetchedData.map((data, i) => {
389
- const poolKey = normalPoolKeys[batchStart + i];
390
- const extension = poolKey.config.extension;
391
- let pool;
392
- switch (extension) {
393
- case 0n: {
394
- if (poolKey.config.tickSpacing === 0) {
395
- pool = constructAndInitialize(full_range_1.FullRangePool, full_range_1.FullRangePoolState.fromQuoter(data), poolKey);
396
- }
397
- else {
398
- pool = constructAndInitialize(base_1.BasePool, base_1.BasePoolState.fromQuoter(data), poolKey);
399
- }
400
- break;
401
- }
402
- case BigInt(this.config.oracle): {
403
- pool = constructAndInitialize(oracle_1.OraclePool, full_range_1.FullRangePoolState.fromQuoter(data), poolKey);
404
- break;
405
- }
406
- default:
407
- throw new Error(`Unknown pool extension ${(0, utils_3.hexlify)(extension)}`);
408
- }
409
- return pool;
410
- });
411
- })().catch(err => {
412
- throw {
413
- batch,
414
- err,
415
- };
416
- }));
385
+ releaseResources() {
386
+ if (this.interval) {
387
+ clearInterval(this.interval);
388
+ this.interval = undefined;
417
389
  }
418
- promises.push(...twammPoolKeys.map(poolKey => (async () => {
419
- const quoteData = await this.contracts.twamm.dataFetcher.getPoolState(poolKey.toAbi(), {
420
- blockTag: blockNumber,
421
- });
422
- return [
423
- constructAndInitialize(twamm_1.TwammPool, twamm_1.TwammPoolState.fromQuoter(quoteData), poolKey),
424
- ];
425
- })()));
426
- return promises.map(promise => promise.then(pools => pools.map(pool => {
427
- const poolId = pool.key.string_id;
428
- this.pools.set(poolId, pool);
429
- return poolId;
430
- })));
431
390
  }
432
391
  async fetchAllPoolKeys() {
433
392
  const res = await this.dexHelper.httpRequest.get(`${this.config.apiUrl}/v1/poolKeys`);
@@ -442,7 +401,96 @@ class Ekubo extends simple_exchange_1.SimpleExchange {
442
401
  .filter(res => this.supportedExtensions.includes(BigInt(res.extension)) &&
443
402
  BigInt(res.core_address) ===
444
403
  BigInt(this.contracts.core.contract.address))
445
- .map(info => new utils_4.PoolKey(BigInt(info.token0), BigInt(info.token1), new utils_4.PoolConfig(info.tick_spacing, BigInt(info.fee), BigInt(info.extension))));
404
+ .map(info => new utils_4.PoolKey(BigInt(info.token0), BigInt(info.token1), new utils_4.PoolConfig(BigInt(info.extension), BigInt(info.fee), info.tick_spacing)));
405
+ }
406
+ getDecimals(erc20Token) {
407
+ const cached = this.decimals[erc20Token];
408
+ if (typeof cached !== 'undefined') {
409
+ return cached;
410
+ }
411
+ const call = new ethers_1.Contract(erc20Token, utils_5.erc20Iface, this.dexHelper.provider).decimals();
412
+ const promise = call.catch((err) => {
413
+ this.logger.error('Failed to fetch decimals for token', erc20Token, 'due to:', err);
414
+ return null;
415
+ });
416
+ this.decimals[erc20Token] = promise;
417
+ return promise;
418
+ }
419
+ async getPools(tokenA, tokenB, blockNumber, limitPools) {
420
+ const [token0, token1] = (0, utils_2.convertAndSortTokens)(tokenA, tokenB);
421
+ let unfilteredPools;
422
+ if (typeof limitPools === 'undefined') {
423
+ unfilteredPools = this.pools.values();
424
+ }
425
+ else {
426
+ const unfilteredPoolsArr = [];
427
+ await Promise.all(limitPools.map(async (stringId) => {
428
+ let pool = this.pools.get(stringId);
429
+ if (typeof pool === 'undefined') {
430
+ try {
431
+ pool = await this.initializeUntrackedPool(stringId, blockNumber);
432
+ }
433
+ catch (err) {
434
+ this.logger.error(`Initializing pool ${stringId} failed: ${err}`);
435
+ return;
436
+ }
437
+ }
438
+ unfilteredPoolsArr.push(pool);
439
+ }));
440
+ unfilteredPools = Iterator.from(unfilteredPoolsArr);
441
+ }
442
+ return unfilteredPools.filter(pool => pool.key.token0 === token0 && pool.key.token1 === token1);
443
+ }
444
+ async initializeUntrackedPool(stringId, blockNumber) {
445
+ const poolKey = utils_4.PoolKey.fromStringId(stringId);
446
+ let constructor;
447
+ const extension = poolKey.config.extension;
448
+ switch (extension) {
449
+ case 0n: {
450
+ if (poolKey.config.tickSpacing === 0) {
451
+ constructor = full_range_1.FullRangePool;
452
+ }
453
+ else {
454
+ constructor = base_1.BasePool;
455
+ }
456
+ break;
457
+ }
458
+ case BigInt(this.config.oracle): {
459
+ constructor = oracle_1.OraclePool;
460
+ break;
461
+ }
462
+ case BigInt(this.config.twamm): {
463
+ constructor = twamm_1.TwammPool;
464
+ break;
465
+ }
466
+ case BigInt(this.config.mevResist): {
467
+ constructor = mev_resist_1.MevResistPool;
468
+ break;
469
+ }
470
+ default: {
471
+ throw new Error(`Unknown pool extension ${(0, utils_3.hexZeroPad)((0, utils_3.hexlify)(extension), 20)}`);
472
+ }
473
+ }
474
+ const pool = new constructor(this.dexKey, this.dexHelper, this.logger, this.contracts, poolKey);
475
+ await pool.initialize(blockNumber);
476
+ this.pools.set(stringId, pool);
477
+ return pool;
478
+ }
479
+ // LEGACY
480
+ getAdapters(_side) {
481
+ return null;
482
+ }
483
+ // LEGACY
484
+ getCalldataGasCost(_poolPrices) {
485
+ return CALLDATA_GAS_COST.DEX_NO_PAYLOAD;
486
+ }
487
+ // LEGACY
488
+ getAdapterParam(_srcToken, _destToken, _srcAmount, _destAmount, _data, _side) {
489
+ return {
490
+ targetExchange: this.dexKey,
491
+ payload: '',
492
+ networkFee: '0',
493
+ };
446
494
  }
447
495
  }
448
496
  exports.Ekubo = Ekubo;