@curvefi/api 1.21.0 → 1.24.0

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/lib/pools.js CHANGED
@@ -59,11 +59,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
59
59
  return (mod && mod.__esModule) ? mod : { "default": mod };
60
60
  };
61
61
  Object.defineProperty(exports, "__esModule", { value: true });
62
- exports.getUserPoolList = exports.crossAssetExchange = exports.crossAssetExchangeEstimateGas = exports.crossAssetExchangeApprove = exports.crossAssetExchangeApproveEstimateGas = exports.crossAssetExchangeIsApproved = exports.crossAssetExchangeExpected = exports.crossAssetExchangeOutputAndSlippage = exports._crossAssetExchangeInfo = exports._getSmallAmountForCoin = exports.crossAssetExchangeAvailable = exports.exchange = exports.exchangeEstimateGas = exports.exchangeApprove = exports.exchangeApproveEstimateGas = exports.exchangeIsApproved = exports.exchangeExpected = exports.getBestPoolAndOutput = exports.Pool = void 0;
62
+ exports.routerExchange = exports.routerExchangeEstimateGas = exports.routerExchangeApprove = exports.routerExchangeApproveEstimateGas = exports.routerExchangeIsApproved = exports.routerExchangeExpected = exports.getBestRouteAndOutput = exports._findAllRoutes = exports.getUserPoolList = exports.crossAssetExchange = exports.crossAssetExchangeEstimateGas = exports.crossAssetExchangeApprove = exports.crossAssetExchangeApproveEstimateGas = exports.crossAssetExchangeIsApproved = exports.crossAssetExchangeExpected = exports.crossAssetExchangeOutputAndSlippage = exports._crossAssetExchangeInfo = exports._getSmallAmountForCoin = exports.crossAssetExchangeAvailable = exports.exchange = exports.exchangeEstimateGas = exports.exchangeApprove = exports.exchangeApproveEstimateGas = exports.exchangeIsApproved = exports.exchangeExpected = exports.getBestPoolAndOutput = exports.Pool = void 0;
63
+ var axios_1 = __importDefault(require("axios"));
63
64
  var ethers_1 = require("ethers");
65
+ var memoizee_1 = __importDefault(require("memoizee"));
66
+ var external_api_1 = require("./external-api");
64
67
  var utils_1 = require("./utils");
65
68
  var curve_1 = require("./curve");
66
- var axios_1 = __importDefault(require("axios"));
67
69
  var Pool = /** @class */ (function () {
68
70
  function Pool(id) {
69
71
  var _this = this;
@@ -224,26 +226,48 @@ var Pool = /** @class */ (function () {
224
226
  }
225
227
  });
226
228
  }); };
