From 2fd6dcb857053dad6784eab90f85f75d3c5c1273 Mon Sep 17 00:00:00 2001 From: fedorovdg <fedorovdgap@gmail.com> Date: Tue, 9 Jan 2024 23:30:43 +0400 Subject: [PATCH 1/2] fix: fixed volumes and apys api --- src/constants/volumeNetworks.ts | 13 +++++ src/external-api.ts | 97 ++++++++++++++++++++++++--------- src/interfaces.ts | 14 +++++ src/pools/PoolTemplate.ts | 62 +++++---------------- src/utils.ts | 39 +++++++------ 5 files changed, 131 insertions(+), 94 deletions(-) create mode 100644 src/constants/volumeNetworks.ts diff --git a/src/constants/volumeNetworks.ts b/src/constants/volumeNetworks.ts new file mode 100644 index 00000000..5c097f3f --- /dev/null +++ b/src/constants/volumeNetworks.ts @@ -0,0 +1,13 @@ +import {IChainId} from "../interfaces"; + +export interface IVolumeNetworks { + getVolumes: IChainId[]; + getSubgraphData: IChainId[]; + getFactoryAPYs: IChainId[]; +} + +export const volumeNetworks: IVolumeNetworks = { + getVolumes: [1,137,8453, 42161], + getSubgraphData: [10,100,250,1284,42220,43114,1313161554], + getFactoryAPYs: [56,324,2222], +} \ No newline at end of file diff --git a/src/external-api.ts b/src/external-api.ts index 21887452..986f1453 100644 --- a/src/external-api.ts +++ b/src/external-api.ts @@ -2,13 +2,13 @@ import axios from "axios"; import memoize from "memoizee"; import { IExtendedPoolDataFromApi, - ISubgraphPoolData, IDict, INetworkName, IPoolType, IGaugesDataFromApi, IDaoProposal, IDaoProposalListItem, + IVolumeAndAPYs, } from "./interfaces"; @@ -38,11 +38,21 @@ export const _getAllPoolsFromApi = async (network: INetworkName): Promise<IExten } export const _getSubgraphData = memoize( - async (network: INetworkName): Promise<{ poolsData: ISubgraphPoolData[], totalVolume: number, cryptoVolume: number, cryptoShare: number }> => { + async (network: INetworkName): Promise<IVolumeAndAPYs> => { const url = `https://api.curve.fi/api/getSubgraphData/${network}`; const response = await axios.get(url, { validateStatus: () => true }); + + const poolsData = response.data.data.poolList.map((item: any) => { + return { + address: item.address, + volumeUSD: item.volumeUSD, + day: item.latestDailyApy, + week: item.latestWeeklyApy, + } + }) + return { - poolsData: response.data.data.poolList ?? [], + poolsData: poolsData ?? [], totalVolume: response.data.data.totalVolume ?? 0, cryptoVolume: response.data.data.cryptoVolume ?? 0, cryptoShare: response.data.data.cryptoShare ?? 0, @@ -54,21 +64,27 @@ export const _getSubgraphData = memoize( } ) -// Moonbeam and Aurora only -export const _getLegacyAPYsAndVolumes = memoize( - async (network: string): Promise<IDict<{ apy: { day: number, week: number }, volume: number }>> => { - if (["kava", "celo", "zksync", "base", "bsc"].includes(network)) return {}; // Exclude Kava, Celo, ZkSync, Base and Bsc - const url = "https://api.curve.fi/api/getMainPoolsAPYs/" + network; - const data = (await axios.get(url, { validateStatus: () => true })).data; - const result: IDict<{ apy: { day: number, week: number }, volume: number }> = {}; - Object.keys(data.apy.day).forEach((poolId) => { - result[poolId] = { apy: { day: 0, week: 0 }, volume: 0}; - result[poolId].apy.day = data.apy.day[poolId] * 100; - result[poolId].apy.week = data.apy.week[poolId] * 100; - result[poolId].volume = data.volume[poolId]; +export const _getVolumes = memoize( + async (network: string): Promise<IVolumeAndAPYs> => { + + const url = `https://api.curve.fi/api/getVolumes/${network}`; + const response = await axios.get(url, { validateStatus: () => true }); + + const poolsData = response.data.data.pools.map((item: any) => { + return { + address: item.address, + volumeUSD: item.volumeUSD, + day: item.latestDailyApyPcent, + week: item.latestWeeklyApyPcent, + } }) - return result; + return { + poolsData: poolsData ?? [], + totalVolume: response.data.data.totalVolumes.totalVolume ?? 0, + cryptoVolume: response.data.data.totalVolumes.totalCryptoVolume ?? 0, + cryptoShare: response.data.data.totalVolumes.cryptoVolumeSharePcent ?? 0, + }; }, { promise: true, @@ -76,15 +92,33 @@ export const _getLegacyAPYsAndVolumes = memoize( } ) -// Base, Bsc, ZkSync, Moonbeam, Kava and Celo only -export const _getFactoryAPYsAndVolumes = memoize( - async (network: string, mode: 'stable' | 'crypto' = 'stable'): Promise<{ poolAddress: string, apy: number, volume: number }[]> => { - if (network === "aurora") return []; // Exclude Aurora - - const url = `https://api.curve.fi/api/getFactoryAPYs/${network}/${mode}`; - const response = await axios.get(url, { validateStatus: () => true }); +export const _getFactoryAPYs = memoize( + async (network: string): Promise<IVolumeAndAPYs> => { + const urlStable = `getFactoryAPYs/${network}/stable}`; + const urlCrypto = `getFactoryAPYs/${network}/crypto}`; + const response = await Promise.all([ + axios.get(urlStable, { validateStatus: () => true }), + axios.get(urlCrypto, { validateStatus: () => true }), + ]); + + const stableVolume = response[0].data.data.totalVolumeUsd || response[0].data.data.totalVolume; + const cryptoVolume = response[1].data.data.totalVolumeUsd || response[1].data.data.totalVolume; + + const poolsData = [...response[0].data.data.pools, ...response[1].data.data.pools].map((item) => { + return { + address: item.poolAddress, + volumeUSD: item.totalVolumeUsd, + day: item.apy, + week: item.apy*7, //Because api does not return week apy + } + }) - return response.data.data.poolDetails ?? []; + return { + poolsData: poolsData ?? [], + totalVolume: stableVolume + cryptoVolume ?? 0, + cryptoVolume: cryptoVolume ?? 0, + cryptoShare: 100*cryptoVolume/(stableVolume + cryptoVolume) ?? 0, + }; }, { promise: true, @@ -92,11 +126,20 @@ export const _getFactoryAPYsAndVolumes = memoize( } ) +//4 export const _getTotalVolumes = memoize( - async (network: string, mode: 'stable' | 'crypto' = 'stable'): Promise<{ totalVolumeUsd: number}> => { - if (network === "aurora") return {totalVolumeUsd: 0}; // Exclude Aurora + async (network: string): Promise<{ + totalVolume: number; + cryptoVolume: number; + cryptoShare: number; + }> => { + if (network === "aurora") return { + totalVolume: 0, + cryptoVolume: 0, + cryptoShare: 0, + }; // Exclude Aurora - const url = `https://api.curve.fi/api/getFactoryAPYs/${network}/${mode}`; + const url = `https://api.curve.fi/api/getSubgraphData/${network}`; const response = await axios.get(url, { validateStatus: () => true }); return response.data.data; diff --git a/src/interfaces.ts b/src/interfaces.ts index b9aadf70..0fd11fbb 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -250,4 +250,18 @@ export interface IDaoProposal extends IDaoProposalListItem{ creatorVotingPower: number, script: string, votes: IDaoProposalVote[], +} + +export interface IVolumeAndAPYsPoolData { + address: string, + volumeUSD: number, + day: number, + week: number, +} + +export interface IVolumeAndAPYs { + totalVolume: number, + cryptoVolume: number, + cryptoShare: number, + poolsData: IVolumeAndAPYsPoolData[], } \ No newline at end of file diff --git a/src/pools/PoolTemplate.ts b/src/pools/PoolTemplate.ts index 553ee5c2..4dabb228 100644 --- a/src/pools/PoolTemplate.ts +++ b/src/pools/PoolTemplate.ts @@ -1,6 +1,6 @@ import BigNumber from 'bignumber.js'; import memoize from "memoizee"; -import { _getPoolsFromApi, _getSubgraphData, _getFactoryAPYsAndVolumes, _getLegacyAPYsAndVolumes } from '../external-api.js'; +import { _getPoolsFromApi } from '../external-api.js'; import { _getCoinAddresses, _getBalances, @@ -28,6 +28,7 @@ import { DIGas, _getAddress, isMethodExist, + getVolumeApiController, } from '../utils.js'; import { IDict, IReward, IProfit, IPoolType } from '../interfaces'; import { curve } from "../curve.js"; @@ -417,62 +418,29 @@ export class PoolTemplate { } private statsVolume = async (): Promise<string> => { - if ([56, 324, 1284, 2222, 8453, 42220, 1313161554].includes(curve.chainId)) { // Bsc || ZkSync || Moonbeam || Kava || Base || Celo || Aurora || Bsc - const _response = await Promise.all([ - _getLegacyAPYsAndVolumes(curve.constants.NETWORK_NAME), - _getFactoryAPYsAndVolumes(curve.constants.NETWORK_NAME, 'stable'), - _getFactoryAPYsAndVolumes(curve.constants.NETWORK_NAME, 'crypto'), - ]); - const [mainPoolsData, factoryPoolsData] = [_response[0], [..._response[1], ..._response[2]]]; - if (this.id in mainPoolsData) { - return (mainPoolsData[this.id].volume ?? 0).toString(); - } - const poolData = factoryPoolsData.find((d) => d.poolAddress.toLowerCase() === this.address); - if (!poolData) throw Error(`Can't get Volume for ${this.name} (id: ${this.id})`) - const lpPrice = await _getUsdRate(this.lpToken); - - return (poolData.volume * lpPrice).toString() - } const network = curve.constants.NETWORK_NAME; - const poolsData = (await _getSubgraphData(network)).poolsData; + const {poolsData} = await getVolumeApiController(network); const poolData = poolsData.find((d) => d.address.toLowerCase() === this.address); - if (!poolData) throw Error(`Can't get Volume for ${this.name} (id: ${this.id})`) - return poolData.volumeUSD.toString() + if(poolData) { + return poolData.volumeUSD.toString() + } + + throw Error(`Can't get Volume for ${this.name} (id: ${this.id})`) } private statsBaseApy = async (): Promise<{ day: string, week: string }> => { - if ([56, 324, 1284, 2222, 8453, 42220, 1313161554].includes(curve.chainId)) { // Bsc || ZkSync || Moonbeam || Kava || Base || Celo || Aurora - const _response = await Promise.all([ - _getLegacyAPYsAndVolumes(curve.constants.NETWORK_NAME), - _getFactoryAPYsAndVolumes(curve.constants.NETWORK_NAME, 'stable'), - _getFactoryAPYsAndVolumes(curve.constants.NETWORK_NAME, 'crypto'), - ]); - const [mainPoolsData, factoryPoolsData] = [_response[0], [..._response[1], ..._response[2]]]; - if (this.id in mainPoolsData) { - return { - day: mainPoolsData[this.id].apy.day.toString(), - week: mainPoolsData[this.id].apy.week.toString(), - } - } - const poolData = factoryPoolsData.find((d) => d.poolAddress.toLowerCase() === this.address); - if (!poolData) throw Error(`Can't get base APY for ${this.name} (id: ${this.id})`) - - return { - day: poolData.apy.toString(), - week: poolData.apy.toString(), - } - } const network = curve.constants.NETWORK_NAME; - const poolsData = (await _getSubgraphData(network)).poolsData; + const {poolsData} = await getVolumeApiController(network); const poolData = poolsData.find((d) => d.address.toLowerCase() === this.address); - if (!poolData) throw Error(`Can't get base APY for ${this.name} (id: ${this.id})`) - - return { - day: poolData.latestDailyApy.toString(), - week: poolData.latestWeeklyApy.toString(), + if(poolData) { + return { + day: poolData.day.toString(), + week: poolData.week.toString(), + } } + throw Error(`Can't get base APY for ${this.name} (id: ${this.id})`) } private _calcTokenApy = async (futureWorkingSupplyBN: BigNumber | null = null): Promise<[baseApy: number, boostedApy: number]> => { diff --git a/src/utils.ts b/src/utils.ts index 8dbfc00d..6a843ce4 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -2,17 +2,17 @@ import axios from 'axios'; import { Contract } from 'ethers'; import { Contract as MulticallContract } from "ethcall"; import BigNumber from 'bignumber.js'; -import {IChainId, IDict, INetworkName, IRewardFromApi, REFERENCE_ASSET} from './interfaces'; +import {IChainId, IDict, INetworkName, IRewardFromApi, IVolumeAndAPYs, REFERENCE_ASSET} from './interfaces'; import { curve, NETWORK_CONSTANTS } from "./curve.js"; import { - _getFactoryAPYsAndVolumes, - _getLegacyAPYsAndVolumes, _getAllPoolsFromApi, + _getFactoryAPYs, _getSubgraphData, - _getTotalVolumes, + _getVolumes, } from "./external-api.js"; import ERC20Abi from './constants/abis/ERC20.json' assert { type: 'json' }; import { L2Networks } from './constants/L2Networks.js'; +import {volumeNetworks} from "./constants/volumeNetworks"; export const ETH_ADDRESS = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; @@ -558,24 +558,23 @@ export const getTVL = async (network: INetworkName | IChainId = curve.chainId): return allTypesExtendedPoolData.reduce((sum, data) => sum + (data.tvl ?? data.tvlAll ?? 0), 0) } -export const getVolume = async (network: INetworkName | IChainId = curve.chainId): Promise<{ totalVolume: number, cryptoVolume: number, cryptoShare: number }> => { - network = _getNetworkName(network); - if (["zksync", "moonbeam", "kava", "base", "celo", "aurora", "bsc"].includes(network)) { - const chainId = _getChainId(network); - if (curve.chainId !== chainId) throw Error("To get volume for ZkSync, Moonbeam, Kava, Base, Celo, Aurora or Bsc connect to the network first"); - - - const [factoryPoolsData, cryptoPoolsData] = await Promise.all([ - _getTotalVolumes(network, 'stable'), - _getTotalVolumes(network, 'crypto'), - ]); - const stableVolume = factoryPoolsData.totalVolumeUsd; - const cryptoVolume = cryptoPoolsData.totalVolumeUsd; - - return { totalVolume: stableVolume + cryptoVolume, cryptoVolume: cryptoVolume, cryptoShare: cryptoVolume/(stableVolume + cryptoVolume) } +export const getVolumeApiController = async (network: INetworkName): Promise<IVolumeAndAPYs> => { + if(volumeNetworks.getVolumes.includes(curve.chainId)) { + return await _getVolumes(network); + } + if(volumeNetworks.getFactoryAPYs.includes(curve.chainId)) { + return await _getFactoryAPYs(network); } + if(volumeNetworks.getSubgraphData.includes(curve.chainId)) { + return await _getSubgraphData(network); + } + + throw Error(`Can't get volume for network: ${network}`); +} - const { totalVolume, cryptoVolume, cryptoShare } = await _getSubgraphData(network); +export const getVolume = async (network: INetworkName | IChainId = curve.chainId): Promise<{ totalVolume: number, cryptoVolume: number, cryptoShare: number }> => { + network = _getNetworkName(network); + const { totalVolume, cryptoVolume, cryptoShare } = await getVolumeApiController(network); return { totalVolume, cryptoVolume, cryptoShare } } From 12ea37bdd119da36a7466a93d98a48a984743ae1 Mon Sep 17 00:00:00 2001 From: fedorovdg <fedorovdgap@gmail.com> Date: Tue, 9 Jan 2024 23:36:23 +0400 Subject: [PATCH 2/2] build: v2.53.8 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 74b92580..7d5be66b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@curvefi/api", - "version": "2.53.7", + "version": "2.53.8", "description": "JavaScript library for curve.fi", "main": "lib/index.js", "author": "Macket",