diff --git a/components/RoundStatus/index.tsx b/components/RoundStatus/index.tsx index d4f5bd21..6627fca9 100644 --- a/components/RoundStatus/index.tsx +++ b/components/RoundStatus/index.tsx @@ -9,7 +9,7 @@ import { QuestionMarkCircledIcon, } from "@modulz/radix-icons"; import { ProtocolQueryResult } from "apollo"; -import { useCurrentRoundData } from "hooks"; +import { useCurrentRoundData, useSupplyChangeData } from "hooks"; import { useTheme } from "next-themes"; import numbro from "numbro"; import { useMemo } from "react"; @@ -77,6 +77,11 @@ const Index = ({ ) || 0, [protocol] ); + const totalSupply = useMemo( + () => (protocol?.totalSupply ? Number(protocol.totalSupply) : null), + [protocol] + ); + const supplyChangeData = useSupplyChangeData(); return ( + + The current total supply of LPT.} + > + + + + Total Supply + + + + + + + + {totalSupply !== null + ? `${numbro(totalSupply).format({ + mantissa: 0, + average: true, + })} LPT` + : "--"} + + + + Total supply change over the past 365 days.} + > + + + + Supply Change (1Y) + + + + + + + + {supplyChangeData?.supplyChange != null + ? numbro(supplyChangeData?.supplyChange ?? 0).format({ + output: "percent", + mantissa: 2, + }) + : "--"} + + + ) : ( { return data ?? null; }; +export const useSupplyChangeData = () => { + const { data } = useSWR(`/supply-change`); + + return data ?? null; +}; + export const useAvailableInferencePipelinesData = () => { const { data, isValidating } = useSWR(`/pipelines`); return { data: data ?? { pipelines: [] }, isValidating }; diff --git a/lib/api/types/get-supply-change.ts b/lib/api/types/get-supply-change.ts new file mode 100644 index 00000000..92775650 --- /dev/null +++ b/lib/api/types/get-supply-change.ts @@ -0,0 +1,7 @@ +export type SupplyChangeData = { + startDate: number; + endDate: number; + startSupply: number; + endSupply: number; + supplyChange: number | null; +}; diff --git a/pages/api/supply-change.tsx b/pages/api/supply-change.tsx new file mode 100644 index 00000000..35647c17 --- /dev/null +++ b/pages/api/supply-change.tsx @@ -0,0 +1,94 @@ +import { getCacheControlHeader } from "@lib/api"; +import type { SupplyChangeData } from "@lib/api/types/get-supply-change"; +import { CHAIN_INFO, DEFAULT_CHAIN_ID } from "@lib/chains"; +import { fetchWithRetry } from "@lib/fetchWithRetry"; +import type { NextApiRequest, NextApiResponse } from "next"; + +const SECONDS_PER_DAY = 24 * 60 * 60; + +const supplyChangeHandler = async ( + req: NextApiRequest, + res: NextApiResponse +) => { + try { + const { method } = req; + if (method !== "GET") { + res.setHeader("Allow", ["GET"]); + return res.status(405).end(`Method ${method} Not Allowed`); + } + res.setHeader("Cache-Control", getCacheControlHeader("day")); + + const rangeStartDate = + Math.floor(Date.now() / 1000) - 365 * SECONDS_PER_DAY; + + const response = await fetchWithRetry( + CHAIN_INFO[DEFAULT_CHAIN_ID].subgraph, + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + query: ` + query SupplyChange($rangeStartDate: Int!) { + start: days( + first: 1 + orderBy: date + orderDirection: asc + where: { date_gte: $rangeStartDate } + ) { + date + totalSupply + } + end: days( + first: 1 + orderBy: date + orderDirection: desc + where: { date_gte: $rangeStartDate } + ) { + date + totalSupply + } + } + `, + variables: { + rangeStartDate, + }, + }), + }, + { + retryOnMethods: ["POST"], + } + ); + + if (!response.ok) { + return res.status(500).json(null); + } + + const { data } = await response.json(); + const start = Array.isArray(data?.start) ? data.start : []; + const end = Array.isArray(data?.end) ? data.end : []; + + const startItem = start[0]; + const endItem = end[0]; + const startSupply = Number(startItem?.totalSupply ?? 0); + const endSupply = Number(endItem?.totalSupply ?? 0); + const supplyChange = + startSupply > 0 && endSupply > 0 + ? (endSupply - startSupply) / startSupply + : null; + + return res.status(200).json({ + startDate: Number(startItem?.date ?? 0), + endDate: Number(endItem?.date ?? 0), + startSupply, + endSupply, + supplyChange, + }); + } catch (err) { + console.error(err); + return res.status(500).json(null); + } +}; + +export default supplyChangeHandler; diff --git a/pages/api/totalTokenSupply.tsx b/pages/api/totalTokenSupply.tsx deleted file mode 100644 index 23a19d04..00000000 --- a/pages/api/totalTokenSupply.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { getCacheControlHeader } from "@lib/api"; -import { CHAIN_INFO, DEFAULT_CHAIN_ID } from "@lib/chains"; -import { fetchWithRetry } from "@lib/fetchWithRetry"; -import type { NextApiRequest, NextApiResponse } from "next"; - -const totalTokenSupply = async (_req: NextApiRequest, res: NextApiResponse) => { - const response = await fetchWithRetry( - CHAIN_INFO[DEFAULT_CHAIN_ID].subgraph, - { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - query: ` - query { - protocol(id: "0") { - totalSupply - } - } - `, - }), - }, - { - retryOnMethods: ["POST"], - } - ); - - res.setHeader("Cache-Control", getCacheControlHeader("day")); - - const { - data: { protocol }, - } = await response.json(); - res.json(Number(protocol.totalSupply)); -}; - -export default totalTokenSupply; diff --git a/pages/treasury/create-proposal.tsx b/pages/treasury/create-proposal.tsx index b24a7983..d24fb2ba 100644 --- a/pages/treasury/create-proposal.tsx +++ b/pages/treasury/create-proposal.tsx @@ -301,9 +301,9 @@ const CreateProposal = () => { }, }} > - + LPT receiver: - +