227
- this.getTotalLiquidity = function () { return __awaiter(_this, void 0, void 0, function () {
228
- var balances, promises, _i, _a, addr, prices, totalLiquidity;
229
- return __generator(this, function (_c) {
230
- switch (_c.label) {
231
- case 0: return [4 /*yield*/, this.getPoolBalances()];
232
- case 1:
233
- balances = _c.sent();
234
- promises = [];
235
- for (_i = 0, _a = this.underlyingCoinAddresses; _i < _a.length; _i++) {
236
- addr = _a[_i];
237
- promises.push((0, utils_1._getUsdRate)(addr));
238
- }
239
- return [4 /*yield*/, Promise.all(promises)];
240
- case 2:
241
- prices = _c.sent();
242
- totalLiquidity = balances.reduce(function (liquidity, b, i) { return liquidity + (Number(b) * prices[i]); }, 0);
243
- return [2 /*return*/, totalLiquidity.toFixed(8)];
244
- }
229
+ this.getTotalLiquidity = function (useApi) {
230
+ if (useApi === void 0) { useApi = true; }
231
+ return __awaiter(_this, void 0, void 0, function () {
232
+ var network, poolType, poolsData, totalLiquidity_1, balances, promises, _i, _a, addr, prices, totalLiquidity;
233
+ var _this = this;
234
+ return __generator(this, function (_c) {
235
+ switch (_c.label) {
236
+ case 0:
237
+ if (!useApi) return [3 /*break*/, 2];
238
+ network = curve_1.curve.chainId === 137 ? "polygon" : "ethereum";
239
+ poolType = !this.isFactory && !this.isCrypto ? "main" :
240
+ !this.isFactory ? "crypto" :
241
+ !this.isCryptoFactory ? "factory" :
242
+ "factory-crypto";
243
+ return [4 /*yield*/, (0, external_api_1._getPoolsFromApi)(network, poolType)];
244
+ case 1:
245
+ poolsData = (_c.sent()).poolData;
246
+ try {
247
+ totalLiquidity_1 = poolsData.filter(function (data) { return data.address.toLowerCase() === _this.swap.toLowerCase(); })[0].usdTotal;
248
+ return [2 /*return*/, String(totalLiquidity_1)];
249
+ }
250
+ catch (err) {
251
+ console.log(err.message);
252
+ }
253
+ _c.label = 2;
254
+ case 2: return [4 /*yield*/, this.getPoolBalances()];
255
+ case 3:
256
+ balances = _c.sent();
257
+ promises = [];
258
+ for (_i = 0, _a = this.underlyingCoinAddresses; _i < _a.length; _i++) {
259
+ addr = _a[_i];
260
+ promises.push((0, utils_1._getUsdRate)(addr));
261
+ }
262
+ return [4 /*yield*/, Promise.all(promises)];
263
+ case 4:
264
+ prices = _c.sent();
265
+ totalLiquidity = balances.reduce(function (liquidity, b, i) { return liquidity + (Number(b) * prices[i]); }, 0);
266
+ return [2 /*return*/, totalLiquidity.toFixed(8)];
267
+ }
268
+ });
245
269
  });
246
- }); };
270
+ };
247
271
  this._getPoolStats = function () { return __awaiter(_this, void 0, void 0, function () {
248
272
  var statsUrl, name, key;
249
273
  return __generator(this, function (_a) {
@@ -273,7 +297,7 @@ var Pool = /** @class */ (function () {
273
297
  if (!(this.isCrypto || (curve_1.curve.chainId === 1 && this.isFactory))) return [3 /*break*/, 2];
274
298
  _a = 1;
275
299
  return [3 /*break*/, 4];
276
- case 2: return [4 /*yield*/, (0, utils_1._getUsdRate)(this.referenceAsset)];
300
+ case 2: return [4 /*yield*/, (0, utils_1._getUsdRate)(this.coinAddresses[0])];
277
301
  case 3:
278
302
  _a = _c.sent();
279
303
  _c.label = 4;
@@ -4157,3 +4181,560 @@ var getUserPoolList = function (address) { return __awaiter(void 0, void 0, void
4157
4181
  });
4158
4182
  }); };
4159
4183
  exports.getUserPoolList = getUserPoolList;
4184
+ // --------- Multi-Pool Exchange ---------
4185
+ // TODO make working or remove
4186
+ var IMBALANCED_POOLS = [];
4187
+ // Inspired by Dijkstra's algorithm
4188
+ var _findAllRoutes = function (inputCoinAddress, outputCoinAddress) { return __awaiter(void 0, void 0, void 0, function () {
4189
+ var ALL_POOLS, basePoolsSet, _i, ALL_POOLS_1, pool, basePoolIds, markedCoins, curCoins, nextCoins, routes, step, _a, curCoins_1, inCoin, _c, ALL_POOLS_2, _d, poolId, poolData, coin_addresses, underlying_coin_addresses, meta_coin_addresses, token_address, is_lending, inCoinIndexes, j, swapType, _e, _f, inCoinRoute, swapType, _h, _j, inCoinRoute, j, tvl, _k, swapType, _l, _m, inCoinRoute, poolAddress, j, tvl, _o, swapType, _p, _q, inCoinRoute, j, tvl, _r, swapType, _s, _t, inCoinRoute, tvl, _u, swapType, _v, _w, inCoinRoute;
4190
+ var _x;
4191
+ var _y, _z, _0, _1, _2, _3, _4, _5;
4192
+ return __generator(this, function (_6) {
4193
+ switch (_6.label) {
4194
+ case 0:
4195
+ inputCoinAddress = inputCoinAddress.toLowerCase();
4196
+ outputCoinAddress = outputCoinAddress.toLowerCase();
4197
+ ALL_POOLS = Object.entries(__assign(__assign(__assign({}, curve_1.POOLS_DATA), curve_1.curve.constants.FACTORY_POOLS_DATA), curve_1.curve.constants.CRYPTO_FACTORY_POOLS_DATA));
4198
+ basePoolsSet = new Set();
4199
+ for (_i = 0, ALL_POOLS_1 = ALL_POOLS; _i < ALL_POOLS_1.length; _i++) {
4200
+ pool = ALL_POOLS_1[_i];
4201
+ if (pool[1].base_pool)
4202
+ basePoolsSet.add(pool[1].base_pool);
4203
+ }
4204
+ basePoolIds = Array.from(basePoolsSet);
4205
+ markedCoins = [];
4206
+ curCoins = [inputCoinAddress];
4207
+ nextCoins = new Set();
4208
+ routes = (_x = {},
4209
+ _x[inputCoinAddress] = [[]],
4210
+ _x);
4211
+ step = 0;
4212
+ _6.label = 1;
4213
+ case 1:
4214
+ if (!(step < 4)) return [3 /*break*/, 21];
4215
+ _a = 0, curCoins_1 = curCoins;
4216
+ _6.label = 2;
4217
+ case 2:
4218
+ if (!(_a < curCoins_1.length)) return [3 /*break*/, 19];
4219
+ inCoin = curCoins_1[_a];
4220
+ _c = 0, ALL_POOLS_2 = ALL_POOLS;
4221
+ _6.label = 3;
4222
+ case 3:
4223
+ if (!(_c < ALL_POOLS_2.length)) return [3 /*break*/, 18];
4224
+ _d = ALL_POOLS_2[_c], poolId = _d[0], poolData = _d[1];
4225
+ coin_addresses = poolData.coin_addresses.map(function (a) { return a.toLowerCase(); });
4226
+ underlying_coin_addresses = poolData.underlying_coin_addresses.map(function (a) { return a.toLowerCase(); });
4227
+ meta_coin_addresses = ((_y = poolData.meta_coin_addresses) === null || _y === void 0 ? void 0 : _y.map(function (a) { return a.toLowerCase(); })) || [];
4228
+ token_address = poolData.token_address.toLowerCase();
4229
+ is_lending = poolData.use_lending.includes(true);
4230
+ inCoinIndexes = {
4231
+ coin: coin_addresses.indexOf(inCoin),
4232
+ underlying_coin: underlying_coin_addresses.indexOf(inCoin),
4233
+ meta_coin: meta_coin_addresses ? meta_coin_addresses.indexOf(inCoin) : -1,
4234
+ };
4235
+ // Find all LP -> underlying coin "swaps" (actually remove_liquidity_one_coin)
4236
+ if (basePoolIds.includes(poolId) && inCoin === token_address) {
4237
+ for (j = 0; j < underlying_coin_addresses.length; j++) {
4238
+ // If this coin already marked or will be marked on the current step, no need to consider it on the next step
4239
+ if (markedCoins.includes(underlying_coin_addresses[j]) || curCoins.includes(underlying_coin_addresses[j]))
4240
+ continue;
4241
+ // Looking for outputCoinAddress only on the final step
4242
+ if (step === 3 && underlying_coin_addresses[j] !== outputCoinAddress)
4243
+ continue;
4244
+ swapType = poolId === 'aave' ? 10 : 9;
4245
+ for (_e = 0, _f = routes[inCoin]; _e < _f.length; _e++) {
4246
+ inCoinRoute = _f[_e];
4247
+ routes[underlying_coin_addresses[j]] = ((_z = routes[underlying_coin_addresses[j]]) !== null && _z !== void 0 ? _z : []).concat([__spreadArray(__spreadArray([], inCoinRoute, true), [
4248
+ {
4249
+ poolId: poolId,
4250
+ poolAddress: poolData.swap_address,
4251
+ outputCoinAddress: underlying_coin_addresses[j],
4252
+ i: 0,
4253
+ j: j,
4254
+ swapType: swapType,
4255
+ swapAddress: ethers_1.ethers.constants.AddressZero,
4256
+ },
4257
+ ], false)]);
4258
+ }
4259
+ nextCoins.add(underlying_coin_addresses[j]);
4260
+ }
4261
+ }
4262
+ // Find all underlying coin -> LP "swaps" (actually add_liquidity)
4263
+ if (basePoolIds.includes(poolId) && underlying_coin_addresses.includes(inCoin)) {
4264
+ // If this coin already marked or will be marked on the current step, no need to consider it on the next step
4265
+ if (markedCoins.includes(token_address) || curCoins.includes(token_address))
4266
+ return [3 /*break*/, 17];
4267
+ // Looking for outputCoinAddress only on the final step
4268
+ if (step === 3 && token_address !== outputCoinAddress)
4269
+ return [3 /*break*/, 17];
4270
+ swapType = is_lending ? 8 : underlying_coin_addresses.length === 2 ? 6 : 7;
4271
+ for (_h = 0, _j = routes[inCoin]; _h < _j.length; _h++) {
4272
+ inCoinRoute = _j[_h];
4273
+ routes[token_address] = ((_0 = routes[token_address]) !== null && _0 !== void 0 ? _0 : []).concat([__spreadArray(__spreadArray([], inCoinRoute, true), [
4274
+ {
4275
+ poolId: poolId,
4276
+ poolAddress: poolData.swap_address,
4277
+ outputCoinAddress: token_address,
4278
+ i: underlying_coin_addresses.indexOf(inCoin),
4279
+ j: 0,
4280
+ swapType: swapType,
4281
+ swapAddress: ethers_1.ethers.constants.AddressZero,
4282
+ },
4283
+ ], false)]);
4284
+ }
4285
+ nextCoins.add(token_address);
4286
+ }
4287
+ // No input coin in this pool --> skip
4288
+ if (inCoinIndexes.coin === -1 && inCoinIndexes.underlying_coin === -1 && inCoinIndexes.meta_coin === -1)
4289
+ return [3 /*break*/, 17];
4290
+ if (!(inCoinIndexes.coin >= 0 && poolId !== "atricrypto3")) return [3 /*break*/, 7];
4291
+ j = 0;
4292
+ _6.label = 4;
4293
+ case 4:
4294
+ if (!(j < coin_addresses.length)) return [3 /*break*/, 7];
4295
+ // If this coin already marked or will be marked on the current step, no need to consider it on the next step
4296
+ if (markedCoins.includes(coin_addresses[j]) || curCoins.includes(coin_addresses[j]))
4297
+ return [3 /*break*/, 6];
4298
+ // Looking for outputCoinAddress only on the final step
4299
+ if (step === 3 && coin_addresses[j] !== outputCoinAddress)
4300
+ return [3 /*break*/, 6];
4301
+ _k = Number;
4302
+ return [4 /*yield*/, (new Pool(poolId)).stats.getTotalLiquidity()];
4303
+ case 5:
4304
+ tvl = _k.apply(void 0, [_6.sent()]);
4305
+ if (tvl === 0)
4306
+ return [3 /*break*/, 6];
4307
+ // Skip imbalanced pools
4308
+ if (IMBALANCED_POOLS.includes(poolId))
4309
+ return [3 /*break*/, 6];
4310
+ swapType = poolData.is_crypto ? 3 : 1;
4311
+ for (_l = 0, _m = routes[inCoin]; _l < _m.length; _l++) {
4312
+ inCoinRoute = _m[_l];
4313
+ routes[coin_addresses[j]] = ((_1 = routes[coin_addresses[j]]) !== null && _1 !== void 0 ? _1 : []).concat([__spreadArray(__spreadArray([], inCoinRoute, true), [
4314
+ {
4315
+ poolId: poolId,
4316
+ poolAddress: poolData.swap_address,
4317
+ outputCoinAddress: coin_addresses[j],
4318
+ i: inCoinIndexes.coin,
4319
+ j: j,
4320
+ swapType: swapType,
4321
+ swapAddress: ethers_1.ethers.constants.AddressZero,
4322
+ },
4323
+ ], false)]);
4324
+ }
4325
+ nextCoins.add(coin_addresses[j]);
4326
+ _6.label = 6;
4327
+ case 6:
4328
+ j++;
4329
+ return [3 /*break*/, 4];
4330
+ case 7:
4331
+ poolAddress = ["eurtusd", "xautusd", "atricrypto3"].includes(poolId) ||
4332
+ (curve_1.curve.chainId === 137 && poolData.is_factory) ? poolData.deposit_address : poolData.swap_address;
4333
+ if (!(coin_addresses.join("|") !== underlying_coin_addresses.join("|") && inCoinIndexes.underlying_coin >= 0)) return [3 /*break*/, 11];
4334
+ j = 0;
4335
+ _6.label = 8;
4336
+ case 8:
4337
+ if (!(j < underlying_coin_addresses.length)) return [3 /*break*/, 11];
4338
+ if (poolId === "atricrypto3" && inCoinIndexes.meta_coin >= 0 && meta_coin_addresses.includes(underlying_coin_addresses[j]))
4339
+ return [3 /*break*/, 10];
4340
+ // If this coin already marked or will be marked on the current step, no need to consider it on the next step
4341
+ if (markedCoins.includes(underlying_coin_addresses[j]) || curCoins.includes(underlying_coin_addresses[j]))
4342
+ return [3 /*break*/, 10];
4343
+ // Looking for outputCoinAddress only on the final step
4344
+ if (step === 3 && underlying_coin_addresses[j] !== outputCoinAddress)
4345
+ return [3 /*break*/, 10];
4346
+ _o = Number;
4347
+ return [4 /*yield*/, (new Pool(poolId)).stats.getTotalLiquidity()];
4348
+ case 9:
4349
+ tvl = _o.apply(void 0, [_6.sent()]);
4350
+ if (tvl === 0)
4351
+ return [3 /*break*/, 10];
4352
+ // Skip imbalanced pools
4353
+ if (IMBALANCED_POOLS.includes(poolId))
4354
+ return [3 /*break*/, 10];
4355
+ swapType = poolData.is_crypto && (poolData.is_fake || poolData.is_meta) ? 4 : poolData.is_crypto ? 3 : 2;
4356
+ for (_p = 0, _q = routes[inCoin]; _p < _q.length; _p++) {
4357
+ inCoinRoute = _q[_p];
4358
+ routes[underlying_coin_addresses[j]] = ((_2 = routes[underlying_coin_addresses[j]]) !== null && _2 !== void 0 ? _2 : []).concat([__spreadArray(__spreadArray([], inCoinRoute, true), [
4359
+ {
4360
+ poolId: poolId,
4361
+ poolAddress: poolAddress,
4362
+ outputCoinAddress: underlying_coin_addresses[j],
4363
+ i: inCoinIndexes.underlying_coin,
4364
+ j: j,
4365
+ swapType: swapType,
4366
+ swapAddress: ethers_1.ethers.constants.AddressZero,
4367
+ },
4368
+ ], false)]);
4369
+ }
4370
+ nextCoins.add(underlying_coin_addresses[j]);
4371
+ _6.label = 10;
4372
+ case 10:
4373
+ j++;
4374
+ return [3 /*break*/, 8];
4375
+ case 11:
4376
+ if (!(inCoinIndexes.coin === 0 && meta_coin_addresses.length > 0 && poolId !== "atricrypto3")) return [3 /*break*/, 15];
4377
+ j = 0;
4378
+ _6.label = 12;
4379
+ case 12:
4380
+ if (!(j < meta_coin_addresses.length)) return [3 /*break*/, 15];
4381
+ // If this coin already marked or will be marked on the current step, no need to consider it on the next step
4382
+ if (markedCoins.includes(meta_coin_addresses[j]) || curCoins.includes(meta_coin_addresses[j]))
4383
+ return [3 /*break*/, 14];
4384
+ // Looking for outputCoinAddress only on the final step
4385
+ if (step === 3 && meta_coin_addresses[j] !== outputCoinAddress)
4386
+ return [3 /*break*/, 14];
4387
+ _r = Number;
4388
+ return [4 /*yield*/, (new Pool(poolId)).stats.getTotalLiquidity()];
4389
+ case 13:
4390
+ tvl = _r.apply(void 0, [_6.sent()]);
4391
+ if (tvl === 0)
4392
+ return [3 /*break*/, 14];
4393
+ // Skip imbalanced pools
4394
+ if (IMBALANCED_POOLS.includes(poolId))
4395
+ return [3 /*break*/, 14];
4396
+ swapType = (curve_1.curve.chainId === 137 && poolData.is_factory) ? 5 : poolData.is_crypto ? 4 : 2;
4397
+ for (_s = 0, _t = routes[inCoin]; _s < _t.length; _s++) {
4398
+ inCoinRoute = _t[_s];
4399
+ routes[meta_coin_addresses[j]] = ((_3 = routes[meta_coin_addresses[j]]) !== null && _3 !== void 0 ? _3 : []).concat([__spreadArray(__spreadArray([], inCoinRoute, true), [
4400
+ {
4401
+ poolId: poolId,
4402
+ poolAddress: poolAddress,
4403
+ outputCoinAddress: meta_coin_addresses[j],
4404
+ i: inCoinIndexes.coin,
4405
+ j: j + 1,
4406
+ swapType: swapType,
4407
+ swapAddress: swapType === 5 ? poolData.swap_address : ethers_1.ethers.constants.AddressZero,
4408
+ },
4409
+ ], false)]);
4410
+ }
4411
+ nextCoins.add(meta_coin_addresses[j]);
4412
+ _6.label = 14;
4413
+ case 14:
4414
+ j++;
4415
+ return [3 /*break*/, 12];
4416
+ case 15:
4417
+ if (!(inCoinIndexes.meta_coin >= 0 && poolId !== "atricrypto3")) return [3 /*break*/, 17];
4418
+ // If this coin already marked or will be marked on the current step, no need to consider it on the next step
4419
+ if (markedCoins.includes(coin_addresses[0]) || curCoins.includes(coin_addresses[0]))
4420
+ return [3 /*break*/, 17];
4421
+ // Looking for outputCoinAddress only on the final step
4422
+ if (step === 3 && coin_addresses[0] !== outputCoinAddress)
4423
+ return [3 /*break*/, 17];
4424
+ _u = Number;
4425
+ return [4 /*yield*/, (new Pool(poolId)).stats.getTotalLiquidity()];
4426
+ case 16:
4427
+ tvl = _u.apply(void 0, [_6.sent()]);
4428
+ if (tvl === 0)
4429
+ return [3 /*break*/, 17];
4430
+ // Skip imbalanced pools
4431
+ if (IMBALANCED_POOLS.includes(poolId))
4432
+ return [3 /*break*/, 17];
4433
+ swapType = (curve_1.curve.chainId === 137 && poolData.is_factory) ? 5 : poolData.is_crypto ? 4 : 2;
4434
+ for (_v = 0, _w = routes[inCoin]; _v < _w.length; _v++) {
4435
+ inCoinRoute = _w[_v];
4436
+ routes[coin_addresses[0]] = ((_4 = routes[coin_addresses[0]]) !== null && _4 !== void 0 ? _4 : []).concat([__spreadArray(__spreadArray([], inCoinRoute, true), [
4437
+ {
4438
+ poolId: poolId,
4439
+ poolAddress: poolAddress,
4440
+ outputCoinAddress: coin_addresses[0],
4441
+ i: inCoinIndexes.meta_coin + 1,
4442
+ j: 0,
4443
+ swapType: swapType,
4444
+ swapAddress: swapType === 5 ? poolData.swap_address : ethers_1.ethers.constants.AddressZero,
4445
+ },
4446
+ ], false)]);
4447
+ nextCoins.add(coin_addresses[0]);
4448
+ }
4449
+ _6.label = 17;
4450
+ case 17:
4451
+ _c++;
4452
+ return [3 /*break*/, 3];
4453
+ case 18:
4454
+ _a++;
4455
+ return [3 /*break*/, 2];
4456
+ case 19:
4457
+ // If target output coin is reached, search is finished. Assumption: the shorter route, the better.
4458
+ if (outputCoinAddress in routes)
4459
+ return [3 /*break*/, 21];
4460
+ markedCoins.push.apply(markedCoins, curCoins);
4461
+ curCoins = Array.from(nextCoins);
4462
+ nextCoins = new Set();
4463
+ _6.label = 20;
4464
+ case 20:
4465
+ step++;
4466
+ return [3 /*break*/, 1];
4467
+ case 21: return [2 /*return*/, (_5 = routes[outputCoinAddress]) !== null && _5 !== void 0 ? _5 : []];
4468
+ }
4469
+ });
4470
+ }); };
4471
+ exports._findAllRoutes = _findAllRoutes;
4472
+ var _getRouteKey = function (route, inputCoinAddress, outputCoinAddress) {
4473
+ var sortedCoins = [inputCoinAddress, outputCoinAddress].sort();
4474
+ var key = "".concat(sortedCoins[0], "-->");
4475
+ for (var _i = 0, _a = route.steps; _i < _a.length; _i++) {
4476
+ var routeStep = _a[_i];
4477
+ key += "".concat(routeStep.poolId, "-->");
4478
+ }
4479
+ key += sortedCoins[1];
4480
+ return key;
4481
+ };
4482
+ var _getExchangeMultipleArgs = function (inputCoinAddress, route) {
4483
+ var _route = [inputCoinAddress];
4484
+ var _swapParams = [];
4485
+ var _factorySwapAddresses = [];
4486
+ for (var _i = 0, _a = route.steps; _i < _a.length; _i++) {
4487
+ var routeStep = _a[_i];
4488
+ _route.push(routeStep.poolAddress, routeStep.outputCoinAddress);
4489
+ _swapParams.push([routeStep.i, routeStep.j, routeStep.swapType]);
4490
+ _factorySwapAddresses.push(routeStep.swapAddress);
4491
+ }
4492
+ _route = _route.concat(Array(9 - _route.length).fill(ethers_1.ethers.constants.AddressZero));
4493
+ _swapParams = _swapParams.concat(Array(4 - _swapParams.length).fill([0, 0, 0]));
4494
+ _factorySwapAddresses = _factorySwapAddresses.concat(Array(4 - _factorySwapAddresses.length).fill(ethers_1.ethers.constants.AddressZero));
4495
+ return { _route: _route, _swapParams: _swapParams, _factorySwapAddresses: _factorySwapAddresses };
4496
+ };
4497
+ var _estimatedGasForDifferentRoutesCache = {};
4498
+ var _estimateGasForDifferentRoutes = function (routes, inputCoinAddress, outputCoinAddress, _amount) { return __awaiter(void 0, void 0, void 0, function () {
4499
+ var contract, gasPromises, value, _i, routes_1, route, routeKey, gasPromise, _a, _route, _swapParams, _factorySwapAddresses, _gasAmounts_2, err_3;
4500
+ var _c;
4501
+ return __generator(this, function (_d) {
4502
+ switch (_d.label) {
4503
+ case 0:
4504
+ inputCoinAddress = inputCoinAddress.toLowerCase();
4505
+ outputCoinAddress = outputCoinAddress.toLowerCase();
4506
+ contract = curve_1.curve.contracts[curve_1.ALIASES.registry_exchange].contract;
4507
+ gasPromises = [];
4508
+ value = (0, utils_1.isEth)(inputCoinAddress) ? _amount : ethers_1.ethers.BigNumber.from(0);
4509
+ for (_i = 0, routes_1 = routes; _i < routes_1.length; _i++) {
4510
+ route = routes_1[_i];
4511
+ routeKey = _getRouteKey(route, inputCoinAddress, outputCoinAddress);
4512
+ gasPromise = void 0;
4513
+ _a = _getExchangeMultipleArgs(inputCoinAddress, route), _route = _a._route, _swapParams = _a._swapParams, _factorySwapAddresses = _a._factorySwapAddresses;
4514
+ if ((((_c = _estimatedGasForDifferentRoutesCache[routeKey]) === null || _c === void 0 ? void 0 : _c.time) || 0) + 3600000 < Date.now()) {
4515
+ gasPromise = contract.estimateGas.exchange_multiple(_route, _swapParams, _amount, 0, _factorySwapAddresses, __assign(__assign({}, curve_1.curve.constantOptions), { value: value }));
4516
+ }
4517
+ else {
4518
+ gasPromise = Promise.resolve(_estimatedGasForDifferentRoutesCache[routeKey].gas);
4519
+ }
4520
+ gasPromises.push(gasPromise);
4521
+ }
4522
+ _d.label = 1;
4523
+ case 1:
4524
+ _d.trys.push([1, 3, , 4]);
4525
+ return [4 /*yield*/, Promise.all(gasPromises)];
4526
+ case 2:
4527
+ _gasAmounts_2 = _d.sent();
4528
+ routes.forEach(function (route, i) {
4529
+ var routeKey = _getRouteKey(route, inputCoinAddress, outputCoinAddress);
4530
+ _estimatedGasForDifferentRoutesCache[routeKey] = { 'gas': _gasAmounts_2[i], 'time': Date.now() };
4531
+ });
4532
+ return [2 /*return*/, _gasAmounts_2.map(function (_g) { return Number(ethers_1.ethers.utils.formatUnits(_g, 0)); })];
4533
+ case 3:
4534
+ err_3 = _d.sent();
4535
+ return [2 /*return*/, routes.map(function () { return 0; })];
4536
+ case 4: return [2 /*return*/];
4537
+ }
4538
+ });
4539
+ }); };
4540
+ var _getBestRouteAndOutput = (0, memoizee_1.default)(function (inputCoinAddress, outputCoinAddress, amount) { return __awaiter(void 0, void 0, void 0, function () {
4541
+ var _a, inputCoinDecimals, outputCoinDecimals, _amount, routesRaw, routes, calls, promises, multicallContract, contract, _i, routesRaw_1, route, _c, _route, _swapParams, _factorySwapAddresses, calls_1, multicallContract_1, _d, routesRaw_2, route, _e, _route, _swapParams, _factorySwapAddresses, _outputAmounts, i, err_4, promises_1, contract_1, _f, routesRaw_3, route, _h, _route, _swapParams, _factorySwapAddresses, res, i, _j, gasAmounts, outputCoinUsdRate, gasData, ethUsdRate, gasPrice, expectedAmounts, expectedAmountsUsd, txCostsUsd;
4542
+ return __generator(this, function (_k) {
4543
+ switch (_k.label) {
4544
+ case 0:
4545
+ _a = (0, utils_1._getCoinDecimals)(inputCoinAddress, outputCoinAddress), inputCoinDecimals = _a[0], outputCoinDecimals = _a[1];
4546
+ _amount = ethers_1.ethers.utils.parseUnits(amount.toString(), inputCoinDecimals);
4547
+ return [4 /*yield*/, (0, exports._findAllRoutes)(inputCoinAddress, outputCoinAddress)];
4548
+ case 1:
4549
+ routesRaw = (_k.sent()).map(function (steps) { return ({ steps: steps, _output: ethers_1.ethers.BigNumber.from(0), outputUsd: 0, txCostUsd: 0 }); });
4550
+ routes = [];
4551
+ calls = [];
4552
+ promises = [];
4553
+ multicallContract = curve_1.curve.contracts[curve_1.ALIASES.registry_exchange].multicallContract;
4554
+ contract = curve_1.curve.contracts[curve_1.ALIASES.registry_exchange].contract;
4555
+ for (_i = 0, routesRaw_1 = routesRaw; _i < routesRaw_1.length; _i++) {
4556
+ route = routesRaw_1[_i];
4557
+ _c = _getExchangeMultipleArgs(inputCoinAddress, route), _route = _c._route, _swapParams = _c._swapParams, _factorySwapAddresses = _c._factorySwapAddresses;
4558
+ calls.push(multicallContract.get_exchange_multiple_amount(_route, _swapParams, _amount, _factorySwapAddresses));
4559
+ promises.push(contract.get_exchange_multiple_amount(_route, _swapParams, _amount, _factorySwapAddresses, curve_1.curve.constantOptions));
4560
+ }
4561
+ _k.label = 2;
4562
+ case 2:
4563
+ _k.trys.push([2, 4, , 6]);
4564
+ calls_1 = [];
4565
+ multicallContract_1 = curve_1.curve.contracts[curve_1.ALIASES.registry_exchange].multicallContract;
4566
+ for (_d = 0, routesRaw_2 = routesRaw; _d < routesRaw_2.length; _d++) {
4567
+ route = routesRaw_2[_d];
4568
+ _e = _getExchangeMultipleArgs(inputCoinAddress, route), _route = _e._route, _swapParams = _e._swapParams, _factorySwapAddresses = _e._factorySwapAddresses;
4569
+ calls_1.push(multicallContract_1.get_exchange_multiple_amount(_route, _swapParams, _amount, _factorySwapAddresses));
4570
+ }
4571
+ return [4 /*yield*/, curve_1.curve.multicallProvider.all(calls_1)];
4572
+ case 3:
4573
+ _outputAmounts = _k.sent();
4574
+ for (i = 0; i < _outputAmounts.length; i++) {
4575
+ routesRaw[i]._output = _outputAmounts[i];
4576
+ routes.push(routesRaw[i]);
4577
+ }
4578
+ return [3 /*break*/, 6];
4579
+ case 4:
4580
+ err_4 = _k.sent();
4581
+ promises_1 = [];
4582
+ contract_1 = curve_1.curve.contracts[curve_1.ALIASES.registry_exchange].contract;
4583
+ for (_f = 0, routesRaw_3 = routesRaw; _f < routesRaw_3.length; _f++) {
4584
+ route = routesRaw_3[_f];
4585
+ _h = _getExchangeMultipleArgs(inputCoinAddress, route), _route = _h._route, _swapParams = _h._swapParams, _factorySwapAddresses = _h._factorySwapAddresses;
4586
+ promises_1.push(contract_1.get_exchange_multiple_amount(_route, _swapParams, _amount, _factorySwapAddresses, curve_1.curve.constantOptions));
4587
+ }
4588
+ return [4 /*yield*/, Promise.allSettled(promises_1)];
4589
+ case 5:
4590
+ res = _k.sent();
4591
+ for (i = 0; i < res.length; i++) {
4592
+ if (res[i].status === 'rejected') {
4593
+ console.log("Route ".concat((routesRaw[i].steps.map(function (s) { return s.poolId; })).join(" --> "), " is anavailable"));
4594
+ continue;
4595
+ }
4596
+ routesRaw[i]._output = res[i].value;
4597
+ routes.push(routesRaw[i]);
4598
+ }
4599
+ return [3 /*break*/, 6];
4600
+ case 6:
4601
+ if (routes.length === 0) {
4602
+ return [2 /*return*/, {
4603
+ steps: [],
4604
+ _output: ethers_1.ethers.BigNumber.from(0),
4605
+ outputUsd: 0,
4606
+ txCostUsd: 0,
4607
+ }];
4608
+ }
4609
+ if (routes.length === 1)
4610
+ return [2 /*return*/, routes[0]];
4611
+ return [4 /*yield*/, Promise.all([
4612
+ _estimateGasForDifferentRoutes(routes, inputCoinAddress, outputCoinAddress, _amount),
4613
+ (0, utils_1._getUsdRate)(outputCoinAddress),
4614
+ axios_1.default.get("https://api.curve.fi/api/getGas"),
4615
+ (0, utils_1._getUsdRate)(curve_1.curve.chainId === 137 ? curve_1.COINS.matic : curve_1.COINS.eth),
4616
+ ])];
4617
+ case 7:
4618
+ _j = _k.sent(), gasAmounts = _j[0], outputCoinUsdRate = _j[1], gasData = _j[2], ethUsdRate = _j[3];
4619
+ gasPrice = gasData.data.data.gas.standard;
4620
+ expectedAmounts = (routes).map(function (route) { return Number(ethers_1.ethers.utils.formatUnits(route._output, outputCoinDecimals)); });
4621
+ expectedAmountsUsd = expectedAmounts.map(function (a) { return a * outputCoinUsdRate; });
4622
+ txCostsUsd = gasAmounts.map(function (a) { return ethUsdRate * a * gasPrice / 1e18; });
4623
+ routes.forEach(function (route, i) {
4624
+ route.outputUsd = expectedAmountsUsd[i];
4625
+ route.txCostUsd = txCostsUsd[i];
4626
+ });
4627
+ return [2 /*return*/, routes.reduce(function (route1, route2) { return (route1.outputUsd - route1.txCostUsd) - (route2.outputUsd - route2.txCostUsd) >= 0 ? route1 : route2; })];
4628
+ }
4629
+ });
4630
+ }); }, {
4631
+ promise: true,
4632
+ maxAge: 5 * 60 * 1000, // 5m
4633
+ });
4634
+ var getBestRouteAndOutput = function (inputCoin, outputCoin, amount) { return __awaiter(void 0, void 0, void 0, function () {
4635
+ var _a, inputCoinAddress, outputCoinAddress, outputCoinDecimals, _c, steps, _output;
4636
+ return __generator(this, function (_d) {
4637
+ switch (_d.label) {
4638
+ case 0:
4639
+ _a = (0, utils_1._getCoinAddresses)(inputCoin, outputCoin), inputCoinAddress = _a[0], outputCoinAddress = _a[1];
4640
+ outputCoinDecimals = (0, utils_1._getCoinDecimals)(outputCoinAddress)[0];
4641
+ return [4 /*yield*/, _getBestRouteAndOutput(inputCoinAddress, outputCoinAddress, amount)];
4642
+ case 1:
4643
+ _c = _d.sent(), steps = _c.steps, _output = _c._output;
4644
+ return [2 /*return*/, { route: steps, output: ethers_1.ethers.utils.formatUnits(_output, outputCoinDecimals) }];
4645
+ }
4646
+ });
4647
+ }); };
4648
+ exports.getBestRouteAndOutput = getBestRouteAndOutput;
4649
+ var routerExchangeExpected = function (inputCoin, outputCoin, amount) { return __awaiter(void 0, void 0, void 0, function () {
4650
+ return __generator(this, function (_a) {
4651
+ switch (_a.label) {
4652
+ case 0: return [4 /*yield*/, (0, exports.getBestRouteAndOutput)(inputCoin, outputCoin, amount)];
4653
+ case 1: return [2 /*return*/, (_a.sent())['output']];
4654
+ }
4655
+ });
4656
+ }); };
4657
+ exports.routerExchangeExpected = routerExchangeExpected;
4658
+ var routerExchangeIsApproved = function (inputCoin, amount) { return __awaiter(void 0, void 0, void 0, function () {
4659
+ return __generator(this, function (_a) {
4660
+ switch (_a.label) {
4661
+ case 0: return [4 /*yield*/, (0, utils_1.hasAllowance)([inputCoin], [amount], curve_1.curve.signerAddress, curve_1.ALIASES.registry_exchange)];
4662
+ case 1: return [2 /*return*/, _a.sent()];
4663
+ }
4664
+ });
4665
+ }); };
4666
+ exports.routerExchangeIsApproved = routerExchangeIsApproved;
4667
+ var routerExchangeApproveEstimateGas = function (inputCoin, amount) { return __awaiter(void 0, void 0, void 0, function () {
4668
+ return __generator(this, function (_a) {
4669
+ switch (_a.label) {
4670
+ case 0: return [4 /*yield*/, (0, utils_1.ensureAllowanceEstimateGas)([inputCoin], [amount], curve_1.ALIASES.registry_exchange)];
4671
+ case 1: return [2 /*return*/, _a.sent()];
4672
+ }
4673
+ });
4674
+ }); };
4675
+ exports.routerExchangeApproveEstimateGas = routerExchangeApproveEstimateGas;
4676
+ var routerExchangeApprove = function (inputCoin, amount) { return __awaiter(void 0, void 0, void 0, function () {
4677
+ return __generator(this, function (_a) {
4678
+ switch (_a.label) {
4679
+ case 0: return [4 /*yield*/, (0, utils_1.ensureAllowance)([inputCoin], [amount], curve_1.ALIASES.registry_exchange)];
4680
+ case 1: return [2 /*return*/, _a.sent()];
4681
+ }
4682
+ });
4683
+ }); };
4684
+ exports.routerExchangeApprove = routerExchangeApprove;
4685
+ var routerExchangeEstimateGas = function (inputCoin, outputCoin, amount) { return __awaiter(void 0, void 0, void 0, function () {
4686
+ var _a, inputCoinAddress, outputCoinAddress, inputCoinDecimals, route, _amount, gas;
4687
+ return __generator(this, function (_c) {
4688
+ switch (_c.label) {
4689
+ case 0:
4690
+ _a = (0, utils_1._getCoinAddresses)(inputCoin, outputCoin), inputCoinAddress = _a[0], outputCoinAddress = _a[1];
4691
+ inputCoinDecimals = (0, utils_1._getCoinDecimals)(inputCoinAddress, outputCoinAddress)[0];
4692
+ return [4 /*yield*/, _getBestRouteAndOutput(inputCoinAddress, outputCoinAddress, amount)];
4693
+ case 1:
4694
+ route = _c.sent();
4695
+ _amount = ethers_1.ethers.utils.parseUnits(amount, inputCoinDecimals);
4696
+ return [4 /*yield*/, _estimateGasForDifferentRoutes([route], inputCoinAddress, outputCoinAddress, _amount)];
4697
+ case 2:
4698
+ gas = (_c.sent())[0];
4699
+ return [2 /*return*/, gas];
4700
+ }
4701
+ });
4702
+ }); };
4703
+ exports.routerExchangeEstimateGas = routerExchangeEstimateGas;
4704
+ var routerExchange = function (inputCoin, outputCoin, amount, maxSlippage) {
4705
+ if (maxSlippage === void 0) { maxSlippage = 0.01; }
4706
+ return __awaiter(void 0, void 0, void 0, function () {
4707
+ var _a, inputCoinAddress, outputCoinAddress, _c, inputCoinDecimals, outputCoinDecimals, route, _d, _route, _swapParams, _factorySwapAddresses, _amount, minRecvAmountBN, _minRecvAmount, contract, value, gasLimit;
4708
+ return __generator(this, function (_e) {
4709
+ switch (_e.label) {
4710
+ case 0:
4711
+ _a = (0, utils_1._getCoinAddresses)(inputCoin, outputCoin), inputCoinAddress = _a[0], outputCoinAddress = _a[1];
4712
+ _c = (0, utils_1._getCoinDecimals)(inputCoinAddress, outputCoinAddress), inputCoinDecimals = _c[0], outputCoinDecimals = _c[1];
4713
+ return [4 /*yield*/, (0, exports.routerExchangeApprove)(inputCoin, amount)];
4714
+ case 1:
4715
+ _e.sent();
4716
+ return [4 /*yield*/, _getBestRouteAndOutput(inputCoinAddress, outputCoinAddress, amount)];
4717
+ case 2:
4718
+ route = _e.sent();
4719
+ if (route.steps.length === 0) {
4720
+ throw new Error("This pair can't be exchanged");
4721
+ }
4722
+ _d = _getExchangeMultipleArgs(inputCoinAddress, route), _route = _d._route, _swapParams = _d._swapParams, _factorySwapAddresses = _d._factorySwapAddresses;
4723
+ _amount = ethers_1.ethers.utils.parseUnits(amount, inputCoinDecimals);
4724
+ minRecvAmountBN = (0, utils_1.toBN)(route._output, outputCoinDecimals).times(1 - maxSlippage);
4725
+ _minRecvAmount = (0, utils_1.fromBN)(minRecvAmountBN, outputCoinDecimals);
4726
+ contract = curve_1.curve.contracts[curve_1.ALIASES.registry_exchange].contract;
4727
+ value = (0, utils_1.isEth)(inputCoinAddress) ? _amount : ethers_1.ethers.BigNumber.from(0);
4728
+ return [4 /*yield*/, curve_1.curve.updateFeeData()];
4729
+ case 3:
4730
+ _e.sent();
4731
+ return [4 /*yield*/, contract.estimateGas.exchange_multiple(_route, _swapParams, _amount, _minRecvAmount, _factorySwapAddresses, __assign(__assign({}, curve_1.curve.constantOptions), { value: value }))];
4732
+ case 4:
4733
+ gasLimit = (_e.sent()).mul(curve_1.curve.chainId === 1 ? 130 : 160).div(100);
4734
+ return [4 /*yield*/, contract.exchange_multiple(_route, _swapParams, _amount, _minRecvAmount, _factorySwapAddresses, __assign(__assign({}, curve_1.curve.options), { value: value, gasLimit: gasLimit }))];
4735
+ case 5: return [2 /*return*/, (_e.sent()).hash];
4736
+ }
4737
+ });
4738
+ });
4739
+ };
4740
+ exports.routerExchange = routerExchange;