@curvefi/api 2.31.1 → 2.32.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/router.js CHANGED
@@ -1,226 +1,11 @@
1
1
  import axios from "axios";
2
2
  import memoize from "memoizee";
3
+ import { findAllRoutes } from "@curvefi/router";
3
4
  import { ethers } from "ethers";
4
5
  import { curve } from "./curve.js";
5
6
  import { _getCoinAddresses, _getCoinDecimals, _getUsdRate, ensureAllowance, ensureAllowanceEstimateGas, fromBN, hasAllowance, isEth, toBN, BN, parseUnits, _cutZeros, ETH_ADDRESS, _get_small_x, _get_price_impact, } from "./utils.js";
6
7
  import { getPool } from "./pools/index.js";
7
8
  import { _getAmplificationCoefficientsFromApi } from "./pools/utils.js";
8
- const getNewRoute = (routeTvl, poolId, poolAddress, inputCoinAddress, outputCoinAddress, i, j, swapType, swapAddress, tvl) => {
9
- const routePoolIds = routeTvl.route.map((s) => s.poolId);
10
- // Steps <= 4
11
- if (routePoolIds.length >= 4)
12
- return { route: [], minTvl: Infinity, totalTvl: 0 };
13
- // Exclude such cases as cvxeth -> tricrypto2 -> tricrypto2 -> susd
14
- if (routePoolIds.includes(poolId))
15
- return { route: [], minTvl: Infinity, totalTvl: 0 };
16
- return {
17
- route: [...routeTvl.route, { poolId, poolAddress, inputCoinAddress, outputCoinAddress, i, j, swapType, swapAddress }],
18
- minTvl: Math.min(tvl, routeTvl.minTvl),
19
- totalTvl: routeTvl.totalTvl + tvl,
20
- };
21
- };
22
- // TODO REMOVE IT!!!
23
- const filterMaticFactory83Route = (routes) => {
24
- return routes.filter((r) => {
25
- for (const step of r.route) {
26
- if (step.poolId === "factory-crypto-83" && step.inputCoinAddress === curve.constants.NATIVE_TOKEN.address)
27
- return false;
28
- }
29
- return true;
30
- });
31
- };
32
- // TODO REMOVE IT!!!
33
- const filterAvax = (routes) => {
34
- return routes.filter((r) => {
35
- for (const step of r.route) {
36
- if (step.poolId == 'avaxcrypto' && step.swapType == 4 && (step.i === 3 || step.j === 3))
37
- return false;
38
- }
39
- return true;
40
- });
41
- };
42
- const MAX_ROUTES_FOR_ONE_COIN = 3;
43
- const filterRoutes = (routes, inputCoinAddress, sortFn) => {
44
- // TODO REMOVE IT!!!
45
- if (curve.chainId === 137)
46
- routes = filterMaticFactory83Route(routes);
47
- if (curve.chainId === 43114)
48
- routes = filterAvax(routes);
49
- return routes
50
- .filter((r) => r.route.length > 0)
51
- .filter((r) => r.route[0].inputCoinAddress === inputCoinAddress) // Truncated routes
52
- .filter((r, i, _routes) => {
53
- const routesByPoolIds = _routes.map((r) => r.route.map((s) => s.poolId).toString());
54
- return routesByPoolIds.indexOf(r.route.map((s) => s.poolId).toString()) === i;
55
- }) // Route duplications
56
- .sort(sortFn).slice(0, MAX_ROUTES_FOR_ONE_COIN);
57
- };
58
- const sortByTvl = (a, b) => b.minTvl - a.minTvl || b.totalTvl - a.totalTvl || a.route.length - b.route.length;
59
- const sortByLength = (a, b) => a.route.length - b.route.length || b.minTvl - a.minTvl || b.totalTvl - a.totalTvl;
60
- // Inspired by Dijkstra's algorithm
61
- const _findAllRoutes = async (inputCoinAddress, outputCoinAddress) => {
62
- inputCoinAddress = inputCoinAddress.toLowerCase();
63
- outputCoinAddress = outputCoinAddress.toLowerCase();
64
- const ALL_POOLS = Object.entries({
65
- ...curve.constants.POOLS_DATA,
66
- ...curve.constants.FACTORY_POOLS_DATA,
67
- ...curve.constants.CRYPTO_FACTORY_POOLS_DATA,
68
- });
69
- const amplificationCoefficientDict = await _getAmplificationCoefficientsFromApi();
70
- // Coins we are searching routes for on the current step
71
- let curCoins = [inputCoinAddress];
72
- // Coins we will search routes for on the next step
73
- let nextCoins = new Set();
74
- // Routes for all coins found
75
- const routesByTvl = {
76
- [inputCoinAddress]: [{ route: [], minTvl: Infinity, totalTvl: 0 }],
77
- };
78
- const routesByLength = {
79
- [inputCoinAddress]: [{ route: [], minTvl: Infinity, totalTvl: 0 }],
80
- };
81
- // No more than 4 steps (swaps)
82
- for (let step = 0; step < 4; step++) {
83
- for (const inCoin of curCoins) {
84
- if (curve.chainId !== 42220 && [curve.constants.NATIVE_TOKEN.address, curve.constants.NATIVE_TOKEN.wrappedAddress].includes(inCoin)) { // Exclude Celo
85
- const outCoin = inCoin === curve.constants.NATIVE_TOKEN.address ? curve.constants.NATIVE_TOKEN.wrappedAddress : curve.constants.NATIVE_TOKEN.address;
86
- const newRoutesByTvl = routesByTvl[inCoin].map((route) => getNewRoute(route, "wrapper", curve.constants.NATIVE_TOKEN.wrappedAddress, inCoin, outCoin, 0, 0, 15, curve.constants.ZERO_ADDRESS, Infinity));
87
- const newRoutesByLength = routesByLength[inCoin].map((route) => getNewRoute(route, "wrapper", curve.constants.NATIVE_TOKEN.wrappedAddress, inCoin, outCoin, 0, 0, 15, curve.constants.ZERO_ADDRESS, Infinity));
88
- routesByTvl[outCoin] = [...(routesByTvl[outCoin] ?? []), ...newRoutesByTvl];
89
- routesByTvl[outCoin] = filterRoutes(routesByTvl[outCoin], inputCoinAddress, sortByTvl);
90
- routesByLength[outCoin] = [...(routesByLength[outCoin] ?? []), ...newRoutesByLength];
91
- routesByLength[outCoin] = filterRoutes(routesByLength[outCoin], inputCoinAddress, sortByLength);
92
- nextCoins.add(outCoin);
93
- }
94
- for (const [poolId, poolData] of ALL_POOLS) {
95
- const wrapped_coin_addresses = poolData.wrapped_coin_addresses.map((a) => a.toLowerCase());
96
- const underlying_coin_addresses = poolData.underlying_coin_addresses.map((a) => a.toLowerCase());
97
- const base_pool = poolData.is_meta ? curve.constants.POOLS_DATA[poolData.base_pool] : null;
98
- const meta_coin_addresses = base_pool ? base_pool.underlying_coin_addresses.map((a) => a.toLowerCase()) : [];
99
- const token_address = poolData.token_address.toLowerCase();
100
- const is_aave_like_lending = poolData.is_lending && wrapped_coin_addresses.length === 3 && !poolData.deposit_address;
101
- const tvlMultiplier = poolData.is_crypto ? 1 : (amplificationCoefficientDict[poolData.swap_address] ?? 1);
102
- const inCoinIndexes = {
103
- wrapped_coin: wrapped_coin_addresses.indexOf(inCoin),
104
- underlying_coin: underlying_coin_addresses.indexOf(inCoin),
105
- meta_coin: meta_coin_addresses ? meta_coin_addresses.indexOf(inCoin) : -1,
106
- };
107
- // Skip pools which don't contain inCoin
108
- if (inCoinIndexes.wrapped_coin === -1 && inCoinIndexes.underlying_coin === -1 && inCoinIndexes.meta_coin === -1 && inCoin !== token_address)
109
- continue;
110
- const tvl = Number(await (getPool(poolId)).stats.totalLiquidity()) * tvlMultiplier;
111
- // Skip empty pools
112
- if (tvl === 0)
113
- continue;
114
- let poolAddress = poolData.is_fake ? poolData.deposit_address : poolData.swap_address;
115
- const coin_addresses = (is_aave_like_lending || poolData.is_fake) ? underlying_coin_addresses : wrapped_coin_addresses;
116
- // LP -> wrapped coin (underlying for lending or fake pool) "swaps" (actually remove_liquidity_one_coin)
117
- if (coin_addresses.length < 6 && inCoin === token_address) {
118
- for (let j = 0; j < coin_addresses.length; j++) {
119
- // Looking for outputCoinAddress only on the final step
120
- if (step === 3 && coin_addresses[j] !== outputCoinAddress)
121
- continue;
122
- // Exclude such cases as cvxeth -> tricrypto2 -> tusd -> susd or cvxeth -> tricrypto2 -> susd -> susd
123
- const outputCoinIdx = coin_addresses.indexOf(outputCoinAddress);
124
- if (outputCoinIdx >= 0 && j !== outputCoinIdx)
125
- continue;
126
- const swapType = poolData.is_crypto ? 14 : is_aave_like_lending ? 13 : 12;
127
- const newRoutesByTvl = routesByTvl[inCoin].map((route) => getNewRoute(route, poolId, poolAddress, inCoin, coin_addresses[j], 0, j, swapType, curve.constants.ZERO_ADDRESS, tvl));
128
- const newRoutesByLength = routesByLength[inCoin].map((route) => getNewRoute(route, poolId, poolAddress, inCoin, coin_addresses[j], 0, j, swapType, curve.constants.ZERO_ADDRESS, tvl));
129
- routesByTvl[coin_addresses[j]] = [...(routesByTvl[coin_addresses[j]] ?? []), ...newRoutesByTvl];
130
- routesByTvl[coin_addresses[j]] = filterRoutes(routesByTvl[coin_addresses[j]], inputCoinAddress, sortByTvl);
131
- routesByLength[coin_addresses[j]] = [...(routesByLength[coin_addresses[j]] ?? []), ...newRoutesByLength];
132
- routesByLength[coin_addresses[j]] = filterRoutes(routesByLength[coin_addresses[j]], inputCoinAddress, sortByLength);
133
- nextCoins.add(coin_addresses[j]);
134
- }
135
- }
136
- // Wrapped coin (underlying for lending or fake pool) -> LP "swaps" (actually add_liquidity)
137
- const inCoinIndex = (is_aave_like_lending || poolData.is_fake) ? inCoinIndexes.underlying_coin : inCoinIndexes.wrapped_coin;
138
- if (coin_addresses.length < 6 && inCoinIndex >= 0) {
139
- // Looking for outputCoinAddress only on the final step
140
- if (!(step === 3 && token_address !== outputCoinAddress)) {
141
- const swapType = is_aave_like_lending ? 9
142
- : coin_addresses.length === 2 ? 7
143
- : coin_addresses.length === 3 ? 8
144
- : coin_addresses.length === 4 ? 10 : 11;
145
- const newRoutesByTvl = routesByTvl[inCoin].map((route) => getNewRoute(route, poolId, poolAddress, inCoin, token_address, coin_addresses.indexOf(inCoin), 0, swapType, curve.constants.ZERO_ADDRESS, tvl));
146
- const newRoutesByLength = routesByLength[inCoin].map((route) => getNewRoute(route, poolId, poolAddress, inCoin, token_address, coin_addresses.indexOf(inCoin), 0, swapType, curve.constants.ZERO_ADDRESS, tvl));
147
- routesByTvl[token_address] = [...(routesByTvl[token_address] ?? []), ...newRoutesByTvl];
148
- routesByTvl[token_address] = filterRoutes(routesByTvl[token_address], inputCoinAddress, sortByTvl);
149
- routesByLength[token_address] = [...(routesByLength[token_address] ?? []), ...newRoutesByLength];
150
- routesByLength[token_address] = filterRoutes(routesByLength[token_address], inputCoinAddress, sortByLength);
151
- nextCoins.add(token_address);
152
- }
153
- }
154
- // Wrapped swaps
155
- if (inCoinIndexes.wrapped_coin >= 0 && !poolData.is_fake) {
156
- for (let j = 0; j < wrapped_coin_addresses.length; j++) {
157
- if (j === inCoinIndexes.wrapped_coin)
158
- continue;
159
- // Native swaps spend less gas
160
- // TODO uncomment
161
- // if (wrapped_coin_addresses[j] !== outputCoinAddress && wrapped_coin_addresses[j] === curve.constants.NATIVE_TOKEN.wrappedAddress) continue;
162
- // Looking for outputCoinAddress only on the final step
163
- if (step === 3 && wrapped_coin_addresses[j] !== outputCoinAddress)
164
- continue;
165
- // Exclude such cases as cvxeth -> tricrypto2 -> tusd -> susd or cvxeth -> tricrypto2 -> susd -> susd
166
- const outputCoinIdx = wrapped_coin_addresses.indexOf(outputCoinAddress);
167
- if (outputCoinIdx >= 0 && j !== outputCoinIdx)
168
- continue;
169
- const swapType = poolData.is_crypto ? 3 : 1;
170
- const newRoutesByTvl = routesByTvl[inCoin].map((route) => getNewRoute(route, poolId, poolData.swap_address, inCoin, wrapped_coin_addresses[j], inCoinIndexes.wrapped_coin, j, swapType, curve.constants.ZERO_ADDRESS, tvl));
171
- const newRoutesByLength = routesByLength[inCoin].map((route) => getNewRoute(route, poolId, poolData.swap_address, inCoin, wrapped_coin_addresses[j], inCoinIndexes.wrapped_coin, j, swapType, curve.constants.ZERO_ADDRESS, tvl));
172
- routesByTvl[wrapped_coin_addresses[j]] = [...(routesByTvl[wrapped_coin_addresses[j]] ?? []), ...newRoutesByTvl];
173
- routesByTvl[wrapped_coin_addresses[j]] = filterRoutes(routesByTvl[wrapped_coin_addresses[j]], inputCoinAddress, sortByTvl);
174
- routesByLength[wrapped_coin_addresses[j]] = [...(routesByLength[wrapped_coin_addresses[j]] ?? []), ...newRoutesByLength];
175
- routesByLength[wrapped_coin_addresses[j]] = filterRoutes(routesByLength[wrapped_coin_addresses[j]], inputCoinAddress, sortByLength);
176
- nextCoins.add(wrapped_coin_addresses[j]);
177
- }
178
- }
179
- // Only for underlying swaps
180
- poolAddress = (poolData.is_crypto && poolData.is_meta) || (base_pool?.is_lending && poolData.is_factory) ?
181
- poolData.deposit_address : poolData.swap_address;
182
- // Underlying swaps
183
- if (!poolData.is_plain && inCoinIndexes.underlying_coin >= 0) {
184
- for (let j = 0; j < underlying_coin_addresses.length; j++) {
185
- if (j === inCoinIndexes.underlying_coin)
186
- continue;
187
- // Don't swap metacoins since they can be swapped directly in base pool
188
- if (inCoinIndexes.meta_coin >= 0 && meta_coin_addresses.includes(underlying_coin_addresses[j]))
189
- continue;
190
- // Looking for outputCoinAddress only on the final step
191
- if (step === 3 && underlying_coin_addresses[j] !== outputCoinAddress)
192
- continue;
193
- // Exclude such cases as cvxeth -> tricrypto2 -> tusd -> susd or cvxeth -> tricrypto2 -> susd -> susd
194
- const outputCoinIdx = underlying_coin_addresses.indexOf(outputCoinAddress);
195
- if (outputCoinIdx >= 0 && j !== outputCoinIdx)
196
- continue;
197
- // Skip empty pools
198
- const tvl = Number(await (getPool(poolId)).stats.totalLiquidity());
199
- if (tvl === 0)
200
- continue;
201
- const hasEth = (inCoin === curve.constants.NATIVE_TOKEN.address || underlying_coin_addresses[j] === curve.constants.NATIVE_TOKEN.address);
202
- const swapType = (poolData.is_crypto && poolData.is_meta && poolData.is_factory) ? 6
203
- : (base_pool?.is_lending && poolData.is_factory) ? 5
204
- : hasEth && poolId !== 'avaxcrypto' ? 3
205
- : poolData.is_crypto ? 4
206
- : 2;
207
- const newRoutesByTvl = routesByTvl[inCoin].map((route) => getNewRoute(route, poolId, poolAddress, inCoin, underlying_coin_addresses[j], inCoinIndexes.underlying_coin, j, swapType, (swapType === 5 || swapType === 6) ? poolData.swap_address : curve.constants.ZERO_ADDRESS, tvl));
208
- const newRoutesByLength = routesByLength[inCoin].map((route) => getNewRoute(route, poolId, poolAddress, inCoin, underlying_coin_addresses[j], inCoinIndexes.underlying_coin, j, swapType, (swapType === 5 || swapType === 6) ? poolData.swap_address : curve.constants.ZERO_ADDRESS, tvl));
209
- routesByTvl[underlying_coin_addresses[j]] = [...(routesByTvl[underlying_coin_addresses[j]] ?? []), ...newRoutesByTvl];
210
- routesByTvl[underlying_coin_addresses[j]] = filterRoutes(routesByTvl[underlying_coin_addresses[j]], inputCoinAddress, sortByTvl);
211
- routesByLength[underlying_coin_addresses[j]] = [...(routesByLength[underlying_coin_addresses[j]] ?? []), ...newRoutesByLength];
212
- routesByLength[underlying_coin_addresses[j]] = filterRoutes(routesByLength[underlying_coin_addresses[j]], inputCoinAddress, sortByLength);
213
- nextCoins.add(underlying_coin_addresses[j]);
214
- }
215
- }
216
- }
217
- }
218
- curCoins = Array.from(nextCoins);
219
- nextCoins = new Set();
220
- }
221
- const routes = [...(routesByTvl[outputCoinAddress] ?? []), ...(routesByLength[outputCoinAddress] ?? [])];
222
- return routes.map((r) => r.route);
223
- };
224
9
  const _getRouteKey = (route, inputCoinAddress, outputCoinAddress) => {
225
10
  const sortedCoins = [inputCoinAddress, outputCoinAddress].sort();
226
11
  let key = `${sortedCoins[0]}-->`;
@@ -282,7 +67,18 @@ const _getBestRoute = memoize(async (inputCoinAddress, outputCoinAddress, amount
282
67
  const _amount = parseUnits(amount, inputCoinDecimals);
283
68
  if (_amount === 0n)
284
69
  return [];
285
- const routesRaw = (await _findAllRoutes(inputCoinAddress, outputCoinAddress)).map((route) => ({ route, _output: 0n, outputUsd: 0, txCostUsd: 0 }));
70
+ const pools = {
71
+ ...curve.constants.POOLS_DATA,
72
+ ...curve.constants.FACTORY_POOLS_DATA,
73
+ ...curve.constants.CRYPTO_FACTORY_POOLS_DATA,
74
+ };
75
+ const ADict = await _getAmplificationCoefficientsFromApi();
76
+ const tvlDict = {};
77
+ const poolIds = Object.keys(pools);
78
+ for (let i = 0; i < poolIds.length; i++) {
79
+ tvlDict[poolIds[i]] = Number(await (getPool(poolIds[i])).stats.totalLiquidity());
80
+ }
81
+ const routesRaw = (findAllRoutes(inputCoinAddress, outputCoinAddress, pools, ADict, curve.chainId, curve.constants.NATIVE_TOKEN, tvlDict)).map((route) => ({ route, _output: 0n, outputUsd: 0, txCostUsd: 0 }));
286
82
  const routes = [];
287
83
  try {
288
84
  const calls = [];
@@ -304,7 +100,6 @@ const _getBestRoute = memoize(async (inputCoinAddress, outputCoinAddress, amount
304
100
  const { _route, _swapParams, _factorySwapAddresses } = _getExchangeMultipleArgs(r.route);
305
101
  promises.push(contract.get_exchange_multiple_amount(_route, _swapParams, _amount, _factorySwapAddresses, curve.constantOptions));
306
102
  }
307
- // @ts-ignore
308
103
  const res = await Promise.allSettled(promises);
309
104
  for (let i = 0; i < res.length; i++) {
310
105
  if (res[i].status === 'rejected') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@curvefi/api",
3
- "version": "2.31.1",
3
+ "version": "2.32.0",
4
4
  "description": "JavaScript library for curve.fi",
5
5
  "main": "lib/index.js",
6
6
  "author": "Macket",
@@ -32,6 +32,7 @@
32
32
  "vue-eslint-parser": "^7.6.0"
33
33
  },
34
34
  "dependencies": {
35
+ "@curvefi/router": "^0.1.0",
35
36
  "axios": "^0.21.1",
36
37
  "bignumber.js": "^9.0.1",
37
38
  "ethcall": "^6.0.0",