@curvefi/api 1.22.0 → 1.23.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/README.md CHANGED
@@ -494,7 +494,66 @@ import curve from "@curvefi/api";
494
494
  })()
495
495
  ```
496
496
 
497
- ## Exchange using all pools
497
+ ## Exchange
498
+
499
+ ### Router exchange
500
+
501
+ ```ts
502
+ import curve from "@curvefi/api";
503
+
504
+ (async () => {
505
+ await curve.init('JsonRpc', {}, { gasPrice: 0, chainId: 1 });
506
+
507
+ console.log(await curve.getBalances(['DAI', 'CRV']));
508
+ // [ '9900.0', '100049.744832225238317557' ]
509
+
510
+ const { route, output } = await curve.getBestRouteAndOutput('DAI', 'CRV', '1000');
511
+ // OR await curve.getBestPoolAndOutput('0x6B175474E89094C44Da98b954EedeAC495271d0F', '0xD533a949740bb3306d119CC777fa900bA034cd52', '10000');
512
+ const expected = await curve.routerExchangeExpected('DAI', 'CRV', '1000');
513
+ // OR await curve.exchangeExpected('0x6B175474E89094C44Da98b954EedeAC495271d0F', '0xD533a949740bb3306d119CC777fa900bA034cd52', '10000');
514
+
515
+ console.log(route, output, expected);
516
+ // route = [
517
+ // {
518
+ // poolId: '3pool',
519
+ // poolAddress: '0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7',
520
+ // outputCoinAddress: '0xdac17f958d2ee523a2206206994597c13d831ec7',
521
+ // i: 0,
522
+ // j: 2,
523
+ // swapType: 1,
524
+ // swapAddress: '0x0000000000000000000000000000000000000000'
525
+ // },
526
+ // {
527
+ // poolId: 'tricrypto2',
528
+ // poolAddress: '0xD51a44d3FaE010294C616388b506AcdA1bfAAE46',
529
+ // outputCoinAddress: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',
530
+ // i: 0,
531
+ // j: 2,
532
+ // swapType: 3,
533
+ // swapAddress: '0x0000000000000000000000000000000000000000'
534
+ // },
535
+ // {
536
+ // poolId: 'crveth',
537
+ // poolAddress: '0x8301AE4fc9c624d1D396cbDAa1ed877821D7C511',
538
+ // outputCoinAddress: '0xd533a949740bb3306d119cc777fa900ba034cd52',
539
+ // i: 0,
540
+ // j: 1,
541
+ // swapType: 3,
542
+ // swapAddress: '0x0000000000000000000000000000000000000000'
543
+ // }
544
+ // ]
545
+ //
546
+ // output = expected = 378.881631202862354937
547
+
548
+ await curve.routerExchange('DAI', 'CRV', '1000')
549
+ // OR await curve.exchange('0x6B175474E89094C44Da98b954EedeAC495271d0F', '0xD533a949740bb3306d119CC777fa900bA034cd52', '10000');
550
+
551
+ console.log(await curve.getBalances(['DAI', 'CRV']));
552
+ // [ '8900.0', '100428.626463428100672494' ]
553
+ })()
554
+ ```
555
+
556
+ ### Single-pool exchange
498
557
 
499
558
  ```ts
500
559
  import curve from "@curvefi/api";
@@ -521,7 +580,7 @@ import curve from "@curvefi/api";
521
580
  })()
522
581
  ```
523
582
 
524
- ## Cross-Asset Exchange
583
+ ### Cross-Asset Exchange
525
584
 
526
585
  ```ts
527
586
  import curve from "@curvefi/api";
