From e5bdc10d8e7d35eb20c96acd6515cee7dc1d332f Mon Sep 17 00:00:00 2001 From: qgatssdev Date: Wed, 13 Nov 2024 22:38:33 +0100 Subject: [PATCH 1/3] feat: add 7d fees and apy to pools table --- src/components/pools-table/pools-table.tsx | 16 ++++++++++++++++ src/types/pools.ts | 2 ++ src/utils/info/pools/index.ts | 5 +++++ 3 files changed, 23 insertions(+) diff --git a/src/components/pools-table/pools-table.tsx b/src/components/pools-table/pools-table.tsx index f298843..981945c 100644 --- a/src/components/pools-table/pools-table.tsx +++ b/src/components/pools-table/pools-table.tsx @@ -57,11 +57,21 @@ const headCells: readonly HeadCell[] = [ numeric: true, label: "Fees 24H", }, + { + id: "fees7d", + numeric: true, + label: "Fees 7D", + }, { id: "feesYearly", numeric: true, label: "Fees Yearly", }, + { + id: "apy", + numeric: true, + label: "APY", + }, ]; interface PoolsTableProps { @@ -218,9 +228,15 @@ export default function PoolsTable({ {formatNumberToMoney(row.fees24h)} + + {formatNumberToMoney(row.fees7d)} + {formatNumberToMoney(row.feesYearly)} + + {formatNumberToMoney(row.apy)} + ); })} diff --git a/src/types/pools.ts b/src/types/pools.ts index 7dfd2e9..461db0d 100644 --- a/src/types/pools.ts +++ b/src/types/pools.ts @@ -44,6 +44,8 @@ export interface Pool { volume24h?: number; volume7d?: number; fees24h?: number; + fees7d?: number; + apy?:number; feesYearly?: number; tvlChartData?: TvlChartData[]; volumeChartData?: VolumeChartData[]; diff --git a/src/utils/info/pools/index.ts b/src/utils/info/pools/index.ts index f6e4ed6..c9223a0 100644 --- a/src/utils/info/pools/index.ts +++ b/src/utils/info/pools/index.ts @@ -144,6 +144,9 @@ export const buildPoolsInfo = async ( const feesYearly = fees7d * 52; + const weeklyYield = fees7d / tvl; + const apy = weeklyYield * 52 * 100; + return { ...poolData, tvlChartData, @@ -152,6 +155,8 @@ export const buildPoolsInfo = async ( volume7d, volume24h, fees24h, + fees7d, + apy, feesYearly, }; }) From 8b8fa56bd6ac07cd9e525ee2dd16d40b7e423894 Mon Sep 17 00:00:00 2001 From: qgatssdev Date: Wed, 27 Nov 2024 16:45:44 +0100 Subject: [PATCH 2/3] feat: token total stats --- pages/api/token-stats.ts | 34 +++++++++ pages/index.tsx | 58 +++++++++++++- pages/pools/[id]/index.tsx | 29 +++++++ src/hooks/tokens.ts | 11 +++ src/services/tokens.ts | 10 ++- src/types/tokens.ts | 15 ++++ src/utils/info/tokens/index.ts | 136 ++++++++++++++++++++++++++++++++- 7 files changed, 290 insertions(+), 3 deletions(-) create mode 100644 pages/api/token-stats.ts diff --git a/pages/api/token-stats.ts b/pages/api/token-stats.ts new file mode 100644 index 0000000..a8e2400 --- /dev/null +++ b/pages/api/token-stats.ts @@ -0,0 +1,34 @@ +import { NextApiRequest, NextApiResponse } from "next"; +import { fetchTokenList } from "services/tokens"; +import { Network } from "types/network"; +import { TokenType } from "types/tokens"; +import { buildPoolsInfo } from "utils/info/pools"; +import { buildTokenStats } from "utils/info/tokens"; +import { getMercuryPools } from "zephyr/helpers"; + +async function handler(req: NextApiRequest, res: NextApiResponse) { + const queryParams = req.query; + + let network = queryParams?.network as string; + network = network?.toUpperCase() as Network; + + if (network !== "MAINNET" && network !== "TESTNET") { + return res.status(400).json({ error: "Invalid network" }); + } + + try { + const tokenList: TokenType[] = await fetchTokenList({ network }); + + const data = await getMercuryPools(network); + + const result = await buildPoolsInfo(data, tokenList, network); + + const tokensInfo = await buildTokenStats(tokenList, result); + + return res.json(tokensInfo); + } catch (error) { + return res.status(500).json({ error: "Failed to fetch token list" }); + } +} + +export default handler; diff --git a/pages/index.tsx b/pages/index.tsx index 0cc1fb0..730e595 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -5,7 +5,7 @@ import PoolsTable from "../src/components/pools-table/pools-table"; import TokensTable from "../src/components/tokens-table/tokens-table"; import TVLChart from "../src/components/tvl-chart"; import { useQueryPools } from "../src/hooks/pools"; -import { useQueryTokens } from "../src/hooks/tokens"; +import { useQueryTokens, useQueryTokenStats } from "../src/hooks/tokens"; import TransactionsTable from "../src/components/transaction-table/transactions-table"; import { useQueryAllEvents } from "../src/hooks/events"; import useEventTopicFilter from "../src/hooks/use-event-topic-filter"; @@ -24,10 +24,12 @@ import { useRouter } from "next/router"; import { StyledCard } from "components/styled/card"; import LoadingSkeleton from "components/loading-skeleton"; import { formatNumberToMoney } from "utils/utils"; +import { Text } from "components/styled/text"; export default function Home() { const pools = useQueryPools(); const tokens = useQueryTokens(); + const tokenStats = useQueryTokenStats(); const router = useRouter(); @@ -82,6 +84,60 @@ export default function Home() { Soroswap Info + + + + Total Volume 24h + + + {formatNumberToMoney(tokenStats.data?.volume24h)} + + + + + Total Volume 7d + + + {formatNumberToMoney(tokenStats.data?.volume7d)} + + + + + Total Volume All Time + + + {formatNumberToMoney(tokenStats.data?.volumeAllTime)} + + + + + + + Total Fees Generated 24h + + + {formatNumberToMoney(tokenStats.data?.fees24h)} + + + + + Total Fees Generated 7d + + + {formatNumberToMoney(tokenStats.data?.fees7d)} + + + + + Total Fees Generated All Time + + + {formatNumberToMoney(tokenStats.data?.feesAllTime)} + + + + + diff --git a/pages/pools/[id]/index.tsx b/pages/pools/[id]/index.tsx index 2ccaf39..8ef5c07 100644 --- a/pages/pools/[id]/index.tsx +++ b/pages/pools/[id]/index.tsx @@ -314,6 +314,35 @@ const PoolPage = () => { + + + + + Volume 7d + + + {formatNumberToMoney(pool.data?.volume7d)} + + + + + Fees 7d + + + {formatNumberToMoney(pool.data?.fees7d)} + + + + + APY + + + {formatNumberToToken(pool.data?.apy)} + + + + + { + const { network, isValidQuery } = useQueryNetwork(); + + return useQuery({ + queryKey: [key, network], + queryFn: () => fetchTokenStats({ network: network! }), + enabled: isValidQuery, + }); +}; diff --git a/src/services/tokens.ts b/src/services/tokens.ts index 817974d..d734488 100644 --- a/src/services/tokens.ts +++ b/src/services/tokens.ts @@ -1,5 +1,5 @@ import { ApiNetwork, Network } from "types/network"; -import { Token } from "../types/tokens"; +import { Token, TokenStats } from "../types/tokens"; import { fillDatesAndSort } from "../utils/complete-chart"; import axiosInstance from "./axios"; import { xlmToken } from "constants/constants"; @@ -105,3 +105,11 @@ export const fetchTokenVolumeChart = async ({ return filledData; }; + +export const fetchTokenStats = async ({ network }: ApiNetwork) => { + const { data } = await axiosInstance.get("/api/token-stats", { + params: { network }, + }); + + return data; +}; diff --git a/src/types/tokens.ts b/src/types/tokens.ts index 0ce4e9a..ad7a3e0 100644 --- a/src/types/tokens.ts +++ b/src/types/tokens.ts @@ -17,6 +17,21 @@ export interface Token { priceChartData?: PriceChartData[]; } +export interface TokenFeesChartData { + date: string; + fees: number; + timestamp: number; +} + +export interface TokenStats { + volume24h: number; + volume7d: number; + volumeAllTime: number; + fees24h: number; + fees7d: number; + feesAllTime: number; +} + export interface TokenType { code: string; issuer?: string; diff --git a/src/utils/info/tokens/index.ts b/src/utils/info/tokens/index.ts index ee0d138..589986a 100644 --- a/src/utils/info/tokens/index.ts +++ b/src/utils/info/tokens/index.ts @@ -6,7 +6,7 @@ import { TvlChartData, VolumeChartData, } from "types/pools"; -import { Token, TokenType } from "types/tokens"; +import { Token, TokenType, TokenFeesChartData, TokenStats } from "types/tokens"; import { MercuryRsvCh, getMercuryRsvCh } from "zephyr/helpers"; import { getDate } from "../pools"; import { getExpectedAmountOfOne } from "utils/utils"; @@ -215,3 +215,137 @@ export const getTokenPriceChart = ( return filledPriceChartData as PriceChartData[]; }; + +export const buildTokenStats = async ( + tokenList: TokenType[], + pools: Pool[] +) => { + const tokens: Token[] = tokenList.map((t) => ({ + asset: t, + fees24h: 0, + price: 0, + priceChange24h: 0, + tvl: 0, + tvlSlippage24h: 0, + tvlSlippage7d: 0, + volume24h: 0, + volume24hChange: 0, + volume7d: 0, + volume7dChange: 0, + })); + + const USDC = tokens.find((token) => token.asset.code === "USDC"); + + if (!USDC) return null; + + const totalStats = { + volume24h: 0, + volume7d: 0, + volumeAllTime: 0, + fees24h: 0, + fees7d: 0, + feesAllTime: 0, + }; + + tokens.forEach((token) => { + const tokenPools = pools.filter( + (pool) => + pool.tokenA.contract === token.asset.contract || + pool.tokenB.contract === token.asset.contract + ); + + const volumeChartData = getTokenVolumeChartData(token, tokenPools); + + const nowTimestamp = new Date().getTime() / 1000; + + const volume24h = volumeChartData.reduce((acc, item) => { + const itemTimestamp = new Date(item.date).getTime() / 1000; + + if (nowTimestamp - itemTimestamp <= 24 * 3600) { + return acc + item.volume; + } + return acc; + }, 0); + + const volume7d = volumeChartData.reduce((acc, item) => { + const itemTimestamp = new Date(item.date).getTime() / 1000; + + if (nowTimestamp - itemTimestamp <= 7 * 24 * 3600) { + return acc + item.volume; + } + return acc; + }, 0); + + const volumeAllTime = volumeChartData.reduce((acc, item) => { + return acc + item.volume; + }, 0); + + const feesChartData = getTokenFeesChartData(tokenPools); + + const fees24h = feesChartData.reduce((acc, item) => { + const itemTimestamp = new Date(item.date).getTime() / 1000; + + if (nowTimestamp - itemTimestamp <= 24 * 3600) { + return acc + item.fees; + } + return acc; + }, 0); + + const fees7d = feesChartData.reduce((acc, item) => { + const itemTimestamp = new Date(item.date).getTime() / 1000; + + if (nowTimestamp - itemTimestamp <= 7 * 24 * 3600) { + return acc + item.fees; + } + return acc; + }, 0); + + const feesAllTime = feesChartData.reduce((acc, item) => { + return acc + item.fees; + }, 0); + + // Accumulate totals + totalStats.volume24h += volume24h; + totalStats.volume7d += volume7d; + totalStats.volumeAllTime += volumeAllTime; + totalStats.fees24h += fees24h; + totalStats.fees7d += fees7d; + totalStats.feesAllTime += feesAllTime; + }); + + return totalStats as TokenStats; +}; + +export const getTokenFeesChartData = ( + tokenPools: Pool[] +): TokenFeesChartData[] => { + let feesChartData: { [x: string]: any } = {}; + + tokenPools.forEach((pool) => { + pool.feesChartData?.forEach((data) => { + const fees = data.fees || 0; + + if (!feesChartData[data.date]) { + feesChartData[data.date] = { + date: data.date, + fees: fees, + timestamp: data.timestamp, + }; + } else { + feesChartData[data.date] = { + ...feesChartData[data.date], + fees: feesChartData[data.date].fees + fees, + }; + } + }); + }); + + feesChartData = Object.values(feesChartData); + + feesChartData.sort( + (a: TokenFeesChartData, b: TokenFeesChartData) => + new Date(a.date).getTime() - new Date(b.date).getTime() + ); + + return feesChartData as TokenFeesChartData[]; +}; From 864ed1e8afa826d56cb0adc8b2c2f4b834cde40f Mon Sep 17 00:00:00 2001 From: qgatssdev Date: Fri, 6 Dec 2024 00:44:14 +0100 Subject: [PATCH 3/3] chore: fix api call --- src/hooks/tokens.ts | 2 +- src/utils/info/tokens/index.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/hooks/tokens.ts b/src/hooks/tokens.ts index fc25c39..986fa63 100644 --- a/src/hooks/tokens.ts +++ b/src/hooks/tokens.ts @@ -77,7 +77,7 @@ export const useQueryTokenStats = () => { const { network, isValidQuery } = useQueryNetwork(); return useQuery({ - queryKey: [key, network], + queryKey: [key, network, "token-stats"], queryFn: () => fetchTokenStats({ network: network! }), enabled: isValidQuery, }); diff --git a/src/utils/info/tokens/index.ts b/src/utils/info/tokens/index.ts index fcb7699..762ec3c 100644 --- a/src/utils/info/tokens/index.ts +++ b/src/utils/info/tokens/index.ts @@ -233,6 +233,7 @@ export const buildTokenStats = async ( volume24hChange: 0, volume7d: 0, volume7dChange: 0, + issuer: "", })); const USDC = tokens.find((token) => token.asset.code === "USDC");