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",