package/lib/index.d.ts CHANGED
@@ -58,12 +58,22 @@ declare const curve: {
58
58
  crossAssetExchangeApprove: (inputCoin: string, amount: string) => Promise<string[]>;
59
59
  crossAssetExchange: (inputCoin: string, outputCoin: string, amount: string, maxSlippage?: number) => Promise<string>;
60
60
  getUserPoolList: (address?: string | undefined) => Promise<string[]>;
61
+ getBestRouteAndOutput: (inputCoin: string, outputCoin: string, amount: string) => Promise<{
62
+ route: import("./interfaces").IRouteStep[];
63
+ output: string;
64
+ }>;
65
+ routerExchangeExpected: (inputCoin: string, outputCoin: string, amount: string) => Promise<string>;
66
+ routerExchangeIsApproved: (inputCoin: string, amount: string) => Promise<boolean>;
67
+ routerExchangeApprove: (inputCoin: string, amount: string) => Promise<string[]>;
68
+ routerExchange: (inputCoin: string, outputCoin: string, amount: string, maxSlippage?: number) => Promise<string>;
61
69
  estimateGas: {
62
70
  ensureAllowance: (coins: string[], amounts: string[], spender: string) => Promise<number>;
63
71
  exchangeApprove: (inputCoin: string, outputCoin: string, amount: string) => Promise<number>;
64
72
  exchange: (inputCoin: string, outputCoin: string, amount: string, maxSlippage?: number) => Promise<number>;
65
73
  crossAssetExchangeApprove: (inputCoin: string, amount: string) => Promise<number>;
66
74
  crossAssetExchange: (inputCoin: string, outputCoin: string, amount: string, maxSlippage?: number) => Promise<number>;
75
+ routerExchangeApprove: (inputCoin: string, amount: string) => Promise<number>;
76
+ routerExchange: (inputCoin: string, outputCoin: string, amount: string, maxSlippage?: number) => Promise<number>;
67
77
  };
68
78
  boosting: {
69
79
  getCrv: (...addresses: string[] | string[][]) => Promise<string | import("./interfaces").DictInterface<string>>;
package/lib/index.js CHANGED
@@ -115,12 +115,19 @@ var curve = {
115
115
  crossAssetExchangeApprove: pools_1.crossAssetExchangeApprove,
116
116
  crossAssetExchange: pools_1.crossAssetExchange,
117
117
  getUserPoolList: pools_1.getUserPoolList,
118
+ getBestRouteAndOutput: pools_1.getBestRouteAndOutput,
119
+ routerExchangeExpected: pools_1.routerExchangeExpected,
120
+ routerExchangeIsApproved: pools_1.routerExchangeIsApproved,
121
+ routerExchangeApprove: pools_1.routerExchangeApprove,
122
+ routerExchange: pools_1.routerExchange,
118
123
  estimateGas: {
119
124
  ensureAllowance: utils_1.ensureAllowanceEstimateGas,
120
125
  exchangeApprove: pools_1.exchangeApproveEstimateGas,
121
126
  exchange: pools_1.exchangeEstimateGas,
122
127
  crossAssetExchangeApprove: pools_1.crossAssetExchangeApproveEstimateGas,
123
128
  crossAssetExchange: pools_1.crossAssetExchangeEstimateGas,
129
+ routerExchangeApprove: pools_1.routerExchangeApproveEstimateGas,
130
+ routerExchange: pools_1.routerExchangeEstimateGas,
124
131
  },
125
132
  boosting: {
126
133
  getCrv: boosting_1.getCrv,
@@ -134,9 +134,24 @@ export interface ISinglePoolSwapData {
134
134
  poolAddress: string;
135
135
  i: number;
136
136
  j: number;
137
- swapType: 1 | 2 | 3 | 4;
137
+ swapType: 1 | 2 | 3 | 4 | 5;
138
138
  swapAddress: string;
139
139
  }
140
140
  export interface ISinglePoolSwapDataAndOutput extends ISinglePoolSwapData {
141
141
  _output: ethers.BigNumber;
142
142
  }
143
+ export interface IRouteStep {
144
+ poolId: string;
145
+ poolAddress: string;
146
+ outputCoinAddress: string;
147
+ i: number;
148
+ j: number;
149
+ swapType: 1 | 2 | 3 | 4 | 5;
150
+ swapAddress: string;
151
+ }
152
+ export interface IRoute {
153
+ steps: IRouteStep[];
154
+ _output: ethers.BigNumber;
155
+ outputUsd: number;
156
+ txCostUsd: number;
157
+ }
package/lib/pools.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { ethers } from "ethers";
2
2
  import BigNumber from 'bignumber.js';
3
- import { DictInterface, RewardsApyInterface } from './interfaces';
3
+ import { DictInterface, IRouteStep, RewardsApyInterface } from './interfaces';
4
4
  export declare class Pool {
5
5
  id: string;
6
6
  name: string;
@@ -254,3 +254,14 @@ export declare const crossAssetExchangeApprove: (inputCoin: string, amount: stri
254
254
  export declare const crossAssetExchangeEstimateGas: (inputCoin: string, outputCoin: string, amount: string, maxSlippage?: number) => Promise<number>;
255
255
  export declare const crossAssetExchange: (inputCoin: string, outputCoin: string, amount: string, maxSlippage?: number) => Promise<string>;
256
256
  export declare const getUserPoolList: (address?: string | undefined) => Promise<string[]>;
257
+ export declare const _findAllRoutes: (inputCoinAddress: string, outputCoinAddress: string) => Promise<IRouteStep[][]>;
258
+ export declare const getBestRouteAndOutput: (inputCoin: string, outputCoin: string, amount: string) => Promise<{
259
+ route: IRouteStep[];
260
+ output: string;
261
+ }>;
262
+ export declare const routerExchangeExpected: (inputCoin: string, outputCoin: string, amount: string) => Promise<string>;
263
+ export declare const routerExchangeIsApproved: (inputCoin: string, amount: string) => Promise<boolean>;
264
+ export declare const routerExchangeApproveEstimateGas: (inputCoin: string, amount: string) => Promise<number>;
265
+ export declare const routerExchangeApprove: (inputCoin: string, amount: string) => Promise<string[]>;
266
+ export declare const routerExchangeEstimateGas: (inputCoin: string, outputCoin: string, amount: string, maxSlippage?: number) => Promise<number>;
267
+ export declare const routerExchange: (inputCoin: string, outputCoin: string, amount: string, maxSlippage?: number) => Promise<string>;
package/lib/pools.js CHANGED
@@ -59,9 +59,10 @@ 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
63
  var axios_1 = __importDefault(require("axios"));
64
64
  var ethers_1 = require("ethers");
65
+ var memoizee_1 = __importDefault(require("memoizee"));
65
66
  var external_api_1 = require("./external-api");
66
67
  var utils_1 = require("./utils");
67
68
  var curve_1 = require("./curve");
@@ -4180,3 +4181,506 @@ var getUserPoolList = function (address) { return __awaiter(void 0, void 0, void
4180
4181
  });
4181
4182
  }); };
4182
4183
  exports.getUserPoolList = getUserPoolList;
4184
+ // --------- Multi-Pool Exchange ---------
4185
+ var IMBALANCED_POOLS = [];
4186
+ // Inspired by Dijkstra's algorithm
4187
+ var _findAllRoutes = function (inputCoinAddress, outputCoinAddress) { return __awaiter(void 0, void 0, void 0, function () {
4188
+ var ALL_POOLS, markedCoins, curCoins, nextCoins, routes, step, _i, curCoins_1, inCoin, _a, ALL_POOLS_1, _c, poolId, poolData, coin_addresses, underlying_coin_addresses, meta_coin_addresses, inCoinIndexes, j, tvl, _d, swapType, _e, _f, inCoinRoute, poolAddress, j, tvl, _h, swapType, _j, _k, inCoinRoute, j, tvl, _l, swapType, _m, _o, inCoinRoute, tvl, _p, swapType, _q, _r, inCoinRoute;
4189
+ var _s;
4190
+ var _t, _u, _v, _w, _x, _y;
4191
+ return __generator(this, function (_z) {
4192
+ switch (_z.label) {
4193
+ case 0:
4194
+ inputCoinAddress = inputCoinAddress.toLowerCase();
4195
+ outputCoinAddress = outputCoinAddress.toLowerCase();
4196
+ 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));
4197
+ markedCoins = [];
4198
+ curCoins = [inputCoinAddress];
4199
+ nextCoins = new Set();
4200
+ routes = (_s = {},
4201
+ _s[inputCoinAddress] = [[]],
4202
+ _s);
4203
+ step = 0;
4204
+ _z.label = 1;
4205
+ case 1:
4206
+ if (!(step < 4)) return [3 /*break*/, 21];
4207
+ _i = 0, curCoins_1 = curCoins;
4208
+ _z.label = 2;
4209
+ case 2:
4210
+ if (!(_i < curCoins_1.length)) return [3 /*break*/, 19];
4211
+ inCoin = curCoins_1[_i];
4212
+ _a = 0, ALL_POOLS_1 = ALL_POOLS;
4213
+ _z.label = 3;
4214
+ case 3:
4215
+ if (!(_a < ALL_POOLS_1.length)) return [3 /*break*/, 18];
4216
+ _c = ALL_POOLS_1[_a], poolId = _c[0], poolData = _c[1];
4217
+ coin_addresses = poolData.coin_addresses.map(function (a) { return a.toLowerCase(); });
4218
+ underlying_coin_addresses = poolData.underlying_coin_addresses.map(function (a) { return a.toLowerCase(); });
4219
+ meta_coin_addresses = ((_t = poolData.meta_coin_addresses) === null || _t === void 0 ? void 0 : _t.map(function (a) { return a.toLowerCase(); })) || [];
4220
+ inCoinIndexes = {
4221
+ coin: coin_addresses.indexOf(inCoin),
4222
+ underlying_coin: underlying_coin_addresses.indexOf(inCoin),
4223
+ meta_coin: meta_coin_addresses ? meta_coin_addresses.indexOf(inCoin) : -1,
4224
+ };
4225
+ // No input coin in this pool --> slip
4226
+ if (inCoinIndexes.coin === -1 && inCoinIndexes.underlying_coin === -1 && inCoinIndexes.meta_coin === -1)
4227
+ return [3 /*break*/, 17];
4228
+ if (!(inCoinIndexes.coin >= 0 && poolId !== "atricrypto3")) return [3 /*break*/, 7];
4229
+ j = 0;
4230
+ _z.label = 4;
4231
+ case 4:
4232
+ if (!(j < coin_addresses.length)) return [3 /*break*/, 7];
4233
+ // If this coin already marked or will be marked on the current step, no need to consider it on the next step
4234
+ if (markedCoins.includes(coin_addresses[j]) || curCoins.includes(coin_addresses[j]))
4235
+ return [3 /*break*/, 6];
4236
+ // Looking for outputCoinAddress only on the final step
4237
+ if (step === 3 && coin_addresses[j] !== outputCoinAddress)
4238
+ return [3 /*break*/, 6];
4239
+ _d = Number;
4240
+ return [4 /*yield*/, (new Pool(poolId)).stats.getTotalLiquidity()];
4241
+ case 5:
4242
+ tvl = _d.apply(void 0, [_z.sent()]);
4243
+ if (tvl === 0)
4244
+ return [3 /*break*/, 6];
4245
+ // Skip imbalanced pools
4246
+ if (IMBALANCED_POOLS.includes(poolId))
4247
+ return [3 /*break*/, 6];
4248
+ swapType = poolData.is_crypto ? 3 : 1;
4249
+ for (_e = 0, _f = routes[inCoin]; _e < _f.length; _e++) {
4250
+ inCoinRoute = _f[_e];
4251
+ routes[coin_addresses[j]] = ((_u = routes[coin_addresses[j]]) !== null && _u !== void 0 ? _u : []).concat([__spreadArray(__spreadArray([], inCoinRoute, true), [
4252
+ {
4253
+ poolId: poolId,
4254
+ poolAddress: poolData.swap_address,
4255
+ outputCoinAddress: coin_addresses[j],
4256
+ i: inCoinIndexes.coin,
4257
+ j: j,
4258
+ swapType: swapType,
4259
+ swapAddress: ethers_1.ethers.constants.AddressZero,
4260
+ },
4261
+ ], false)]);
4262
+ }
4263
+ nextCoins.add(coin_addresses[j]);
4264
+ _z.label = 6;
4265
+ case 6:
4266
+ j++;
4267
+ return [3 /*break*/, 4];
4268
+ case 7:
4269
+ poolAddress = ["eurtusd", "xautusd", "atricrypto3"].includes(poolId) ||
4270
+ (curve_1.curve.chainId === 137 && poolData.is_factory) ? poolData.deposit_address : poolData.swap_address;
4271
+ if (!(coin_addresses.join("|") !== underlying_coin_addresses.join("|") && inCoinIndexes.underlying_coin >= 0)) return [3 /*break*/, 11];
4272
+ j = 0;
4273
+ _z.label = 8;
4274
+ case 8:
4275
+ if (!(j < underlying_coin_addresses.length)) return [3 /*break*/, 11];
4276
+ if (poolId === "atricrypto3" && inCoinIndexes.meta_coin >= 0 && meta_coin_addresses.includes(underlying_coin_addresses[j]))
4277
+ return [3 /*break*/, 10];
4278
+ // If this coin already marked or will be marked on the current step, no need to consider it on the next step
4279
+ if (markedCoins.includes(underlying_coin_addresses[j]) || curCoins.includes(underlying_coin_addresses[j]))
4280
+ return [3 /*break*/, 10];
4281
+ // Looking for outputCoinAddress only on the final step
4282
+ if (step === 3 && underlying_coin_addresses[j] !== outputCoinAddress)
4283
+ return [3 /*break*/, 10];
4284
+ _h = Number;
4285
+ return [4 /*yield*/, (new Pool(poolId)).stats.getTotalLiquidity()];
4286
+ case 9:
4287
+ tvl = _h.apply(void 0, [_z.sent()]);
4288
+ if (tvl === 0)
4289
+ return [3 /*break*/, 10];
4290
+ // Skip imbalanced pools
4291
+ if (IMBALANCED_POOLS.includes(poolId))
4292
+ return [3 /*break*/, 10];
4293
+ swapType = poolData.is_crypto && (poolData.is_fake || poolData.is_meta) ? 4 : poolData.is_crypto ? 3 : 2;
4294
+ for (_j = 0, _k = routes[inCoin]; _j < _k.length; _j++) {
4295
+ inCoinRoute = _k[_j];
4296
+ routes[underlying_coin_addresses[j]] = ((_v = routes[underlying_coin_addresses[j]]) !== null && _v !== void 0 ? _v : []).concat([__spreadArray(__spreadArray([], inCoinRoute, true), [
4297
+ {
4298
+ poolId: poolId,
4299
+ poolAddress: poolAddress,
4300
+ outputCoinAddress: underlying_coin_addresses[j],
4301
+ i: inCoinIndexes.underlying_coin,
4302
+ j: j,
4303
+ swapType: swapType,
4304
+ swapAddress: ethers_1.ethers.constants.AddressZero,
4305
+ },
4306
+ ], false)]);
4307
+ }
4308
+ nextCoins.add(underlying_coin_addresses[j]);
4309
+ _z.label = 10;
4310
+ case 10:
4311
+ j++;
4312
+ return [3 /*break*/, 8];
4313
+ case 11:
4314
+ if (!(inCoinIndexes.coin === 0 && meta_coin_addresses.length > 0 && poolId !== "atricrypto3")) return [3 /*break*/, 15];
4315
+ j = 0;
4316
+ _z.label = 12;
4317
+ case 12:
4318
+ if (!(j < meta_coin_addresses.length)) return [3 /*break*/, 15];
4319
+ // If this coin already marked or will be marked on the current step, no need to consider it on the next step
4320
+ if (markedCoins.includes(meta_coin_addresses[j]) || curCoins.includes(meta_coin_addresses[j]))
4321
+ return [3 /*break*/, 14];
4322
+ // Looking for outputCoinAddress only on the final step
4323
+ if (step === 3 && meta_coin_addresses[j] !== outputCoinAddress)
4324
+ return [3 /*break*/, 14];
4325
+ _l = Number;
4326
+ return [4 /*yield*/, (new Pool(poolId)).stats.getTotalLiquidity()];
4327
+ case 13:
4328
+ tvl = _l.apply(void 0, [_z.sent()]);
4329
+ if (tvl === 0)
4330
+ return [3 /*break*/, 14];
4331
+ // Skip imbalanced pools
4332
+ if (IMBALANCED_POOLS.includes(poolId))
4333
+ return [3 /*break*/, 14];
4334
+ swapType = (curve_1.curve.chainId === 137 && poolData.is_factory) ? 5 : poolData.is_crypto ? 4 : 2;
4335
+ for (_m = 0, _o = routes[inCoin]; _m < _o.length; _m++) {
4336
+ inCoinRoute = _o[_m];
4337
+ routes[meta_coin_addresses[j]] = ((_w = routes[meta_coin_addresses[j]]) !== null && _w !== void 0 ? _w : []).concat([__spreadArray(__spreadArray([], inCoinRoute, true), [
4338
+ {
4339
+ poolId: poolId,
4340
+ poolAddress: poolAddress,
4341
+ outputCoinAddress: meta_coin_addresses[j],
4342
+ i: inCoinIndexes.coin,
4343
+ j: j + 1,
4344
+ swapType: swapType,
4345
+ swapAddress: swapType === 5 ? poolData.swap_address : ethers_1.ethers.constants.AddressZero,
4346
+ },
4347
+ ], false)]);
4348
+ }
4349
+ nextCoins.add(meta_coin_addresses[j]);
4350
+ _z.label = 14;
4351
+ case 14:
4352
+ j++;
4353
+ return [3 /*break*/, 12];
4354
+ case 15:
4355
+ if (!(inCoinIndexes.meta_coin >= 0 && poolId !== "atricrypto3")) return [3 /*break*/, 17];
4356
+ // If this coin already marked or will be marked on the current step, no need to consider it on the next step
4357
+ if (markedCoins.includes(coin_addresses[0]) || curCoins.includes(coin_addresses[0]))
4358
+ return [3 /*break*/, 17];
4359
+ // Looking for outputCoinAddress only on the final step
4360
+ if (step === 3 && coin_addresses[0] !== outputCoinAddress)
4361
+ return [3 /*break*/, 17];
4362
+ _p = Number;
4363
+ return [4 /*yield*/, (new Pool(poolId)).stats.getTotalLiquidity()];
4364
+ case 16:
4365
+ tvl = _p.apply(void 0, [_z.sent()]);
4366
+ if (tvl === 0)
4367
+ return [3 /*break*/, 17];
4368
+ // Skip imbalanced pools
4369
+ if (IMBALANCED_POOLS.includes(poolId))
4370
+ return [3 /*break*/, 17];
4371
+ swapType = (curve_1.curve.chainId === 137 && poolData.is_factory) ? 5 : poolData.is_crypto ? 4 : 2;
4372
+ for (_q = 0, _r = routes[inCoin]; _q < _r.length; _q++) {
4373
+ inCoinRoute = _r[_q];
4374
+ routes[coin_addresses[0]] = ((_x = routes[coin_addresses[0]]) !== null && _x !== void 0 ? _x : []).concat([__spreadArray(__spreadArray([], inCoinRoute, true), [
4375
+ {
4376
+ poolId: poolId,
4377
+ poolAddress: poolAddress,
4378
+ outputCoinAddress: coin_addresses[0],
4379
+ i: inCoinIndexes.meta_coin + 1,
4380
+ j: 0,
4381
+ swapType: swapType,
4382
+ swapAddress: swapType === 5 ? poolData.swap_address : ethers_1.ethers.constants.AddressZero,
4383
+ },
4384
+ ], false)]);
4385
+ nextCoins.add(coin_addresses[0]);
4386
+ }
4387
+ _z.label = 17;
4388
+ case 17:
4389
+ _a++;
4390
+ return [3 /*break*/, 3];
4391
+ case 18:
4392
+ _i++;
4393
+ return [3 /*break*/, 2];
4394
+ case 19:
4395
+ // If target output coin is reached, search is finished. Assumption: the shorter route, the better.
4396
+ if (outputCoinAddress in routes)
4397
+ return [3 /*break*/, 21];
4398
+ markedCoins.push.apply(markedCoins, curCoins);
4399
+ curCoins = Array.from(nextCoins);
4400
+ nextCoins = new Set();
4401
+ _z.label = 20;
4402
+ case 20:
4403
+ step++;
4404
+ return [3 /*break*/, 1];
4405
+ case 21: return [2 /*return*/, (_y = routes[outputCoinAddress]) !== null && _y !== void 0 ? _y : []];
4406
+ }
4407
+ });
4408
+ }); };
4409
+ exports._findAllRoutes = _findAllRoutes;
4410
+ var _getRouteKey = function (route, inputCoinAddress, outputCoinAddress) {
4411
+ var sortedCoins = [inputCoinAddress, outputCoinAddress].sort();
4412
+ var key = "".concat(sortedCoins[0], "-->");
4413
+ for (var _i = 0, _a = route.steps; _i < _a.length; _i++) {
4414
+ var routeStep = _a[_i];
4415
+ key += "".concat(routeStep.poolId, "-->");
4416
+ }
4417
+ key += sortedCoins[1];
4418
+ return key;
4419
+ };
4420
+ var _getExchangeMultipleArgs = function (inputCoinAddress, route) {
4421
+ var _route = [inputCoinAddress];
4422
+ var _swapParams = [];
4423
+ var _factorySwapAddresses = [];
4424
+ for (var _i = 0, _a = route.steps; _i < _a.length; _i++) {
4425
+ var routeStep = _a[_i];
4426
+ _route.push(routeStep.poolAddress, routeStep.outputCoinAddress);
4427
+ _swapParams.push([routeStep.i, routeStep.j, routeStep.swapType]);
4428
+ _factorySwapAddresses.push(routeStep.swapAddress);
4429
+ }
4430
+ _route = _route.concat(Array(9 - _route.length).fill(ethers_1.ethers.constants.AddressZero));
4431
+ _swapParams = _swapParams.concat(Array(4 - _swapParams.length).fill([0, 0, 0]));
4432
+ _factorySwapAddresses = _factorySwapAddresses.concat(Array(4 - _factorySwapAddresses.length).fill(ethers_1.ethers.constants.AddressZero));
4433
+ return { _route: _route, _swapParams: _swapParams, _factorySwapAddresses: _factorySwapAddresses };
4434
+ };
4435
+ var _estimatedGasForDifferentRoutesCache = {};
4436
+ var _estimateGasForDifferentRoutes = function (routes, inputCoinAddress, outputCoinAddress, _amount) { return __awaiter(void 0, void 0, void 0, function () {
4437
+ var registryExchangeContract, gasPromises, _i, routes_1, route, routeKey, gasPromise, _a, _route, _swapParams, _factorySwapAddresses, _gasAmounts_2, err_3;
4438
+ var _c;
4439
+ return __generator(this, function (_d) {
4440
+ switch (_d.label) {
4441
+ case 0:
4442
+ inputCoinAddress = inputCoinAddress.toLowerCase();
4443
+ outputCoinAddress = outputCoinAddress.toLowerCase();
4444
+ registryExchangeContract = curve_1.curve.contracts[curve_1.ALIASES.registry_exchange].contract;
4445
+ gasPromises = [];
4446
+ for (_i = 0, routes_1 = routes; _i < routes_1.length; _i++) {
4447
+ route = routes_1[_i];
4448
+ routeKey = _getRouteKey(route, inputCoinAddress, outputCoinAddress);
4449
+ gasPromise = void 0;
4450
+ _a = _getExchangeMultipleArgs(inputCoinAddress, route), _route = _a._route, _swapParams = _a._swapParams, _factorySwapAddresses = _a._factorySwapAddresses;
4451
+ if ((((_c = _estimatedGasForDifferentRoutesCache[routeKey]) === null || _c === void 0 ? void 0 : _c.time) || 0) + 3600000 < Date.now()) {
4452
+ gasPromise = registryExchangeContract.estimateGas.exchange_multiple(_route, _swapParams, _amount, 0, _factorySwapAddresses, curve_1.curve.constantOptions);
4453
+ }
4454
+ else {
4455
+ gasPromise = Promise.resolve(_estimatedGasForDifferentRoutesCache[routeKey].gas);
4456
+ }
4457
+ gasPromises.push(gasPromise);
4458
+ }
4459
+ _d.label = 1;
4460
+ case 1:
4461
+ _d.trys.push([1, 3, , 4]);
4462
+ return [4 /*yield*/, Promise.all(gasPromises)];
4463
+ case 2:
4464
+ _gasAmounts_2 = _d.sent();
4465
+ routes.forEach(function (route, i) {
4466
+ var routeKey = _getRouteKey(route, inputCoinAddress, outputCoinAddress);
4467
+ _estimatedGasForDifferentRoutesCache[routeKey] = { 'gas': _gasAmounts_2[i], 'time': Date.now() };
4468
+ });
4469
+ return [2 /*return*/, _gasAmounts_2.map(function (_g) { return Number(ethers_1.ethers.utils.formatUnits(_g, 0)); })];
4470
+ case 3:
4471
+ err_3 = _d.sent();
4472
+ return [2 /*return*/, routes.map(function () { return 0; })];
4473
+ case 4: return [2 /*return*/];
4474
+ }
4475
+ });
4476
+ }); };
4477
+ var _getBestRouteAndOutput = (0, memoizee_1.default)(function (inputCoinAddress, outputCoinAddress, amount) { return __awaiter(void 0, void 0, void 0, function () {
4478
+ var _a, inputCoinDecimals, outputCoinDecimals, _amount, routesRaw, routes, _i, routesRaw_1, route, _outputAmount, _c, _d, routeStep, poolId, poolAddress, i, j, swapType, swapAddress, contract, _e, err_4, _f, gasAmounts, outputCoinUsdRate, gasData, ethUsdRate, gasPrice, expectedAmounts, expectedAmountsUsd, txCostsUsd;
4479
+ return __generator(this, function (_h) {
4480
+ switch (_h.label) {
4481
+ case 0:
4482
+ _a = (0, utils_1._getCoinDecimals)(inputCoinAddress, outputCoinAddress), inputCoinDecimals = _a[0], outputCoinDecimals = _a[1];
4483
+ _amount = ethers_1.ethers.utils.parseUnits(amount.toString(), inputCoinDecimals);
4484
+ return [4 /*yield*/, (0, exports._findAllRoutes)(inputCoinAddress, outputCoinAddress)];
4485
+ case 1:
4486
+ routesRaw = (_h.sent()).map(function (steps) { return ({ steps: steps, _output: ethers_1.ethers.BigNumber.from(0), outputUsd: 0, txCostUsd: 0 }); });
4487
+ routes = [];
4488
+ _i = 0, routesRaw_1 = routesRaw;
4489
+ _h.label = 2;
4490
+ case 2:
4491
+ if (!(_i < routesRaw_1.length)) return [3 /*break*/, 13];
4492
+ route = routesRaw_1[_i];
4493
+ // If one of pools in the route is imbalanced
4494
+ if (route.steps.reduce(function (acc, step) { return acc || IMBALANCED_POOLS.includes(step.poolId); }, false))
4495
+ return [3 /*break*/, 12];
4496
+ _outputAmount = _amount;
4497
+ _c = 0, _d = route.steps;
4498
+ _h.label = 3;
4499
+ case 3:
4500
+ if (!(_c < _d.length)) return [3 /*break*/, 11];
4501
+ routeStep = _d[_c];
4502
+ poolId = routeStep.poolId, poolAddress = routeStep.poolAddress, i = routeStep.i, j = routeStep.j, swapType = routeStep.swapType, swapAddress = routeStep.swapAddress;
4503
+ contract = curve_1.curve.contracts[swapAddress === ethers_1.ethers.constants.AddressZero ? poolAddress : swapAddress].contract;
4504
+ _h.label = 4;
4505
+ case 4:
4506
+ _h.trys.push([4, 9, , 10]);
4507
+ if (![2, 4, 5].includes(swapType)) return [3 /*break*/, 6];
4508
+ return [4 /*yield*/, contract.get_dy_underlying(i, j, _outputAmount, curve_1.curve.constantOptions)];
4509
+ case 5:
4510
+ _e = _h.sent();
4511
+ return [3 /*break*/, 8];
4512
+ case 6: return [4 /*yield*/, contract.get_dy(i, j, _outputAmount, curve_1.curve.constantOptions)];
4513
+ case 7:
4514
+ _e = _h.sent();
4515
+ _h.label = 8;
4516
+ case 8:
4517
+ _outputAmount = _e;
4518
+ return [3 /*break*/, 10];
4519
+ case 9:
4520
+ err_4 = _h.sent();
4521
+ console.log("Pool ".concat(poolId, " is empty or very imbalanced"));
4522
+ IMBALANCED_POOLS.push(poolId);
4523
+ return [3 /*break*/, 12];
4524
+ case 10:
4525
+ _c++;
4526
+ return [3 /*break*/, 3];
4527
+ case 11:
4528
+ route._output = _outputAmount;
4529
+ routes.push(route);
4530
+ _h.label = 12;
4531
+ case 12:
4532
+ _i++;
4533
+ return [3 /*break*/, 2];
4534
+ case 13:
4535
+ if (routes.length === 0) {
4536
+ return [2 /*return*/, {
4537
+ steps: [],
4538
+ _output: ethers_1.ethers.BigNumber.from(0),
4539
+ outputUsd: 0,
4540
+ txCostUsd: 0,
4541
+ }];
4542
+ }
4543
+ if (routes.length === 1)
4544
+ return [2 /*return*/, routes[0]];
4545
+ return [4 /*yield*/, Promise.all([
4546
+ _estimateGasForDifferentRoutes(routes, inputCoinAddress, outputCoinAddress, _amount),
4547
+ (0, utils_1._getUsdRate)(outputCoinAddress),
4548
+ axios_1.default.get("https://api.curve.fi/api/getGas"),
4549
+ (0, utils_1._getUsdRate)(curve_1.curve.chainId === 137 ? curve_1.COINS.matic : curve_1.COINS.eth),
4550
+ ])];
4551
+ case 14:
4552
+ _f = _h.sent(), gasAmounts = _f[0], outputCoinUsdRate = _f[1], gasData = _f[2], ethUsdRate = _f[3];
4553
+ gasPrice = gasData.data.data.gas.standard;
4554
+ expectedAmounts = (routes).map(function (route) { return Number(ethers_1.ethers.utils.formatUnits(route._output, outputCoinDecimals)); });
4555
+ expectedAmountsUsd = expectedAmounts.map(function (a) { return a * outputCoinUsdRate; });
4556
+ txCostsUsd = gasAmounts.map(function (a) { return ethUsdRate * a * gasPrice / 1e18; });
4557
+ routes.forEach(function (route, i) {
4558
+ route.outputUsd = expectedAmountsUsd[i];
4559
+ route.txCostUsd = txCostsUsd[i];
4560
+ });
4561
+ return [2 /*return*/, routes.reduce(function (route1, route2) { return (route1.outputUsd - route1.txCostUsd) - (route2.outputUsd - route2.txCostUsd) >= 0 ? route1 : route2; })];
4562
+ }
4563
+ });
4564
+ }); }, {
4565
+ promise: true,
4566
+ maxAge: 5 * 60 * 1000, // 5m
4567
+ });
4568
+ var getBestRouteAndOutput = function (inputCoin, outputCoin, amount) { return __awaiter(void 0, void 0, void 0, function () {
4569
+ var _a, inputCoinAddress, outputCoinAddress, outputCoinDecimals, _c, steps, _output;
4570
+ return __generator(this, function (_d) {
4571
+ switch (_d.label) {
4572
+ case 0:
4573
+ _a = (0, utils_1._getCoinAddresses)(inputCoin, outputCoin), inputCoinAddress = _a[0], outputCoinAddress = _a[1];
4574
+ outputCoinDecimals = (0, utils_1._getCoinDecimals)(outputCoinAddress)[0];
4575
+ return [4 /*yield*/, _getBestRouteAndOutput(inputCoinAddress, outputCoinAddress, amount)];
4576
+ case 1:
4577
+ _c = _d.sent(), steps = _c.steps, _output = _c._output;
4578
+ return [2 /*return*/, { route: steps, output: ethers_1.ethers.utils.formatUnits(_output, outputCoinDecimals) }];
4579
+ }
4580
+ });
4581
+ }); };
4582
+ exports.getBestRouteAndOutput = getBestRouteAndOutput;
4583
+ var routerExchangeExpected = function (inputCoin, outputCoin, amount) { return __awaiter(void 0, void 0, void 0, function () {
4584
+ return __generator(this, function (_a) {
4585
+ switch (_a.label) {
4586
+ case 0: return [4 /*yield*/, (0, exports.getBestRouteAndOutput)(inputCoin, outputCoin, amount)];
4587
+ case 1: return [2 /*return*/, (_a.sent())['output']];
4588
+ }
4589
+ });
4590
+ }); };
4591
+ exports.routerExchangeExpected = routerExchangeExpected;
4592
+ var routerExchangeIsApproved = function (inputCoin, amount) { return __awaiter(void 0, void 0, void 0, function () {
4593
+ return __generator(this, function (_a) {
4594
+ switch (_a.label) {
4595
+ case 0: return [4 /*yield*/, (0, utils_1.hasAllowance)([inputCoin], [amount], curve_1.curve.signerAddress, curve_1.ALIASES.registry_exchange)];
4596
+ case 1: return [2 /*return*/, _a.sent()];
4597
+ }
4598
+ });
4599
+ }); };
4600
+ exports.routerExchangeIsApproved = routerExchangeIsApproved;
4601
+ var routerExchangeApproveEstimateGas = function (inputCoin, amount) { return __awaiter(void 0, void 0, void 0, function () {
4602
+ return __generator(this, function (_a) {
4603
+ switch (_a.label) {
4604
+ case 0: return [4 /*yield*/, (0, utils_1.ensureAllowanceEstimateGas)([inputCoin], [amount], curve_1.ALIASES.registry_exchange)];
4605
+ case 1: return [2 /*return*/, _a.sent()];
4606
+ }
4607
+ });
4608
+ }); };
4609
+ exports.routerExchangeApproveEstimateGas = routerExchangeApproveEstimateGas;
4610
+ var routerExchangeApprove = function (inputCoin, amount) { return __awaiter(void 0, void 0, void 0, function () {
4611
+ return __generator(this, function (_a) {
4612
+ switch (_a.label) {
4613
+ case 0: return [4 /*yield*/, (0, utils_1.ensureAllowance)([inputCoin], [amount], curve_1.ALIASES.registry_exchange)];
4614
+ case 1: return [2 /*return*/, _a.sent()];
4615
+ }
4616
+ });
4617
+ }); };
4618
+ exports.routerExchangeApprove = routerExchangeApprove;
4619
+ var routerExchangeEstimateGas = function (inputCoin, outputCoin, amount, maxSlippage) {
4620
+ if (maxSlippage === void 0) { maxSlippage = 0.01; }
4621
+ return __awaiter(void 0, void 0, void 0, function () {
4622
+ var _a, inputCoinAddress, outputCoinAddress, _c, inputCoinDecimals, outputCoinDecimals, route, _d, _route, _swapParams, _factorySwapAddresses, _amount, minRecvAmountBN, _minRecvAmount, contract, value;
4623
+ return __generator(this, function (_e) {
4624
+ switch (_e.label) {
4625
+ case 0:
4626
+ _a = (0, utils_1._getCoinAddresses)(inputCoin, outputCoin), inputCoinAddress = _a[0], outputCoinAddress = _a[1];
4627
+ _c = (0, utils_1._getCoinDecimals)(inputCoinAddress, outputCoinAddress), inputCoinDecimals = _c[0], outputCoinDecimals = _c[1];
4628
+ return [4 /*yield*/, _getBestRouteAndOutput(inputCoinAddress, outputCoinAddress, amount)];
4629
+ case 1:
4630
+ route = _e.sent();
4631
+ if (route.steps.length === 0) {
4632
+ throw new Error("This pair can't be exchanged");
4633
+ }
4634
+ _d = _getExchangeMultipleArgs(inputCoinAddress, route), _route = _d._route, _swapParams = _d._swapParams, _factorySwapAddresses = _d._factorySwapAddresses;
4635
+ _amount = ethers_1.ethers.utils.parseUnits(amount, inputCoinDecimals);
4636
+ minRecvAmountBN = (0, utils_1.toBN)(route._output, outputCoinDecimals).times(1 - maxSlippage);
4637
+ _minRecvAmount = (0, utils_1.fromBN)(minRecvAmountBN, outputCoinDecimals);
4638
+ contract = curve_1.curve.contracts[curve_1.ALIASES.registry_exchange].contract;
4639
+ value = (0, utils_1.isEth)(inputCoinAddress) ? _amount : ethers_1.ethers.BigNumber.from(0);
4640
+ return [4 /*yield*/, curve_1.curve.updateFeeData()];
4641
+ case 2:
4642
+ _e.sent();
4643
+ return [4 /*yield*/, contract.estimateGas.exchange_multiple(_route, _swapParams, _amount, _minRecvAmount, _factorySwapAddresses, __assign(__assign({}, curve_1.curve.constantOptions), { value: value }))];
4644
+ case 3: return [2 /*return*/, (_e.sent()).toNumber()];
4645
+ }
4646
+ });
4647
+ });
4648
+ };
4649
+ exports.routerExchangeEstimateGas = routerExchangeEstimateGas;
4650
+ var routerExchange = function (inputCoin, outputCoin, amount, maxSlippage) {
4651
+ if (maxSlippage === void 0) { maxSlippage = 0.01; }
4652
+ return __awaiter(void 0, void 0, void 0, function () {
4653
+ var _a, inputCoinAddress, outputCoinAddress, _c, inputCoinDecimals, outputCoinDecimals, route, _d, _route, _swapParams, _factorySwapAddresses, _amount, minRecvAmountBN, _minRecvAmount, contract, value, gasLimit;
4654
+ return __generator(this, function (_e) {
4655
+ switch (_e.label) {
4656
+ case 0:
4657
+ _a = (0, utils_1._getCoinAddresses)(inputCoin, outputCoin), inputCoinAddress = _a[0], outputCoinAddress = _a[1];
4658
+ _c = (0, utils_1._getCoinDecimals)(inputCoinAddress, outputCoinAddress), inputCoinDecimals = _c[0], outputCoinDecimals = _c[1];
4659
+ return [4 /*yield*/, (0, utils_1.ensureAllowance)([inputCoin], [amount], curve_1.ALIASES.registry_exchange)];
4660
+ case 1:
4661
+ _e.sent();
4662
+ return [4 /*yield*/, _getBestRouteAndOutput(inputCoinAddress, outputCoinAddress, amount)];
4663
+ case 2:
4664
+ route = _e.sent();
4665
+ if (route.steps.length === 0) {
4666
+ throw new Error("This pair can't be exchanged");
4667
+ }
4668
+ _d = _getExchangeMultipleArgs(inputCoinAddress, route), _route = _d._route, _swapParams = _d._swapParams, _factorySwapAddresses = _d._factorySwapAddresses;
4669
+ _amount = ethers_1.ethers.utils.parseUnits(amount, inputCoinDecimals);
4670
+ minRecvAmountBN = (0, utils_1.toBN)(route._output, outputCoinDecimals).times(1 - maxSlippage);
4671
+ _minRecvAmount = (0, utils_1.fromBN)(minRecvAmountBN, outputCoinDecimals);
4672
+ contract = curve_1.curve.contracts[curve_1.ALIASES.registry_exchange].contract;
4673
+ value = (0, utils_1.isEth)(inputCoinAddress) ? _amount : ethers_1.ethers.BigNumber.from(0);
4674
+ return [4 /*yield*/, curve_1.curve.updateFeeData()];
4675
+ case 3:
4676
+ _e.sent();
4677
+ return [4 /*yield*/, contract.estimateGas.exchange_multiple(_route, _swapParams, _amount, _minRecvAmount, _factorySwapAddresses, __assign(__assign({}, curve_1.curve.constantOptions), { value: value }))];
4678
+ case 4:
4679
+ gasLimit = (_e.sent()).mul(130).div(100);
4680
+ return [4 /*yield*/, contract.exchange_multiple(_route, _swapParams, _amount, _minRecvAmount, _factorySwapAddresses, __assign(__assign({}, curve_1.curve.options), { value: value, gasLimit: gasLimit }))];
4681
+ case 5: return [2 /*return*/, (_e.sent()).hash];
4682
+ }
4683
+ });
4684
+ });
4685
+ };
4686
+ exports.routerExchange = routerExchange;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@curvefi/api",
3
- "version": "1.22.0",
3
+ "version": "1.23.0",
4
4
  "description": "JavaScript library for curve.fi",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {