From 7a8dd30bf7a2692159e5445c4d1c649083f71629 Mon Sep 17 00:00:00 2001 From: wjrjerome Date: Wed, 18 Dec 2024 02:22:49 +1100 Subject: [PATCH] fix: supporting sending query param in repeat format --- package-lock.json | 270 +++++++++++++------------ package.json | 2 + src/app/api/apiWrapper.ts | 61 +++--- src/app/api/getDelegations.ts | 8 +- src/app/api/getDelegationsV2.ts | 10 +- src/app/api/getFinalityProviders.ts | 6 +- src/app/api/getStakers.ts | 47 ----- src/app/api/getUnbondingEligibility.ts | 6 +- src/app/api/postFilterOrdinals.ts | 12 +- src/app/api/postUnbonding.ts | 2 +- src/app/components/Stakers/Stakers.tsx | 96 --------- 11 files changed, 195 insertions(+), 325 deletions(-) delete mode 100644 src/app/api/getStakers.ts delete mode 100644 src/app/components/Stakers/Stakers.tsx diff --git a/package-lock.json b/package-lock.json index 83692f39..a38872b8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,6 +28,7 @@ "framer-motion": "^11.1.9", "next": "14.2.15", "next-themes": "^0.3.0", + "qs": "^6.13.1", "react": "^18", "react-dom": "^18", "react-icons": "^5.1.0", @@ -54,6 +55,7 @@ "@testing-library/react": "^16.0.0", "@types/jest": "^29.5.12", "@types/node": "^20", + "@types/qs": "^6.9.17", "@types/react": "^18", "@types/react-dom": "^18", "autoprefixer": "^10.0.1", @@ -3461,6 +3463,126 @@ "node": ">= 10" } }, + "node_modules/@next/swc-darwin-x64": { + "version": "14.2.15", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.15.tgz", + "integrity": "sha512-5TGyjFcf8ampZP3e+FyCax5zFVHi+Oe7sZyaKOngsqyaNEpOgkKB3sqmymkZfowy3ufGA/tUgDPPxpQx931lHg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "14.2.15", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.15.tgz", + "integrity": "sha512-3Bwv4oc08ONiQ3FiOLKT72Q+ndEMyLNsc/D3qnLMbtUYTQAmkx9E/JRu0DBpHxNddBmNT5hxz1mYBphJ3mfrrw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "14.2.15", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.15.tgz", + "integrity": "sha512-k5xf/tg1FBv/M4CMd8S+JL3uV9BnnRmoe7F+GWC3DxkTCD9aewFRH1s5rJ1zkzDa+Do4zyN8qD0N8c84Hu96FQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "14.2.15", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.15.tgz", + "integrity": "sha512-kE6q38hbrRbKEkkVn62reLXhThLRh6/TvgSP56GkFNhU22TbIrQDEMrO7j0IcQHcew2wfykq8lZyHFabz0oBrA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "14.2.15", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.15.tgz", + "integrity": "sha512-PZ5YE9ouy/IdO7QVJeIcyLn/Rc4ml9M2G4y3kCM9MNf1YKvFY4heg3pVa/jQbMro+tP6yc4G2o9LjAz1zxD7tQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "14.2.15", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.15.tgz", + "integrity": "sha512-2raR16703kBvYEQD9HNLyb0/394yfqzmIeyp2nDzcPV4yPjqNUG3ohX6jX00WryXz6s1FXpVhsCo3i+g4RUX+g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-ia32-msvc": { + "version": "14.2.15", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.15.tgz", + "integrity": "sha512-fyTE8cklgkyR1p03kJa5zXEaZ9El+kDNM5A+66+8evQS5e/6v0Gk28LqA0Jet8gKSOyP+OTm/tJHzMlGdQerdQ==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "14.2.15", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.15.tgz", + "integrity": "sha512-SzqGbsLsP9OwKNUG9nekShTwhj6JSB9ZLMWQ8g1gG6hdE5gQLncbnbymrwy2yVmH9nikSLYRYxYMFu78Ggp7/g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@ngraveio/bc-ur": { "version": "1.1.13", "license": "MIT", @@ -5068,6 +5190,13 @@ "devOptional": true, "license": "MIT" }, + "node_modules/@types/qs": { + "version": "6.9.17", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.17.tgz", + "integrity": "sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/react": { "version": "18.3.16", "devOptional": true, @@ -6434,7 +6563,6 @@ }, "node_modules/call-bound": { "version": "1.0.2", - "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.8", @@ -11499,7 +11627,6 @@ }, "node_modules/object-inspect": { "version": "1.13.3", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -12452,6 +12579,21 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/qs": { + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.1.tgz", + "integrity": "sha512-EJPeIn0CYrGu+hli1xilKAPXODtJ12T0sP63Ijx2/khC2JtuaN3JyNIpvmnkmaEtha9ocbG4A4cMcr+TvqvwQg==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/querystringify": { "version": "2.2.0", "license": "MIT" @@ -13294,7 +13436,6 @@ }, "node_modules/side-channel": { "version": "1.1.0", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -13312,7 +13453,6 @@ }, "node_modules/side-channel-list": { "version": "1.0.0", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -13327,7 +13467,6 @@ }, "node_modules/side-channel-map": { "version": "1.0.1", - "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -13344,7 +13483,6 @@ }, "node_modules/side-channel-weakmap": { "version": "1.0.2", - "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -15108,126 +15246,6 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } - }, - "node_modules/@next/swc-darwin-x64": { - "version": "14.2.15", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.15.tgz", - "integrity": "sha512-5TGyjFcf8ampZP3e+FyCax5zFVHi+Oe7sZyaKOngsqyaNEpOgkKB3sqmymkZfowy3ufGA/tUgDPPxpQx931lHg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-gnu": { - "version": "14.2.15", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.15.tgz", - "integrity": "sha512-3Bwv4oc08ONiQ3FiOLKT72Q+ndEMyLNsc/D3qnLMbtUYTQAmkx9E/JRu0DBpHxNddBmNT5hxz1mYBphJ3mfrrw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-musl": { - "version": "14.2.15", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.15.tgz", - "integrity": "sha512-k5xf/tg1FBv/M4CMd8S+JL3uV9BnnRmoe7F+GWC3DxkTCD9aewFRH1s5rJ1zkzDa+Do4zyN8qD0N8c84Hu96FQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-gnu": { - "version": "14.2.15", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.15.tgz", - "integrity": "sha512-kE6q38hbrRbKEkkVn62reLXhThLRh6/TvgSP56GkFNhU22TbIrQDEMrO7j0IcQHcew2wfykq8lZyHFabz0oBrA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-musl": { - "version": "14.2.15", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.15.tgz", - "integrity": "sha512-PZ5YE9ouy/IdO7QVJeIcyLn/Rc4ml9M2G4y3kCM9MNf1YKvFY4heg3pVa/jQbMro+tP6yc4G2o9LjAz1zxD7tQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-arm64-msvc": { - "version": "14.2.15", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.15.tgz", - "integrity": "sha512-2raR16703kBvYEQD9HNLyb0/394yfqzmIeyp2nDzcPV4yPjqNUG3ohX6jX00WryXz6s1FXpVhsCo3i+g4RUX+g==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-ia32-msvc": { - "version": "14.2.15", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.15.tgz", - "integrity": "sha512-fyTE8cklgkyR1p03kJa5zXEaZ9El+kDNM5A+66+8evQS5e/6v0Gk28LqA0Jet8gKSOyP+OTm/tJHzMlGdQerdQ==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-x64-msvc": { - "version": "14.2.15", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.15.tgz", - "integrity": "sha512-SzqGbsLsP9OwKNUG9nekShTwhj6JSB9ZLMWQ8g1gG6hdE5gQLncbnbymrwy2yVmH9nikSLYRYxYMFu78Ggp7/g==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } } } } diff --git a/package.json b/package.json index 840e8604..97d99fe3 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "framer-motion": "^11.1.9", "next": "14.2.15", "next-themes": "^0.3.0", + "qs": "^6.13.1", "react": "^18", "react-dom": "^18", "react-icons": "^5.1.0", @@ -71,6 +72,7 @@ "@testing-library/react": "^16.0.0", "@types/jest": "^29.5.12", "@types/node": "^20", + "@types/qs": "^6.9.17", "@types/react": "^18", "@types/react-dom": "^18", "autoprefixer": "^10.0.1", diff --git a/src/app/api/apiWrapper.ts b/src/app/api/apiWrapper.ts index ded5ed9f..2f1729d2 100644 --- a/src/app/api/apiWrapper.ts +++ b/src/app/api/apiWrapper.ts @@ -1,45 +1,44 @@ -import axios from "axios"; +import axios, { AxiosRequestConfig } from "axios"; +import qs from "qs"; + +type QueryParamValue = + | string + | number + | boolean + | Array; +type QueryParams = Record; + +interface RequestParams { + query?: QueryParams; + body?: any; +} export const apiWrapper = async ( method: "GET" | "POST", path: string, generalErrorMessage: string, - params?: any, + params?: RequestParams, timeout?: number, ) => { - let response; - let handler; - switch (method) { - case "GET": - handler = axios.get; - break; - case "POST": - handler = axios.post; - break; - default: - throw new Error("Invalid method"); - } + const axiosConfig: AxiosRequestConfig = { + timeout: timeout || 0, + params: params?.query, + paramsSerializer: (params) => + qs.stringify(params, { arrayFormat: "repeat" }), + }; + + const url = `${process.env.NEXT_PUBLIC_API_URL}${path}`; try { - // destructure params in case of post request - response = await handler( - `${process.env.NEXT_PUBLIC_API_URL}${path}`, - method === "POST" - ? { ...params } - : { - params, - }, - { - timeout: timeout || 0, // 0 is no timeout - }, - ); + if (method === "GET") { + return await axios.get(url, axiosConfig); + } else { + return await axios.post(url, params?.body, axiosConfig); + } } catch (error) { if (axios.isAxiosError(error)) { - const message = error?.response?.data?.message; - throw new Error(message || generalErrorMessage); - } else { - throw new Error(generalErrorMessage); + throw new Error(error?.response?.data?.message || generalErrorMessage); } + throw new Error(generalErrorMessage); } - return response; }; diff --git a/src/app/api/getDelegations.ts b/src/app/api/getDelegations.ts index d4f2c609..5dbdbe58 100644 --- a/src/app/api/getDelegations.ts +++ b/src/app/api/getDelegations.ts @@ -1,5 +1,3 @@ -import { encode } from "url-safe-base64"; - import { Pagination } from "../types/api"; import { Delegation } from "../types/delegations"; @@ -49,8 +47,8 @@ export const getDelegations = async ( } const params = { - pagination_key: encode(key), - staker_btc_pk: encode(publicKeyNoCoord), + pagination_key: key, + staker_btc_pk: publicKeyNoCoord, // We only fetch for states that can have pending actions. // We don't care terminal states such as "withdrawn" or "transitioned". state: ["active", "unbonded", "unbonding", "unbonding_requested"], @@ -60,7 +58,7 @@ export const getDelegations = async ( "GET", "/v1/staker/delegations", "Error getting delegations", - params, + { query: params }, ); const delegationsAPIResponse: DelegationsAPIResponse = response.data; diff --git a/src/app/api/getDelegationsV2.ts b/src/app/api/getDelegationsV2.ts index 07669313..a5a67194 100644 --- a/src/app/api/getDelegationsV2.ts +++ b/src/app/api/getDelegationsV2.ts @@ -1,5 +1,3 @@ -import { encode } from "url-safe-base64"; - import { Pagination } from "../types/api"; import { DelegationV2, @@ -62,7 +60,7 @@ export const getDelegationV2 = async ( "GET", "/v2/delegation", "Error getting delegation v2", - params, + { query: params }, ); const delegationAPIResponse: DelegationV2APIResponse = response.data; @@ -115,15 +113,15 @@ export const getDelegationsV2 = async ( throw new Error("No public key provided"); } const params = { - staker_pk_hex: encode(publicKeyNoCoord), - pagination_key: pageKey ? encode(pageKey) : "", + staker_pk_hex: publicKeyNoCoord, + pagination_key: pageKey ? pageKey : "", }; const response = await apiWrapper( "GET", "/v2/delegations", "Error getting delegations v2", - params, + { query: params }, ); const delegationsAPIResponse: DelegationsV2APIResponse = response.data; diff --git a/src/app/api/getFinalityProviders.ts b/src/app/api/getFinalityProviders.ts index 2cd443ac..2b67b778 100644 --- a/src/app/api/getFinalityProviders.ts +++ b/src/app/api/getFinalityProviders.ts @@ -1,5 +1,3 @@ -import { encode } from "url-safe-base64"; - import { isValidUrl } from "@/utils/url"; import { Pagination } from "../types/api"; @@ -53,7 +51,7 @@ export const getFinalityProviders = async ({ pk?: string; }): Promise => { const params = { - pagination_key: encode(key), + pagination_key: key, finality_provider_pk: pk, sort_by: sortBy, order, @@ -64,7 +62,7 @@ export const getFinalityProviders = async ({ "GET", "/v2/finality-providers", "Error getting finality providers", - params, + { query: params }, ); const finalityProvidersAPIResponse: FinalityProvidersAPIResponse = diff --git a/src/app/api/getStakers.ts b/src/app/api/getStakers.ts deleted file mode 100644 index 218ac416..00000000 --- a/src/app/api/getStakers.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { apiWrapper } from "./apiWrapper"; - -export interface Stakers { - stakers: StakersAPI[]; -} - -export interface StakersAPI { - staker_pk_hex: string; - active_tvl: number; - total_tvl: number; - active_delegations: number; - total_delegations: number; -} - -interface StakersAPIResponse { - data: StakersAPI[]; -} - -export const getStakers = async (): Promise => { - try { - // Intentionally used without pagination for now - const limit = 50; - // const reverse = false; - - const params = { - // "pagination_key": encode(key), - // "pagination_reverse": reverse, - pagination_limit: limit, - }; - - const response = await apiWrapper( - "GET", - "/v1/stats/staker", - "Error getting stakers", - params, - ); - - const stakersAPIResponse: StakersAPIResponse = response.data; - const stakersAPI: StakersAPI[] = stakersAPIResponse.data; - - return { - stakers: stakersAPI, - }; - } catch (error) { - throw error; - } -}; diff --git a/src/app/api/getUnbondingEligibility.ts b/src/app/api/getUnbondingEligibility.ts index 6773cdbf..55677b46 100644 --- a/src/app/api/getUnbondingEligibility.ts +++ b/src/app/api/getUnbondingEligibility.ts @@ -1,5 +1,3 @@ -import { encode } from "url-safe-base64"; - import { apiWrapper } from "./apiWrapper"; export const getUnbondingEligibility = async (txID: string) => { @@ -8,14 +6,14 @@ export const getUnbondingEligibility = async (txID: string) => { } const params = { - staking_tx_hash_hex: encode(txID), + staking_tx_hash_hex: txID, }; const response = await apiWrapper( "GET", "/v1/unbonding/eligibility", "Error checking unbonding eligibility", - params, + { query: params }, ); // If the response status is 200, the unbonding is eligible diff --git a/src/app/api/postFilterOrdinals.ts b/src/app/api/postFilterOrdinals.ts index 291b87d2..3d38d773 100644 --- a/src/app/api/postFilterOrdinals.ts +++ b/src/app/api/postFilterOrdinals.ts @@ -24,11 +24,13 @@ export const postVerifyUtxoOrdinals = async ( "/v1/ordinals/verify-utxos", "Error verifying utxos ordinals", { - address, - utxos: chunk.map((utxo) => ({ - txid: utxo.txid, - vout: utxo.vout, - })), + body: { + address, + utxos: chunk.map((utxo) => ({ + txid: utxo.txid, + vout: utxo.vout, + })), + }, }, TIMEOUT_DURATION, ), diff --git a/src/app/api/postUnbonding.ts b/src/app/api/postUnbonding.ts index f4e216af..19c8954b 100644 --- a/src/app/api/postUnbonding.ts +++ b/src/app/api/postUnbonding.ts @@ -24,7 +24,7 @@ export const postUnbonding = async ( "POST", "/v1/unbonding", "Error submitting unbonding request", - payload, + { body: payload }, ); // If the response status is 202, the request was accepted diff --git a/src/app/components/Stakers/Stakers.tsx b/src/app/components/Stakers/Stakers.tsx deleted file mode 100644 index 196b4e9f..00000000 --- a/src/app/components/Stakers/Stakers.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import { useQuery } from "@tanstack/react-query"; -import { useEffect } from "react"; -import { AiOutlineInfoCircle } from "react-icons/ai"; -import { Tooltip } from "react-tooltip"; - -import { getStakers } from "@/app/api/getStakers"; -import { LoadingView } from "@/app/components/Loading/Loading"; -import { useError } from "@/app/context/Error/ErrorContext"; -import { ErrorState } from "@/app/types/errors"; - -import { Staker } from "./Staker"; - -interface StakersProps {} - -export const Stakers: React.FC = () => { - const { showError, hideError, isErrorOpen, captureError } = useError(); - - const { - data: stakersData, - error, - refetch, - } = useQuery({ - queryKey: ["stakers"], - queryFn: getStakers, - refetchInterval: 60000, // 1 minute - select: (data) => data.stakers, - retry: (failureCount, error) => { - return !isErrorOpen && failureCount <= 3; - }, - }); - - useEffect(() => { - if (error) { - showError({ - error: { - message: error.message, - errorState: ErrorState.SERVER_ERROR, - }, - retryAction: refetch, - }); - captureError(error); - } - }, [error, refetch, showError, hideError, captureError]); - - return ( -
-

Top Stakers

- {stakersData && ( -
-

Staker

-
-

Delegations

- - - -
-
-

Stake

- - - -
-
- )} -
- {stakersData ? ( - stakersData.map( - (staker) => - staker && ( - - ), - ) - ) : ( - - )} -
- - -
- ); -};