From ecdfb69d925d01873899ed9183b8aca8446526f7 Mon Sep 17 00:00:00 2001 From: must-be-carbon <175485679+must-be-carbon@users.noreply.github.com> Date: Tue, 30 Jul 2024 21:54:22 +0530 Subject: [PATCH] must-be-carbon/feat/2377 add supply endpoint for KLIMA & BCT (#2382) * feat(app): add API endpoint for supply * chore(github): fix typo in PR template * feat(app): add support for supply by token * feat(app): shorten api paths * refactor(app): use getTokenDecimals * refactor(app): make type a required param --- .github/pull_request_template.md | 2 +- app/pages/api/supply.ts | 99 ++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 app/pages/api/supply.ts diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 7ae924dbeb..6968a78755 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -16,7 +16,7 @@ Also related to ## How to Test 1. _step1_ diff --git a/app/pages/api/supply.ts b/app/pages/api/supply.ts new file mode 100644 index 0000000000..33f41b0e91 --- /dev/null +++ b/app/pages/api/supply.ts @@ -0,0 +1,99 @@ +import { + getContract, + getStaticProvider, + getTokenDecimals, +} from "@klimadao/lib/utils"; +import { formatUnits } from "ethers/lib/utils"; +import { DEFAULT_NETWORK } from "lib/constants"; +import type { NextApiRequest, NextApiResponse } from "next"; + +export const config = { + api: {}, + // Specifies the maximum allowed duration for this function to execute (in seconds) + maxDuration: 5, +}; + +type APIResponse = + | number + | { + totalSupply: number; + circulatingSupply: number; + } + | { + error: string; + }; + +const DEFAULT_TOKENS = ["klima", "bct"]; + +/** + * @description fetch total supply for a token + * @param req + * @param res + * @example GET /api/supply?type=total&token=klima Returns number + * @example GET /api/supply?type=circulating&token=klima Return number + */ +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +) { + try { + switch (req.method) { + case "GET": + const { type, token } = req.query; + + if (!token || !type) { + return res + .status(400) + .send({ error: "Token or type is missing from params" }); + } + + if (typeof token !== "string") { + return res.status(400).send({ error: "Token should be a string" }); + } + + const targetToken = DEFAULT_TOKENS.find((t) => t === token); + + if (!targetToken) { + return res.status(400).send({ error: "Invalid token" }); + } + + const provider = getStaticProvider({ + chain: "polygon", + }); + + const contract = getContract({ + // TODO: Improve typing by exporting ContractName type from lib + contractName: targetToken as "klima" | "bct", + network: DEFAULT_NETWORK, + provider, + }); + + const totalSupply = await contract.totalSupply(); + + if (!totalSupply) { + return res.status(500).send({ error: "Failed to get total supply" }); + } + + const totalSupplyNum = Number( + formatUnits(totalSupply, getTokenDecimals(targetToken)) + ); + + switch (type) { + case "total": + case "circulating": + return res.status(200).send(totalSupplyNum); + default: + return res.status(400).send({ error: "Invalid type" }); + } + + default: + res.setHeader("Allow", ["GET"]); + return res + .status(405) + .send({ error: `Method ${req.method} Not Allowed` }); + } + } catch (error) { + console.error(error); + return res.status(500).send({ error: "Internal Server Error" }); + } +}