Skip to content

Commit

Permalink
Merge pull request #32 from curvefi/external-api
Browse files Browse the repository at this point in the history
External API
  • Loading branch information
Macket authored Mar 28, 2022
2 parents a560012 + 243c1d7 commit c7039cb
Show file tree
Hide file tree
Showing 8 changed files with 107 additions and 94 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,9 @@ import curve from "@curvefi/api";
(async () => {
await curve.init('JsonRpc', {}, {gasPrice: 0, maxFeePerGas: 0, maxPriorityFeePerGas: 0});
console.log(await curve.getTVL());
// 19281307454.671753
const aave = new curve.Pool('aave');
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@curvefi/api",
"version": "1.21.0",
"version": "1.22.0",
"description": "JavaScript library for curve.fi",
"main": "lib/index.js",
"scripts": {
Expand Down
19 changes: 19 additions & 0 deletions src/external-api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { IExtendedPoolDataFromApi } from "./interfaces";
import axios from "axios";
import memoize from "memoizee";

export const _getPoolsFromApi = memoize(
async (network: "ethereum" | "polygon", poolType: "main" | "crypto" | "factory" | "factory-crypto"): Promise<IExtendedPoolDataFromApi> => {
const url = `https://api.curve.fi/api/getPools/${network}/${poolType}`;
try {
const response = await axios.get(url);
return response.data.data;
} catch (err) {
return { poolData: [], tvl: 0, tvlAll: 0 };
}
},
{
promise: true,
maxAge: 5 * 60 * 1000, // 5m
}
)
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import {
getFactoryPoolList,
getCryptoFactoryPoolList,
getUsdRate,
getTVL,
} from "./utils";

async function init (
Expand Down Expand Up @@ -81,6 +82,7 @@ const curve = {
getFactoryPoolList,
getCryptoFactoryPoolList,
getUsdRate,
getTVL,
setCustomFeeData,
signerAddress: '',
chainId: 0,
Expand Down
8 changes: 8 additions & 0 deletions src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ export interface ICoinFromPoolDataApi {
address: string,
symbol: string,
decimals: string,
usdPrice: number | string,
}

export interface IPoolDataFromApi {
Expand All @@ -95,6 +96,13 @@ export interface IPoolDataFromApi {
implementation: string,
implementationAddress: string,
coins: ICoinFromPoolDataApi[],
usdTotal: number,
}

export interface IExtendedPoolDataFromApi {
poolData: IPoolDataFromApi[],
tvl?: number,
tvlAll: number,
}

export interface RewardsApyInterface {
Expand Down
26 changes: 21 additions & 5 deletions src/pools.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import axios from "axios";
import { ethers } from "ethers";
import BigNumber from 'bignumber.js'
import BigNumber from 'bignumber.js';
import { _getPoolsFromApi } from './external-api';
import {
_getCoinAddresses,
_getCoinDecimals,
Expand Down Expand Up @@ -35,7 +37,6 @@ import {
LINK_COINS_LOWER_CASE,
COINS,
} from "./curve";
import axios from "axios";


export class Pool {
Expand Down Expand Up @@ -95,7 +96,7 @@ export class Pool {
getParameters: () => Promise<{ virtualPrice: string, fee: string, adminFee: string, A: string, gamma?: string }>,
getPoolBalances: () => Promise<string[]>,
getPoolWrappedBalances: () => Promise<string[]>,
getTotalLiquidity: () => Promise<string>,
getTotalLiquidity: (useApi?: boolean) => Promise<string>,
getVolume: () => Promise<string>,
getBaseApy: () => Promise<{day: string, week: string, month: string, total: string}>,
getTokenApy: () => Promise<[baseApy: string, boostedApy: string]>,
Expand Down Expand Up @@ -307,7 +308,22 @@ export class Pool {
return _wrappedBalances.map((_b, i) => ethers.utils.formatUnits(_b, this.decimals[i]));
}

private getTotalLiquidity = async (): Promise<string> => {
private getTotalLiquidity = async (useApi = true): Promise<string> => {
if (useApi) {
const network = curve.chainId === 137 ? "polygon" : "ethereum";
const poolType = !this.isFactory && !this.isCrypto ? "main" :
!this.isFactory ? "crypto" :
!this.isCryptoFactory ? "factory" :
"factory-crypto";
const poolsData = (await _getPoolsFromApi(network, poolType)).poolData;

try {
const totalLiquidity = poolsData.filter((data) => data.address.toLowerCase() === this.swap.toLowerCase())[0].usdTotal;
return String(totalLiquidity);
} catch (err) {
console.log((err as Error).message);
}
}
const balances = await this.getPoolBalances();

const promises = [];
Expand Down Expand Up @@ -342,7 +358,7 @@ export class Pool {

private getVolume = async (): Promise<string> => {
const volume = (await this._getPoolStats()).volume;
const usdRate = (this.isCrypto || (curve.chainId === 1 && this.isFactory)) ? 1 : await _getUsdRate(this.referenceAsset);
const usdRate = (this.isCrypto || (curve.chainId === 1 && this.isFactory)) ? 1 : await _getUsdRate(this.coinAddresses[0]);

return String(volume * usdRate)
}
Expand Down
41 changes: 40 additions & 1 deletion src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import BigNumber from 'bignumber.js';
import {DictInterface, IStats } from './interfaces';
import { curve, POOLS_DATA, LP_TOKENS, GAUGES } from "./curve";
import { COINS, DECIMALS_LOWER_CASE } from "./curve";
import { _getPoolsFromApi } from "./external-api";


const ETH_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE';
Expand Down Expand Up @@ -206,9 +207,33 @@ export const getPoolNameBySwapAddress = (swapAddress: string): string => {
return Object.entries(POOLS_DATA).filter(([_, poolData]) => poolData.swap_address.toLowerCase() === swapAddress.toLowerCase())[0][0];
}

const _usdRatesCache: DictInterface<{ rate: number, time: number }> = {}
export const _getUsdPricesFromApi = async (): Promise<DictInterface<number>> => {
const network = curve.chainId === 137 ? "polygon" : "ethereum";
const promises = [
_getPoolsFromApi(network, "main"),
_getPoolsFromApi(network, "crypto"),
_getPoolsFromApi(network, "factory"),
_getPoolsFromApi(network, "factory-crypto"),
];
const allTypesExtendedPoolData = await Promise.all(promises);
const priceDict: DictInterface<number> = {};

for (const extendedPoolData of allTypesExtendedPoolData) {
for (const pool of extendedPoolData.poolData) {
for (const coin of pool.coins) {
if (typeof coin.usdPrice === "number") priceDict[coin.address.toLowerCase()] = coin.usdPrice;
}
}
}

return priceDict
}

const _usdRatesCache: DictInterface<{ rate: number, time: number }> = {}
export const _getUsdRate = async (assetId: string): Promise<number> => {
const pricesFromApi = await _getUsdPricesFromApi();
if (assetId.toLowerCase() in pricesFromApi) return pricesFromApi[assetId.toLowerCase()];

if (assetId === 'USD' || (curve.chainId === 137 && (assetId.toLowerCase() === COINS.am3crv.toLowerCase()))) return 1

let chainName = {
Expand Down Expand Up @@ -352,4 +377,18 @@ export const getCryptoFactoryPoolList = (): string[] => Object.keys(curve.consta
export const getUsdRate = async (coin: string): Promise<number> => {
const [coinAddress] = _getCoinAddresses(coin);
return await _getUsdRate(coinAddress);
}

export const getTVL = async (chainId = curve.chainId): Promise<number> => {
const network = chainId === 137 ? "polygon" : "ethereum";

const promises = [
_getPoolsFromApi(network, "main"),
_getPoolsFromApi(network, "crypto"),
_getPoolsFromApi(network, "factory"),
_getPoolsFromApi(network, "factory-crypto"),
];
const allTypesExtendedPoolData = await Promise.all(promises);

return allTypesExtendedPoolData.reduce((sum, data) => sum + (data.tvl ?? data.tvlAll), 0)
}
100 changes: 13 additions & 87 deletions test/stats.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,92 +17,15 @@ const MAIN_POOLS_ETHEREUM = [
'eurtusd', 'crveth', 'cvxeth', 'xautusd',
'spelleth', 'teth',
];
const FACTORY_POOLS_ETHEREUM = [
'ibEUR+sEUR-f', 'ibKRW+sKRW-f', 'ibEUR+sEUR-2-f',
'crvCRVsCRV-f', 'jGBP+TGBP-f', '2CRV-f',
'crvCRV-f', 'ibbtc/sbtcCRV-f', 'OUSD3CRV-f',
'aUSDC+aDAI-f', 'FEI3CRV3CRV-f', 'GrapeFUSD3CRV-f',
'SifuETH3CRV-f', 'RC_INV3CRV-f', 'RC_xRULER3CRV-f',
'RC_xCOVER3CRV-f', 'nUSD3CRV-f', 'cvxcrv-f',
'USDM3CRV-f', 'mEUR-f', 'waUSD3CRV-f',
'waBTC/sbtcCRV-f', 'DOLA3POOL3CRV-f', 'ibJPY+sJPY-f',
'ibAUD+sAUD-f', 'ibGBP+sGBP-f', 'ibCHF+sCHF-f',
'OPEN MATIC-f', 'EURN/EURT-f', 'sdCRV-f',
'BTCpx/sbtcCRV-f', 'PWRD3CRV3CRV-f', 'sansUSDT-f',
'alETH+ETH-f', '17PctCypt3CRV-f', '17PctCypt3CRV-2-f',
'tbtc2/sbtcCRV-f', 'kusd3pool3CRV-f', 'tusd3pool3CRV-f',
'PWRD3CRV-f', 'fUSD3CRV-f', 'TPD3CRV-f',
'DEI3CRV-f', 'MIM-UST-f', 'ETH/vETH2-f',
'QBITWELLS3CRV-f', 'QWell13CRV-f', 'bveCVX-CVX-f',
'UST_whv23CRV-f', 'DSU+3Crv3CRV-f', 'DSU3CRV-f',
'aETHb-f', 'D3-f', 'aMATICb-f',
'pax-usdp3CRV-f', 'ibbtc/sbtcCRV-2-f', 'fxEUR_CRV-f',
'ORK/sbtcCRV-f', 'agEUR/sEUR-f', 'ibZAR+ZARP-f',
'3DYDX3CRV-f', '3EURpool-f', 'tWETH+WETH-f',
'XSTUSD3CRV-f', 'XIM3CRV3CRV-f', 'XIM3CRV-f',
'RAMP rUSD3CRV-f', 'bhome3CRV-f', 'JPYC+ibJPY-f',
'UST-FRAX-f', 'FEIPCV-1-f', 'bentcvx-f',
'USX3CRV3CRV-f', 'ag+ib-EUR-f', 'tFRAX+FRAX-f',
'ELONXSWAP3CRV-f', 'BEAN3CRV-f', 'USDV3CRV-f',
'PARUSDC3CRV-f', 'baoUSD-3CRV-f', 'sUSD3CRV-f',
'AETHV13CRV-f',
];
const CRYPTO_FACTORY_POOLS_ETHEREUM = [
'FXSETH-fV2', 'FXSETH-2-fV2',
'FXSETH-3-fV2', 'FXSETH-4-fV2',
'BADGERWBTC-fV2', 'INVDOLA-fV2',
'RAIFRAX-fV2', 'RAIETH-fV2',
'YFIETH-fV2', 'palStkAAVE-fV2',
'DYDXETH-fV2', 'SDTETH-fV2',
'CADCUSDC-fV2', 'RAIAGEUR-fV2',
'rp-eth-fV2', 'PARUSDC-fV2',
'DUCKETH-fV2', 'BTRFLYETH-fV2',
];
const FACTORY_POOLS_COUNT_ETHEREUM = 104;
const CRYPTO_FACTORY_POOLS_COUNT_ETHEREUM = 38;

const MAIN_POOLS_POLYGON = [ 'aave', 'ren', 'atricrypto3', 'eurtusd' ];
const FACTORY_POOLS_POLYGON = [
'usdtusdt-f', 'busd3CRV-f', 'busd23CRV-f', '2BTCWETH-f',
'3EUR-f', 'MAI3CRV-f', 'maave-f', 'MAI3pool-f',
'creth-f', 'CWM-f', 'USDN3CRV-f', 'FRAX3CRV-f3CRV-f',
'ibbtc3CRV-f', 'crvmat200-f', 'rUSD3CRV-f', 'ISusdt-f',
'jSGD+XSGD-f', 'crvAUR-JRT-f', 'ibbtc3CRV-2-f', 'ibbtc3CRV-3-f',
'crv2euro-f', 'AUSD3CRV-f', 'jSGD+XSGD-2-f', 'jCAD+CADC-f',
'PK3CRV-f', 'PK3CRV-2-f', '4MT100-f', '4MT100-2-f',
'illMATIK-f', 'MEG3CRV-f', 'TEST_POOL-f', 'USDT/FLSH3CRV-f',
'moUSD3CRV-f', 'TxPUSTSw-f', 'StSwTrPo-f', '4eur-f',
'aavesarco3CRV-f', '4eur-2-f', 'PopBTCETH-f', 'CJS-f',
'wCJS-f', 'AMISam3CRV-f', 'AMISamUEM-f', 'AMISamUEB-f',
'ATETRACRYP-f', 'am3CRVAMIS-f', 'sMVI3CRV-f', 'bMVI3CRV-f',
'3CRVUAMIS-f', 'aTri TONI3CRV-f', 'bBtc TONI3CRV-f', 'bBtc MARCO3CRV-f',
'bTri MARCO3CRV-f', 'am3CRVItal-f', '2JPY-f', '2JPY-2-f',
'jEUR-4eur-f', 'jEUR-4eur-2-f', 'AUSDAM3Crv3CRV-f', 'DOGGYUP3CRV-f',
'DOGGYUP3CRV-2-f', 'aTest3CRV-f', 'PYD3CRV-f', 'renDAI-f',
'renETH-f', 'renUSDC-f', 'renUSDT-f', 'LAMBO-USDT-f',
'LAMBO-USDT-2-f', 'PYD3CRV-2-f', 'OOPSPoS-f', 'BRNR-MATIC-f',
'agEUR+4eur-f', 'xDRINK-f', 'LUK3CRV-f', 'USDC+UST-f',
'wTEST-f', 'fx5eur-f', '2chf-f', '2aud-f',
'2php-f', '2php-2-f', '2aud-2-f', 'fx5eur-2-f',
'2chf-2-f', '2jpy-f', 'crv3X-f', 'DeiUsdc3CRV-f',
'DeiUsdc3CRV-2-f', 'NEXP-f', 'wust3CRV-f', 'fxEUR_4eur-f',
'fxAUD_jAUD-f', 'izumi-f', 'MAI3CRV-2-f', 'MAI3CRV-3-f',
'MAI3CRV-4-f', 'MAI3CRV-5-f', 'MAI3CRV-6-f', 'MAI33CRV-f',
'fxEUR_jEUR-f', 'Corinthian3CRV-f', 'cvxfxs-f-f', 'SUSHPOOOL3CRV-f',
'cOMI-f', 'cOMI-2-f', 'MAI3Pool3CRV-f', 'MAI+3Pool3CRV-f',
'cvxcrv-f', 'MAI33CRV-2-f', 'Crypl3CRV-f', 'deUSDC-3P3CRV-f',
'ABC3CRV3CRV-f', 'CRVALRTO-f', 'SoDeDAI3CRV-f', 'SoDeDAI3CRV-2-f',
'SoDeDAI3CRV-3-f', 'SoHeCRV-f', 'CURVERTO3CRV-f', 'CURVERTO3CRV-2-f',
'AlertoCRV3-f', 'crv3erto3CRV-f', 'AlertoCrv33CRV-f', 'LertoMatic-f',
'MATIC/ALRT-f', 'MATIC/ALRT-2-f', 'MATIC/ALRT-3-f', 'Makerto-f',
'Makerto-2-f', 'Binagon-f', 'BNBMATIC-f', 'BNBMATIC-2-f',
'Binagon-2-f', 'IDEXUSDT3CRV-f', 'IDEXUSDT3CRV-2-f', '196.967-f',
'107-86-f', '33373430-f', 'SPARK-f', 'Lithereum-f',
'Lithereum3CRV-f', 'PSOLM3CRV-f', 'PSOLM3CRV-2-f', 'PSOLM-f',
'PSOLM3CRV-3-f', 'PSOLM3CRV-4-f',
];
const FACTORY_POOLS_COUNT_POLYGON = 213;


const checkNumber = (str: string) => {
const re = /-?\d+(\.\d+)?/g
const re = /-?\d+(\.\d+)?(e-\d+)?/g
const match = str.match(re);
return match && str === match[0]
}
Expand Down Expand Up @@ -173,19 +96,22 @@ describe('Stats test', async function () {
poolStatsTest(poolName);
}

for (const poolName of FACTORY_POOLS_ETHEREUM) {
poolStatsTest(poolName);
for (let i = 0; i < FACTORY_POOLS_COUNT_ETHEREUM; i++) {
poolStatsTest("factory-v2-" + i);
}

for (const poolName of CRYPTO_FACTORY_POOLS_ETHEREUM) {
poolStatsTest(poolName);
for (let i = 0; i < CRYPTO_FACTORY_POOLS_COUNT_ETHEREUM; i++) {
poolStatsTest("factory-crypto-" + i);
}

// for (const poolName of MAIN_POOLS_POLYGON) {
// poolStatsTest(poolName);
// }
//
// for (const poolName of FACTORY_POOLS_POLYGON) {
// poolStatsTest(poolName);
// for (let i = 0; i < FACTORY_POOLS_COUNT_POLYGON + 9; i++) {
// const blacklist = [126, 136, 155, 156, 157, 163, 187, 189, 195];
// if (blacklist.includes(i)) continue;
//
// poolStatsTest("factory-v2-" + i);
// }
})

0 comments on commit c7039cb

Please sign in to comment.