From d529272dcb39086d447e72c95d4a9f7ec9c246e5 Mon Sep 17 00:00:00 2001 From: jeremy-babylonchain Date: Tue, 10 Sep 2024 11:48:28 +0800 Subject: [PATCH 01/15] add points feature --- src/app/api/getDelegationPoints.ts | 85 +++++++++++ src/app/api/getStakersPoints.ts | 28 ++++ src/app/components/Delegations/Delegation.tsx | 11 +- .../components/Delegations/Delegations.tsx | 49 +++++- .../components/Points/DelegationPoints.tsx | 31 ++++ src/app/components/Points/StakerPoints.tsx | 39 +++++ src/app/components/Staking/Staking.tsx | 12 +- src/app/components/Summary/Summary.tsx | 141 ++++++++++-------- .../context/api/DelegationsPointsProvider.tsx | 92 ++++++++++++ src/app/page.tsx | 2 + 10 files changed, 414 insertions(+), 76 deletions(-) create mode 100644 src/app/api/getDelegationPoints.ts create mode 100644 src/app/api/getStakersPoints.ts create mode 100644 src/app/components/Points/DelegationPoints.tsx create mode 100644 src/app/components/Points/StakerPoints.tsx create mode 100644 src/app/context/api/DelegationsPointsProvider.tsx diff --git a/src/app/api/getDelegationPoints.ts b/src/app/api/getDelegationPoints.ts new file mode 100644 index 00000000..34a69072 --- /dev/null +++ b/src/app/api/getDelegationPoints.ts @@ -0,0 +1,85 @@ +import { encode } from "url-safe-base64"; + +import { Pagination } from "../types/api"; + +import { apiWrapper } from "./apiWrapper"; + +export interface DelegationPoints { + staking_tx_hash_hex: string; + staker: { + pk: string; + points: number; + }; + finality_provider: { + pk: string; + points: number; + }; + staking_height: number; + unbonding_height: number | null; + expiry_height: number; +} + +export interface PaginatedDelegationsPoints { + data: DelegationPoints[]; + pagination: Pagination; +} + +export const getDelegationPoints = async ( + paginationKey?: string, + stakerBtcPk?: string, + stakingTxHashHexes?: string[], +): Promise => { + const params: Record = {}; + + if (stakerBtcPk && stakingTxHashHexes && stakingTxHashHexes.length > 0) { + throw new Error( + "Only one of stakerBtcPk or stakingTxHashHexes should be provided", + ); + } + + if ( + !stakerBtcPk && + (!stakingTxHashHexes || stakingTxHashHexes.length === 0) + ) { + throw new Error( + "Either stakerBtcPk or stakingTxHashHexes must be provided", + ); + } + + if (stakerBtcPk) { + params.staker_btc_pk = encode(stakerBtcPk); + } + + let allDelegationPoints: DelegationPoints[] = []; + let nextPaginationKey = paginationKey; + + do { + const currentParams = { ...params }; + if (nextPaginationKey && nextPaginationKey !== "") { + currentParams.pagination_key = encode(nextPaginationKey); + } + + if (stakingTxHashHexes && stakingTxHashHexes.length > 0) { + currentParams.staking_tx_hash_hex = stakingTxHashHexes.slice(0, 10); + stakingTxHashHexes = stakingTxHashHexes.slice(10); + } + + const response = await apiWrapper( + "GET", + "/v1/points/staker/delegations", + "Error getting delegation points", + currentParams, + ); + + allDelegationPoints = allDelegationPoints.concat(response.data.data); + nextPaginationKey = response.data.pagination.next_key; + } while ( + nextPaginationKey || + (stakingTxHashHexes && stakingTxHashHexes.length > 0) + ); + + return { + data: allDelegationPoints, + pagination: { next_key: nextPaginationKey || "" }, + }; +}; diff --git a/src/app/api/getStakersPoints.ts b/src/app/api/getStakersPoints.ts new file mode 100644 index 00000000..cd86bf8e --- /dev/null +++ b/src/app/api/getStakersPoints.ts @@ -0,0 +1,28 @@ +import { encode } from "url-safe-base64"; + +import { apiWrapper } from "./apiWrapper"; + +export interface StakerPoints { + staker_btc_pk: string; + points: number; +} + +export const getStakersPoints = async ( + stakerBtcPk: string[], +): Promise => { + const params: Record = {}; + + params.staker_btc_pk = + stakerBtcPk.length > 1 + ? stakerBtcPk.map(encode).join(",") + : encode(stakerBtcPk[0]); + + const response = await apiWrapper( + "GET", + "/v1/points/stakers", + "Error getting staker points", + params, + ); + + return response.data; +}; diff --git a/src/app/components/Delegations/Delegation.tsx b/src/app/components/Delegations/Delegation.tsx index eb9c5080..0ecd4265 100644 --- a/src/app/components/Delegations/Delegation.tsx +++ b/src/app/components/Delegations/Delegation.tsx @@ -13,6 +13,8 @@ import { getState, getStateTooltip } from "@/utils/getState"; import { maxDecimals } from "@/utils/maxDecimals"; import { trim } from "@/utils/trim"; +import { DelegationPoints } from "../Points/DelegationPoints"; + interface DelegationProps { stakingTx: StakingTx; stakingValueSat: number; @@ -122,7 +124,7 @@ export const Delegation: React.FC = ({

overflow

)} -
+

@@ -142,13 +144,11 @@ export const Delegation: React.FC = ({ {trim(stakingTxHash)}

- {/* Future data placeholder */} -
{/* we need to center the text without the tooltip add its size 12px and gap 4px, 16/2 = 8px */} -
+

{renderState()}

= ({
+
+ +
{generateActionButton()}
diff --git a/src/app/components/Delegations/Delegations.tsx b/src/app/components/Delegations/Delegations.tsx index bf2f7e96..d58da715 100644 --- a/src/app/components/Delegations/Delegations.tsx +++ b/src/app/components/Delegations/Delegations.tsx @@ -5,6 +5,7 @@ import { useLocalStorage } from "usehooks-ts"; import { SignPsbtTransaction } from "@/app/common/utils/psbt"; import { LoadingTableList } from "@/app/components/Loading/Loading"; +import { DelegationsPointsProvider } from "@/app/context/api/DelegationsPointsProvider"; import { useError } from "@/app/context/Error/ErrorContext"; import { QueryMeta } from "@/app/types/api"; import { @@ -39,19 +40,56 @@ interface DelegationsProps { pushTx: WalletProvider["pushTx"]; queryMeta: QueryMeta; getNetworkFees: WalletProvider["getNetworkFees"]; + isWalletConnected: boolean; } export const Delegations: React.FC = ({ delegationsAPI, delegationsLocalStorage, globalParamsVersion, - publicKeyNoCoord, - btcWalletNetwork, + signPsbtTx, + pushTx, + queryMeta, + getNetworkFees, address, + btcWalletNetwork, + publicKeyNoCoord, + isWalletConnected, +}) => { + return ( + + + + ); +}; + +const DelegationsContent: React.FC = ({ + delegationsAPI, + delegationsLocalStorage, + globalParamsVersion, signPsbtTx, pushTx, queryMeta, getNetworkFees, + address, + btcWalletNetwork, + publicKeyNoCoord, }) => { const [modalOpen, setModalOpen] = useState(false); const [txID, setTxID] = useState(""); @@ -113,7 +151,7 @@ export const Delegations: React.FC = ({ id, delegationsAPI, publicKeyNoCoord, - btcWalletNetwork, + btcWalletNetwork!, signPsbtTx, ); // Update the local state with the new intermediate delegation @@ -143,7 +181,7 @@ export const Delegations: React.FC = ({ id, delegationsAPI, publicKeyNoCoord, - btcWalletNetwork, + btcWalletNetwork!, signPsbtTx, address, getNetworkFees, @@ -233,11 +271,12 @@ export const Delegations: React.FC = ({
) : ( <> -
+

Amount

Inception

Transaction hash

Status

+

Points

Action

= ({ + stakingTxHash, +}) => { + const { delegationPoints, isLoading } = useDelegationsPoints(); + + const points = delegationPoints.get(stakingTxHash); + + if (isLoading) { + return ( +
+
+
+ ); + } + + return ( +
+

+ {points !== undefined ? points : 0} +

+
+ ); +}; diff --git a/src/app/components/Points/StakerPoints.tsx b/src/app/components/Points/StakerPoints.tsx new file mode 100644 index 00000000..7ccae028 --- /dev/null +++ b/src/app/components/Points/StakerPoints.tsx @@ -0,0 +1,39 @@ +import { useQuery } from "@tanstack/react-query"; +import React from "react"; + +import { getStakersPoints } from "@/app/api/getStakersPoints"; +import { satoshiToBtc } from "@/utils/btcConversions"; +import { maxDecimals } from "@/utils/maxDecimals"; + +interface StakerPointsProps { + publicKeyNoCoord: string; +} + +export const StakerPoints: React.FC = ({ + publicKeyNoCoord, +}) => { + const { data: stakerPoints, isLoading } = useQuery({ + queryKey: ["stakerPoints", publicKeyNoCoord], + queryFn: () => getStakersPoints([publicKeyNoCoord]), + enabled: !!publicKeyNoCoord, + refetchInterval: 60000, // Refresh every minute + }); + + if (isLoading) { + return ( +
+
+
+ ); + } + + const points = stakerPoints?.[0]?.points; + + return ( +
+

+ {points !== undefined ? maxDecimals(satoshiToBtc(points), 8) : 0} +

+
+ ); +}; diff --git a/src/app/components/Staking/Staking.tsx b/src/app/components/Staking/Staking.tsx index 12f9cb65..0950be55 100644 --- a/src/app/components/Staking/Staking.tsx +++ b/src/app/components/Staking/Staking.tsx @@ -1,5 +1,5 @@ import { useQuery, useQueryClient } from "@tanstack/react-query"; -import { Transaction, networks } from "bitcoinjs-lib"; +import { networks, Transaction } from "bitcoinjs-lib"; import { Dispatch, SetStateAction, useEffect, useMemo, useState } from "react"; import { Tooltip } from "react-tooltip"; import { useLocalStorage } from "usehooks-ts"; @@ -27,8 +27,8 @@ import { } from "@/utils/delegations/signStakingTx"; import { getFeeRateFromMempool } from "@/utils/getFeeRateFromMempool"; import { - ParamsWithContext, getCurrentGlobalParamsVersion, + ParamsWithContext, } from "@/utils/globalParams"; import { isStakingSignReady } from "@/utils/isStakingSignReady"; import { toLocalStorageDelegation } from "@/utils/local_storage/toLocalStorageDelegation"; @@ -74,13 +74,13 @@ export const Staking: React.FC = ({ isWalletConnected, onConnect, isLoading, - btcWallet, - btcWalletNetwork, - address, - publicKeyNoCoord, setDelegationsLocalStorage, btcWalletBalanceSat, availableUTXOs, + publicKeyNoCoord, + address, + btcWallet, + btcWalletNetwork, }) => { // Staking form state const [stakingAmountSat, setStakingAmountSat] = useState(0); diff --git a/src/app/components/Summary/Summary.tsx b/src/app/components/Summary/Summary.tsx index 083189ee..851a7cf0 100644 --- a/src/app/components/Summary/Summary.tsx +++ b/src/app/components/Summary/Summary.tsx @@ -15,15 +15,18 @@ import { maxDecimals } from "@/utils/maxDecimals"; import { Network } from "@/utils/wallet/wallet_provider"; import { LoadingSmall } from "../Loading/Loading"; +import { StakerPoints } from "../Points/StakerPoints"; interface SummaryProps { totalStakedSat: number; btcWalletBalanceSat?: number; + publicKeyNoCoord: string; } export const Summary: React.FC = ({ totalStakedSat, btcWalletBalanceSat, + publicKeyNoCoord, }) => { const { coinName } = getNetworkConfig(); const onMainnet = getNetworkConfig().network === Network.MAINNET; @@ -46,73 +49,89 @@ export const Summary: React.FC = ({ }, [btcHeight, globalParams]); return ( -
+

Your staking summary

-
-
-
-

Total staked

- - - - -
-
- -

- {totalStakedSat - ? maxDecimals(satoshiToBtc(totalStakedSat), 8) - : 0}{" "} - {coinName} -

+
+
+
+
+

Total staked

+ + + + +
+
+ +

+ {totalStakedSat + ? maxDecimals(satoshiToBtc(totalStakedSat), 8) + : 0}{" "} + {coinName} +

+
-
-
-
-
-

Stakable Balance

- - - - +
+
+
+

Total points

+ + + + +
+
+ +
-
- - {typeof btcWalletBalanceSat === "number" ? ( -

+

+
+

Stakable Balance

+ - {maxDecimals(satoshiToBtc(btcWalletBalanceSat), 8)} {coinName} -

- ) : ( - - )} + +
+ +
+
+ + {typeof btcWalletBalanceSat === "number" ? ( +

+ {maxDecimals(satoshiToBtc(btcWalletBalanceSat), 8)} {coinName} +

+ ) : ( + + )} +
- {!onMainnet && ( - - Get Test Tokens - - )}
+ {!onMainnet && ( + + Get Test Tokens + + )}
); diff --git a/src/app/context/api/DelegationsPointsProvider.tsx b/src/app/context/api/DelegationsPointsProvider.tsx new file mode 100644 index 00000000..733c5eb8 --- /dev/null +++ b/src/app/context/api/DelegationsPointsProvider.tsx @@ -0,0 +1,92 @@ +import { useQuery } from "@tanstack/react-query"; +import React, { createContext, useContext, useEffect, useState } from "react"; + +import { + getDelegationPoints, + PaginatedDelegationsPoints, +} from "@/app/api/getDelegationPoints"; +import { Delegation } from "@/app/types/delegations"; + +interface PointsContextType { + delegationPoints: Map; + isLoading: boolean; + error: Error | null; +} + +const DelegationsPointsContext = createContext( + undefined, +); + +export const useDelegationsPoints = () => { + const context = useContext(DelegationsPointsContext); + if (context === undefined) { + throw new Error( + "useDelegationsPoints must be used within a DelegationsPointsProvider", + ); + } + return context; +}; + +interface DelegationsPointsProviderProps { + children: React.ReactNode; + publicKeyNoCoord: string; + delegationsAPI: Delegation[]; + isWalletConnected: boolean; +} + +export const DelegationsPointsProvider: React.FC< + DelegationsPointsProviderProps +> = ({ children, publicKeyNoCoord, delegationsAPI, isWalletConnected }) => { + const [delegationPoints, setDelegationPoints] = useState>( + new Map(), + ); + + const fetchAllPoints = async () => { + let allPoints: PaginatedDelegationsPoints["data"] = []; + let paginationKey = ""; + + const stakingTxHashHexes = delegationsAPI.map( + (delegation) => delegation.stakingTxHashHex, + ); + + do { + const result = await getDelegationPoints( + paginationKey, + undefined, + stakingTxHashHexes, + ); + allPoints = [...allPoints, ...result.data]; + paginationKey = result.pagination.next_key; + } while (paginationKey !== ""); + + return allPoints; + }; + + const { data, isLoading, error } = useQuery({ + queryKey: ["delegationPoints", publicKeyNoCoord, delegationsAPI], + queryFn: fetchAllPoints, + enabled: isWalletConnected && delegationsAPI.length > 0, + }); + + useEffect(() => { + if (data) { + const newDelegationPoints = new Map(); + data.forEach((point) => { + newDelegationPoints.set(point.staking_tx_hash_hex, point.staker.points); + }); + setDelegationPoints(newDelegationPoints); + } + }, [data]); + + const value = { + delegationPoints, + isLoading, + error, + }; + + return ( + + {children} + + ); +}; diff --git a/src/app/page.tsx b/src/app/page.tsx index e99f2b9e..422a934c 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -333,6 +333,7 @@ const Home: React.FC = () => { )} = () => { isFetchingMore: isFetchingNextDelegationsPage, }} getNetworkFees={btcWallet.getNetworkFees} + isWalletConnected={!!btcWallet} /> )} {/* At this point of time is not used */} From 4e36c19959b1e72a092bfc25586a1cc964f9722d Mon Sep 17 00:00:00 2001 From: jeremy-babylonchain Date: Tue, 10 Sep 2024 17:17:06 +0800 Subject: [PATCH 02/15] faq and tooltips update --- src/app/components/Delegations/Delegations.tsx | 4 ++-- src/app/components/FAQ/data/questions.ts | 6 ++++++ src/app/components/Summary/Summary.tsx | 7 +++++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/app/components/Delegations/Delegations.tsx b/src/app/components/Delegations/Delegations.tsx index d58da715..0ecc6a93 100644 --- a/src/app/components/Delegations/Delegations.tsx +++ b/src/app/components/Delegations/Delegations.tsx @@ -151,7 +151,7 @@ const DelegationsContent: React.FC = ({ id, delegationsAPI, publicKeyNoCoord, - btcWalletNetwork!, + btcWalletNetwork, signPsbtTx, ); // Update the local state with the new intermediate delegation @@ -181,7 +181,7 @@ const DelegationsContent: React.FC = ({ id, delegationsAPI, publicKeyNoCoord, - btcWalletNetwork!, + btcWalletNetwork, signPsbtTx, address, getNetworkFees, diff --git a/src/app/components/FAQ/data/questions.ts b/src/app/components/FAQ/data/questions.ts index c07f4044..1ed41bae 100644 --- a/src/app/components/FAQ/data/questions.ts +++ b/src/app/components/FAQ/data/questions.ts @@ -94,6 +94,12 @@ export const questions = ( title: "Are hardware wallets supported?", content: `

Keystone via QR code is the only hardware wallet supporting Bitcoin Staking. Using any other hardware wallet through any means (such as connection to a software/extension/mobile wallet) can lead to permanent inability to withdraw the stake.

`, }, + { + title: "What are the points?", + content: `

You agree not to engage in any manipulative, fraudulent, dishonest, or abusive activities in connection with points or any distribution of digital assets by us or our affiliates. This includes, but is not limited to, creating multiple accounts to claim additional points or digital assets, using bots or scripts to automate claiming, or participating in any schemes that artificially inflate the number or perceived value of points or digital assets. We or our affiliates may terminate any or all of your points due to such activities, or for breaching any license granted by us, and may disclose privately and publicly why such action was taken.

+
+

You agree not to engage in any manipulative, fraudulent, dishonest, or abusive activities in connection with points or any distribution of digital assets by us or our affiliates. This includes, but is not limited to, creating multiple accounts to claim additional points or digital assets, using bots or scripts to automate claiming, or participating in any schemes that artificially inflate the number or perceived value of points or digital assets. We or our affiliates may terminate any or all of your points due to such activities, or for breaching any license granted by us, and may disclose privately and publicly why such action was taken.

`, + }, ]; if (shouldDisplayTestingMsg()) { questionList.push({ diff --git a/src/app/components/Summary/Summary.tsx b/src/app/components/Summary/Summary.tsx index 851a7cf0..19316a45 100644 --- a/src/app/components/Summary/Summary.tsx +++ b/src/app/components/Summary/Summary.tsx @@ -83,7 +83,7 @@ export const Summary: React.FC = ({ @@ -113,7 +113,10 @@ export const Summary: React.FC = ({
{typeof btcWalletBalanceSat === "number" ? ( -

+

{maxDecimals(satoshiToBtc(btcWalletBalanceSat), 8)} {coinName}

) : ( From 5246d9779e02b3f2a2034f55ee64449122672965 Mon Sep 17 00:00:00 2001 From: jeremy-babylonchain Date: Tue, 10 Sep 2024 19:08:02 +0800 Subject: [PATCH 03/15] hotfix --- src/app/components/Points/StakerPoints.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/app/components/Points/StakerPoints.tsx b/src/app/components/Points/StakerPoints.tsx index 7ccae028..808e3ac2 100644 --- a/src/app/components/Points/StakerPoints.tsx +++ b/src/app/components/Points/StakerPoints.tsx @@ -2,8 +2,6 @@ import { useQuery } from "@tanstack/react-query"; import React from "react"; import { getStakersPoints } from "@/app/api/getStakersPoints"; -import { satoshiToBtc } from "@/utils/btcConversions"; -import { maxDecimals } from "@/utils/maxDecimals"; interface StakerPointsProps { publicKeyNoCoord: string; @@ -32,7 +30,7 @@ export const StakerPoints: React.FC = ({ return (

- {points !== undefined ? maxDecimals(satoshiToBtc(points), 8) : 0} + {points !== undefined ? points : 0}

); From 6e32ae1af1530df477491155864e1ac41c28aba8 Mon Sep 17 00:00:00 2001 From: jeremy-babylonchain Date: Wed, 11 Sep 2024 04:33:26 +0800 Subject: [PATCH 04/15] resolve comments --- src/app/api/getDelegationPoints.ts | 62 +++++++++---------- src/app/components/Staking/Staking.tsx | 8 +-- .../context/api/DelegationsPointsProvider.tsx | 7 +-- 3 files changed, 38 insertions(+), 39 deletions(-) diff --git a/src/app/api/getDelegationPoints.ts b/src/app/api/getDelegationPoints.ts index 34a69072..586b2383 100644 --- a/src/app/api/getDelegationPoints.ts +++ b/src/app/api/getDelegationPoints.ts @@ -24,59 +24,59 @@ export interface PaginatedDelegationsPoints { pagination: Pagination; } -export const getDelegationPoints = async ( +// Get delegation points by staker BTC public key +export const getDelegationPointsByStakerBtcPk = async ( + stakerBtcPk: string, paginationKey?: string, - stakerBtcPk?: string, - stakingTxHashHexes?: string[], ): Promise => { - const params: Record = {}; + const params: Record = { + staker_btc_pk: encode(stakerBtcPk), + }; - if (stakerBtcPk && stakingTxHashHexes && stakingTxHashHexes.length > 0) { - throw new Error( - "Only one of stakerBtcPk or stakingTxHashHexes should be provided", - ); + if (paginationKey && paginationKey !== "") { + params.pagination_key = encode(paginationKey); } - if ( - !stakerBtcPk && - (!stakingTxHashHexes || stakingTxHashHexes.length === 0) - ) { - throw new Error( - "Either stakerBtcPk or stakingTxHashHexes must be provided", - ); - } + const response = await apiWrapper( + "GET", + "/v1/points/staker/delegations", + "Error getting delegation points by staker BTC public key", + params, + ); - if (stakerBtcPk) { - params.staker_btc_pk = encode(stakerBtcPk); - } + return { + data: response.data.data, + pagination: response.data.pagination, + }; +}; +// Get delegation points by staking transaction hash hex +export const getDelegationPointsByStakingTxHashHexes = async ( + stakingTxHashHexes: string[], + paginationKey?: string, +): Promise => { let allDelegationPoints: DelegationPoints[] = []; let nextPaginationKey = paginationKey; do { - const currentParams = { ...params }; + const currentParams: Record = { + staking_tx_hash_hex: stakingTxHashHexes.splice(0, 10), + }; + if (nextPaginationKey && nextPaginationKey !== "") { currentParams.pagination_key = encode(nextPaginationKey); } - if (stakingTxHashHexes && stakingTxHashHexes.length > 0) { - currentParams.staking_tx_hash_hex = stakingTxHashHexes.slice(0, 10); - stakingTxHashHexes = stakingTxHashHexes.slice(10); - } - const response = await apiWrapper( "GET", - "/v1/points/staker/delegations", - "Error getting delegation points", + "/v1/points/staking-tx/delegations", + "Error getting delegation points by staking transaction hashes", currentParams, ); allDelegationPoints = allDelegationPoints.concat(response.data.data); nextPaginationKey = response.data.pagination.next_key; - } while ( - nextPaginationKey || - (stakingTxHashHexes && stakingTxHashHexes.length > 0) - ); + } while (nextPaginationKey || stakingTxHashHexes.length > 0); return { data: allDelegationPoints, diff --git a/src/app/components/Staking/Staking.tsx b/src/app/components/Staking/Staking.tsx index 0950be55..edddf005 100644 --- a/src/app/components/Staking/Staking.tsx +++ b/src/app/components/Staking/Staking.tsx @@ -74,13 +74,13 @@ export const Staking: React.FC = ({ isWalletConnected, onConnect, isLoading, + btcWallet, + btcWalletNetwork, + address, + publicKeyNoCoord, setDelegationsLocalStorage, btcWalletBalanceSat, availableUTXOs, - publicKeyNoCoord, - address, - btcWallet, - btcWalletNetwork, }) => { // Staking form state const [stakingAmountSat, setStakingAmountSat] = useState(0); diff --git a/src/app/context/api/DelegationsPointsProvider.tsx b/src/app/context/api/DelegationsPointsProvider.tsx index 733c5eb8..267904cd 100644 --- a/src/app/context/api/DelegationsPointsProvider.tsx +++ b/src/app/context/api/DelegationsPointsProvider.tsx @@ -2,7 +2,7 @@ import { useQuery } from "@tanstack/react-query"; import React, { createContext, useContext, useEffect, useState } from "react"; import { - getDelegationPoints, + getDelegationPointsByStakingTxHashHexes, PaginatedDelegationsPoints, } from "@/app/api/getDelegationPoints"; import { Delegation } from "@/app/types/delegations"; @@ -50,10 +50,9 @@ export const DelegationsPointsProvider: React.FC< ); do { - const result = await getDelegationPoints( - paginationKey, - undefined, + const result = await getDelegationPointsByStakingTxHashHexes( stakingTxHashHexes, + paginationKey, ); allPoints = [...allPoints, ...result.data]; paginationKey = result.pagination.next_key; From 272fe3d2e284551df48311d8abc8b5a03618ee2a Mon Sep 17 00:00:00 2001 From: jeremy-babylonchain Date: Wed, 11 Sep 2024 17:11:30 +0800 Subject: [PATCH 05/15] resolve comments --- src/app/api/getDelegationPoints.ts | 2 +- src/app/components/FAQ/data/questions.ts | 4 +--- src/app/components/Points/DelegationPoints.tsx | 10 ++++++++-- src/app/components/Points/StakerPoints.tsx | 1 + src/app/components/Summary/Summary.tsx | 2 +- src/app/context/api/DelegationsPointsProvider.tsx | 2 ++ 6 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/app/api/getDelegationPoints.ts b/src/app/api/getDelegationPoints.ts index 586b2383..d24e1592 100644 --- a/src/app/api/getDelegationPoints.ts +++ b/src/app/api/getDelegationPoints.ts @@ -69,7 +69,7 @@ export const getDelegationPointsByStakingTxHashHexes = async ( const response = await apiWrapper( "GET", - "/v1/points/staking-tx/delegations", + "/v1/points/delegations", "Error getting delegation points by staking transaction hashes", currentParams, ); diff --git a/src/app/components/FAQ/data/questions.ts b/src/app/components/FAQ/data/questions.ts index 1ed41bae..975b475c 100644 --- a/src/app/components/FAQ/data/questions.ts +++ b/src/app/components/FAQ/data/questions.ts @@ -96,9 +96,7 @@ export const questions = ( }, { title: "What are the points?", - content: `

You agree not to engage in any manipulative, fraudulent, dishonest, or abusive activities in connection with points or any distribution of digital assets by us or our affiliates. This includes, but is not limited to, creating multiple accounts to claim additional points or digital assets, using bots or scripts to automate claiming, or participating in any schemes that artificially inflate the number or perceived value of points or digital assets. We or our affiliates may terminate any or all of your points due to such activities, or for breaching any license granted by us, and may disclose privately and publicly why such action was taken.

-
-

You agree not to engage in any manipulative, fraudulent, dishonest, or abusive activities in connection with points or any distribution of digital assets by us or our affiliates. This includes, but is not limited to, creating multiple accounts to claim additional points or digital assets, using bots or scripts to automate claiming, or participating in any schemes that artificially inflate the number or perceived value of points or digital assets. We or our affiliates may terminate any or all of your points due to such activities, or for breaching any license granted by us, and may disclose privately and publicly why such action was taken.

`, + content: `

We use points to track staking activity. Points are not blockchain tokens. Points do not, and may never, convert to, accrue to, be used as a basis to calculate, or become tokens, other digital assets, or distributions thereof. Points are virtual calculations with no monetary value. Points do not constitute any currency or property of any type and are not redeemable, refundable, or transferable.

`, }, ]; if (shouldDisplayTestingMsg()) { diff --git a/src/app/components/Points/DelegationPoints.tsx b/src/app/components/Points/DelegationPoints.tsx index 1e9018e7..09b9f946 100644 --- a/src/app/components/Points/DelegationPoints.tsx +++ b/src/app/components/Points/DelegationPoints.tsx @@ -1,4 +1,5 @@ import React from "react"; +import { useMediaQuery } from "usehooks-ts"; import { useDelegationsPoints } from "@/app/context/api/DelegationsPointsProvider"; @@ -10,6 +11,7 @@ export const DelegationPoints: React.FC = ({ stakingTxHash, }) => { const { delegationPoints, isLoading } = useDelegationsPoints(); + const isMobile = useMediaQuery("(max-width:1023px)"); const points = delegationPoints.get(stakingTxHash); @@ -23,8 +25,12 @@ export const DelegationPoints: React.FC = ({ return (
-

- {points !== undefined ? points : 0} +

+ {isMobile + ? `Points: ${points !== undefined ? points : 0}` + : points !== undefined + ? points + : 0}

); diff --git a/src/app/components/Points/StakerPoints.tsx b/src/app/components/Points/StakerPoints.tsx index 808e3ac2..0d145f23 100644 --- a/src/app/components/Points/StakerPoints.tsx +++ b/src/app/components/Points/StakerPoints.tsx @@ -15,6 +15,7 @@ export const StakerPoints: React.FC = ({ queryFn: () => getStakersPoints([publicKeyNoCoord]), enabled: !!publicKeyNoCoord, refetchInterval: 60000, // Refresh every minute + refetchOnWindowFocus: false, }); if (isLoading) { diff --git a/src/app/components/Summary/Summary.tsx b/src/app/components/Summary/Summary.tsx index 19316a45..665ca298 100644 --- a/src/app/components/Summary/Summary.tsx +++ b/src/app/components/Summary/Summary.tsx @@ -83,7 +83,7 @@ export const Summary: React.FC = ({ diff --git a/src/app/context/api/DelegationsPointsProvider.tsx b/src/app/context/api/DelegationsPointsProvider.tsx index 267904cd..3eb793bb 100644 --- a/src/app/context/api/DelegationsPointsProvider.tsx +++ b/src/app/context/api/DelegationsPointsProvider.tsx @@ -65,6 +65,8 @@ export const DelegationsPointsProvider: React.FC< queryKey: ["delegationPoints", publicKeyNoCoord, delegationsAPI], queryFn: fetchAllPoints, enabled: isWalletConnected && delegationsAPI.length > 0, + refetchInterval: 60000, // Refetch every 60 seconds + refetchOnWindowFocus: false, }); useEffect(() => { From 9f0be3578af69f80be592994a5ae5992b3305b0e Mon Sep 17 00:00:00 2001 From: jeremy-babylonchain Date: Wed, 11 Sep 2024 18:19:03 +0800 Subject: [PATCH 06/15] add geo-block handling --- src/app/components/Delegations/Delegation.tsx | 10 +++-- .../components/Points/DelegationPoints.tsx | 2 +- src/app/components/Summary/Summary.tsx | 43 +++++++++++-------- .../context/api/DelegationsPointsProvider.tsx | 8 +++- 4 files changed, 41 insertions(+), 22 deletions(-) diff --git a/src/app/components/Delegations/Delegation.tsx b/src/app/components/Delegations/Delegation.tsx index 0ecd4265..10a72f45 100644 --- a/src/app/components/Delegations/Delegation.tsx +++ b/src/app/components/Delegations/Delegation.tsx @@ -4,6 +4,7 @@ import { FaBitcoin } from "react-icons/fa"; import { IoIosWarning } from "react-icons/io"; import { Tooltip } from "react-tooltip"; +import { useHealthCheck } from "@/app/hooks/useHealthCheck"; import { DelegationState, StakingTx } from "@/app/types/delegations"; import { GlobalParamsVersion } from "@/app/types/globalParams"; import { getNetworkConfig } from "@/config/network.config"; @@ -43,6 +44,7 @@ export const Delegation: React.FC = ({ }) => { const { startTimestamp } = stakingTx; const [currentTime, setCurrentTime] = useState(Date.now()); + const { isApiNormal, isGeoBlocked } = useHealthCheck(); useEffect(() => { const timerId = setInterval(() => { @@ -162,9 +164,11 @@ export const Delegation: React.FC = ({
-
- -
+ {!isApiNormal && !isGeoBlocked && ( +
+ +
+ )}
{generateActionButton()}
diff --git a/src/app/components/Points/DelegationPoints.tsx b/src/app/components/Points/DelegationPoints.tsx index 09b9f946..1a9a0edd 100644 --- a/src/app/components/Points/DelegationPoints.tsx +++ b/src/app/components/Points/DelegationPoints.tsx @@ -11,7 +11,7 @@ export const DelegationPoints: React.FC = ({ stakingTxHash, }) => { const { delegationPoints, isLoading } = useDelegationsPoints(); - const isMobile = useMediaQuery("(max-width:1023px)"); + const isMobile = useMediaQuery("(max-width:1000px)"); const points = delegationPoints.get(stakingTxHash); diff --git a/src/app/components/Summary/Summary.tsx b/src/app/components/Summary/Summary.tsx index 665ca298..9b8d7f5b 100644 --- a/src/app/components/Summary/Summary.tsx +++ b/src/app/components/Summary/Summary.tsx @@ -5,6 +5,7 @@ import { Tooltip } from "react-tooltip"; import { useGlobalParams } from "@/app/context/api/GlobalParamsProvider"; import { useBtcHeight } from "@/app/context/mempool/BtcHeightProvider"; +import { useHealthCheck } from "@/app/hooks/useHealthCheck"; import { getNetworkConfig } from "@/config/network.config"; import { satoshiToBtc } from "@/utils/btcConversions"; import { @@ -36,6 +37,7 @@ export const Summary: React.FC = ({ const btcHeight = useBtcHeight(); const globalParams = useGlobalParams(); + const { isApiNormal, isGeoBlocked } = useHealthCheck(); useMemo(() => { if (!btcHeight || !globalParams.data) { @@ -76,23 +78,30 @@ export const Summary: React.FC = ({

-
-
-
-

Total points

- - - - -
-
- -
-
+ {isApiNormal && !isGeoBlocked && ( + <> +
+
+
+

Total points

+ + + + +
+
+ +
+
+ + )}
diff --git a/src/app/context/api/DelegationsPointsProvider.tsx b/src/app/context/api/DelegationsPointsProvider.tsx index 3eb793bb..4f96f849 100644 --- a/src/app/context/api/DelegationsPointsProvider.tsx +++ b/src/app/context/api/DelegationsPointsProvider.tsx @@ -5,6 +5,7 @@ import { getDelegationPointsByStakingTxHashHexes, PaginatedDelegationsPoints, } from "@/app/api/getDelegationPoints"; +import { useHealthCheck } from "@/app/hooks/useHealthCheck"; import { Delegation } from "@/app/types/delegations"; interface PointsContextType { @@ -40,6 +41,7 @@ export const DelegationsPointsProvider: React.FC< const [delegationPoints, setDelegationPoints] = useState>( new Map(), ); + const { isApiNormal, isGeoBlocked } = useHealthCheck(); const fetchAllPoints = async () => { let allPoints: PaginatedDelegationsPoints["data"] = []; @@ -64,7 +66,11 @@ export const DelegationsPointsProvider: React.FC< const { data, isLoading, error } = useQuery({ queryKey: ["delegationPoints", publicKeyNoCoord, delegationsAPI], queryFn: fetchAllPoints, - enabled: isWalletConnected && delegationsAPI.length > 0, + enabled: + isWalletConnected && + delegationsAPI.length > 0 && + isApiNormal && + !isGeoBlocked, refetchInterval: 60000, // Refetch every 60 seconds refetchOnWindowFocus: false, }); From 4be36d52c0ab8c3f0bf2e101d703ca0260801b49 Mon Sep 17 00:00:00 2001 From: jeremy-babylonchain Date: Thu, 12 Sep 2024 13:54:28 +0800 Subject: [PATCH 07/15] add point url --- .env.example | 1 + .github/workflows/publish.yaml | 1 + Dockerfile | 3 +++ README.md | 2 ++ src/app/api/apiWrapper.ts | 5 +++-- src/app/api/getDelegationPoints.ts | 2 ++ src/app/api/getDelegations.ts | 1 + src/app/api/getFinalityProviders.ts | 1 + src/app/api/getGlobalParams.ts | 1 + src/app/api/getStakers.ts | 1 + src/app/api/getStakersPoints.ts | 1 + src/app/api/getStats.ts | 7 ++++++- src/app/api/getUnbondingEligibility.ts | 1 + src/app/api/postFilterOrdinals.ts | 1 + src/app/api/postUnbonding.ts | 1 + 15 files changed, 26 insertions(+), 3 deletions(-) diff --git a/.env.example b/.env.example index 71b4231d..d08db61f 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,5 @@ NEXT_PUBLIC_MEMPOOL_API=https://mempool.space NEXT_PUBLIC_API_URL=https://staking-api.testnet.babylonchain.io +NEXT_PUBLIC_POINTS_API_URL=https://points.testnet.babylonchain.io NEXT_PUBLIC_NETWORK=signet NEXT_PUBLIC_DISPLAY_TESTING_MESSAGES=true \ No newline at end of file diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 2c9f4030..1c946ac5 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -37,6 +37,7 @@ jobs: build-args: | NEXT_PUBLIC_MEMPOOL_API=${{ vars.NEXT_PUBLIC_MEMPOOL_API }} NEXT_PUBLIC_API_URL=${{ vars.NEXT_PUBLIC_API_URL }} + NEXT_PUBLIC_POINTS_API_URL=${{ vars.NEXT_PUBLIC_POINTS_API_URL }} NEXT_PUBLIC_NETWORK=${{ vars.NEXT_PUBLIC_NETWORK }} NEXT_PUBLIC_DISPLAY_TESTING_MESSAGES=${{ vars.NEXT_PUBLIC_DISPLAY_TESTING_MESSAGES }} diff --git a/Dockerfile b/Dockerfile index 08a69e30..e9b5ed96 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,6 +20,9 @@ ENV NEXT_PUBLIC_MEMPOOL_API=${NEXT_PUBLIC_MEMPOOL_API} ARG NEXT_PUBLIC_API_URL ENV NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL} +ARG NEXT_PUBLIC_POINTS_API_URL +ENV NEXT_PUBLIC_POINTS_API_URL=${NEXT_PUBLIC_POINTS_API_URL} + ARG NEXT_PUBLIC_NETWORK ENV NEXT_PUBLIC_NETWORK=${NEXT_PUBLIC_NETWORK} diff --git a/README.md b/README.md index f370b0ef..03db1c9d 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,8 @@ where, node queries - `NEXT_PUBLIC_API_URL` specifies the back-end API to use for the staking system queries +- `NEXT_PUBLIC_POINTS_API_URL` specifies the Points API to use for the points + system - `NEXT_PUBLIC_NETWORK` specifies the BTC network environment - `NEXT_PUBLIC_DISPLAY_TESTING_MESSAGES` boolean value to indicate whether display testing network related message. Default to true diff --git a/src/app/api/apiWrapper.ts b/src/app/api/apiWrapper.ts index 85742e18..032aab35 100644 --- a/src/app/api/apiWrapper.ts +++ b/src/app/api/apiWrapper.ts @@ -2,7 +2,8 @@ import axios from "axios"; export const apiWrapper = async ( method: "GET" | "POST", - url: string, + apiUrl: string, + endpoint: string, generalErrorMessage: string, params?: any, timeout?: number, @@ -23,7 +24,7 @@ export const apiWrapper = async ( try { // destructure params in case of post request response = await handler( - `${process.env.NEXT_PUBLIC_API_URL}${url}`, + `${apiUrl}${endpoint}`, method === "POST" ? { ...params } : { diff --git a/src/app/api/getDelegationPoints.ts b/src/app/api/getDelegationPoints.ts index d24e1592..d97469f0 100644 --- a/src/app/api/getDelegationPoints.ts +++ b/src/app/api/getDelegationPoints.ts @@ -39,6 +39,7 @@ export const getDelegationPointsByStakerBtcPk = async ( const response = await apiWrapper( "GET", + process.env.NEXT_PUBLIC_POINTS_API_URL || "", "/v1/points/staker/delegations", "Error getting delegation points by staker BTC public key", params, @@ -69,6 +70,7 @@ export const getDelegationPointsByStakingTxHashHexes = async ( const response = await apiWrapper( "GET", + process.env.NEXT_PUBLIC_POINTS_API_URL || "", "/v1/points/delegations", "Error getting delegation points by staking transaction hashes", currentParams, diff --git a/src/app/api/getDelegations.ts b/src/app/api/getDelegations.ts index 135caceb..a1862b9d 100644 --- a/src/app/api/getDelegations.ts +++ b/src/app/api/getDelegations.ts @@ -59,6 +59,7 @@ export const getDelegations = async ( const response = await apiWrapper( "GET", + process.env.NEXT_PUBLIC_API_URL || "", "/v1/staker/delegations", "Error getting delegations", params, diff --git a/src/app/api/getFinalityProviders.ts b/src/app/api/getFinalityProviders.ts index 59941156..6eaf92f0 100644 --- a/src/app/api/getFinalityProviders.ts +++ b/src/app/api/getFinalityProviders.ts @@ -47,6 +47,7 @@ export const getFinalityProviders = async ( const response = await apiWrapper( "GET", + process.env.NEXT_PUBLIC_API_URL || "", "/v1/finality-providers", "Error getting finality providers", params, diff --git a/src/app/api/getGlobalParams.ts b/src/app/api/getGlobalParams.ts index 97c180c6..4f0443f0 100644 --- a/src/app/api/getGlobalParams.ts +++ b/src/app/api/getGlobalParams.ts @@ -26,6 +26,7 @@ interface GlobalParamsDataResponse { export const getGlobalParams = async (): Promise => { const { data } = (await apiWrapper( "GET", + process.env.NEXT_PUBLIC_API_URL || "", "/v1/global-params", "Error getting global params", )) as AxiosResponse<{ data: GlobalParamsDataResponse }>; diff --git a/src/app/api/getStakers.ts b/src/app/api/getStakers.ts index 218ac416..dabf7b6a 100644 --- a/src/app/api/getStakers.ts +++ b/src/app/api/getStakers.ts @@ -30,6 +30,7 @@ export const getStakers = async (): Promise => { const response = await apiWrapper( "GET", + process.env.NEXT_PUBLIC_API_URL || "", "/v1/stats/staker", "Error getting stakers", params, diff --git a/src/app/api/getStakersPoints.ts b/src/app/api/getStakersPoints.ts index cd86bf8e..1d583b17 100644 --- a/src/app/api/getStakersPoints.ts +++ b/src/app/api/getStakersPoints.ts @@ -19,6 +19,7 @@ export const getStakersPoints = async ( const response = await apiWrapper( "GET", + process.env.NEXT_PUBLIC_POINTS_API_URL || "", "/v1/points/stakers", "Error getting staker points", params, diff --git a/src/app/api/getStats.ts b/src/app/api/getStats.ts index 45a50e62..43306dff 100644 --- a/src/app/api/getStats.ts +++ b/src/app/api/getStats.ts @@ -16,7 +16,12 @@ interface StatsAPI { } export const getStats = async (): Promise => { - const response = await apiWrapper("GET", "/v1/stats", "Error getting stats"); + const response = await apiWrapper( + "GET", + process.env.NEXT_PUBLIC_API_URL || "", + "/v1/stats", + "Error getting stats", + ); const statsAPIResponse: StatsAPIResponse = response.data; const statsAPI: StatsAPI = statsAPIResponse.data; diff --git a/src/app/api/getUnbondingEligibility.ts b/src/app/api/getUnbondingEligibility.ts index 6773cdbf..d319e934 100644 --- a/src/app/api/getUnbondingEligibility.ts +++ b/src/app/api/getUnbondingEligibility.ts @@ -13,6 +13,7 @@ export const getUnbondingEligibility = async (txID: string) => { const response = await apiWrapper( "GET", + process.env.NEXT_PUBLIC_API_URL || "", "/v1/unbonding/eligibility", "Error checking unbonding eligibility", params, diff --git a/src/app/api/postFilterOrdinals.ts b/src/app/api/postFilterOrdinals.ts index 306a452c..3dfdfcba 100644 --- a/src/app/api/postFilterOrdinals.ts +++ b/src/app/api/postFilterOrdinals.ts @@ -21,6 +21,7 @@ export const postVerifyUtxoOrdinals = async ( utxoChunks.map((chunk) => apiWrapper( "POST", + process.env.NEXT_PUBLIC_API_URL || "", "/v1/ordinals/verify-utxos", "Error verifying utxos ordinals", { diff --git a/src/app/api/postUnbonding.ts b/src/app/api/postUnbonding.ts index f4e216af..574e0773 100644 --- a/src/app/api/postUnbonding.ts +++ b/src/app/api/postUnbonding.ts @@ -22,6 +22,7 @@ export const postUnbonding = async ( const response = await apiWrapper( "POST", + process.env.NEXT_PUBLIC_API_URL || "", "/v1/unbonding", "Error submitting unbonding request", payload, From 32c7c988462f27173e841e41455398b3c355b3fd Mon Sep 17 00:00:00 2001 From: jeremy-babylonchain Date: Thu, 12 Sep 2024 18:05:35 +0800 Subject: [PATCH 08/15] update delegation points function and fix css --- src/app/api/getDelegationPoints.ts | 40 ++--- src/app/components/Delegations/Delegation.tsx | 2 +- .../components/Delegations/Delegations.tsx | 6 +- src/app/components/Summary/Summary.tsx | 149 +++++++++--------- .../context/api/DelegationsPointsProvider.tsx | 27 ++-- 5 files changed, 106 insertions(+), 118 deletions(-) diff --git a/src/app/api/getDelegationPoints.ts b/src/app/api/getDelegationPoints.ts index d97469f0..f851e363 100644 --- a/src/app/api/getDelegationPoints.ts +++ b/src/app/api/getDelegationPoints.ts @@ -24,6 +24,10 @@ export interface PaginatedDelegationsPoints { pagination: Pagination; } +export interface DelegationsPoints { + data: DelegationPoints[]; +} + // Get delegation points by staker BTC public key export const getDelegationPointsByStakerBtcPk = async ( stakerBtcPk: string, @@ -54,34 +58,16 @@ export const getDelegationPointsByStakerBtcPk = async ( // Get delegation points by staking transaction hash hex export const getDelegationPointsByStakingTxHashHexes = async ( stakingTxHashHexes: string[], - paginationKey?: string, -): Promise => { - let allDelegationPoints: DelegationPoints[] = []; - let nextPaginationKey = paginationKey; - - do { - const currentParams: Record = { - staking_tx_hash_hex: stakingTxHashHexes.splice(0, 10), - }; - - if (nextPaginationKey && nextPaginationKey !== "") { - currentParams.pagination_key = encode(nextPaginationKey); - } - - const response = await apiWrapper( - "GET", - process.env.NEXT_PUBLIC_POINTS_API_URL || "", - "/v1/points/delegations", - "Error getting delegation points by staking transaction hashes", - currentParams, - ); - - allDelegationPoints = allDelegationPoints.concat(response.data.data); - nextPaginationKey = response.data.pagination.next_key; - } while (nextPaginationKey || stakingTxHashHexes.length > 0); +): Promise => { + const response = await apiWrapper( + "POST", + process.env.NEXT_PUBLIC_POINTS_API_URL || "", + "/v1/points/delegations", + "Error getting delegation points by staking transaction hashes", + { staking_tx_hash_hex: stakingTxHashHexes }, + ); return { - data: allDelegationPoints, - pagination: { next_key: nextPaginationKey || "" }, + data: response.data, }; }; diff --git a/src/app/components/Delegations/Delegation.tsx b/src/app/components/Delegations/Delegation.tsx index 10a72f45..310a8724 100644 --- a/src/app/components/Delegations/Delegation.tsx +++ b/src/app/components/Delegations/Delegation.tsx @@ -164,7 +164,7 @@ export const Delegation: React.FC = ({
- {!isApiNormal && !isGeoBlocked && ( + {isApiNormal && !isGeoBlocked && (
diff --git a/src/app/components/Delegations/Delegations.tsx b/src/app/components/Delegations/Delegations.tsx index 0ecc6a93..591a31f6 100644 --- a/src/app/components/Delegations/Delegations.tsx +++ b/src/app/components/Delegations/Delegations.tsx @@ -7,6 +7,7 @@ import { SignPsbtTransaction } from "@/app/common/utils/psbt"; import { LoadingTableList } from "@/app/components/Loading/Loading"; import { DelegationsPointsProvider } from "@/app/context/api/DelegationsPointsProvider"; import { useError } from "@/app/context/Error/ErrorContext"; +import { useHealthCheck } from "@/app/hooks/useHealthCheck"; import { QueryMeta } from "@/app/types/api"; import { Delegation as DelegationInterface, @@ -95,6 +96,7 @@ const DelegationsContent: React.FC = ({ const [txID, setTxID] = useState(""); const [modalMode, setModalMode] = useState(); const { showError } = useError(); + const { isApiNormal, isGeoBlocked } = useHealthCheck(); // Local storage state for intermediate delegations (withdrawing, unbonding) const intermediateDelegationsLocalStorageKey = @@ -276,7 +278,9 @@ const DelegationsContent: React.FC = ({

Inception

Transaction hash

Status

-

Points

+ {isApiNormal && !isGeoBlocked && ( +

Points

+ )}

Action

= ({

Your staking summary

-
-
-
-

Total staked

- - - - -
-
- -

- {totalStakedSat - ? maxDecimals(satoshiToBtc(totalStakedSat), 8) - : 0}{" "} - {coinName} -

+
+
+
+
+

Total staked

+ + + + +
+
+ +

+ {totalStakedSat + ? maxDecimals(satoshiToBtc(totalStakedSat), 8) + : 0}{" "} + {coinName} +

+
-
- {isApiNormal && !isGeoBlocked && ( - <> -
-
-
-

Total points

- - - - + {isApiNormal && !isGeoBlocked && ( + <> +
+
+
+

Total points

+ + + + +
+
+ +
-
- -
-
- - )} -
-
-
-

Stakable Balance

- + )} +
+
+
+

Stakable Balance

+ - - - -
-
- - {typeof btcWalletBalanceSat === "number" ? ( -

- {maxDecimals(satoshiToBtc(btcWalletBalanceSat), 8)} {coinName} -

- ) : ( - - )} + + + +
+
+ + {typeof btcWalletBalanceSat === "number" ? ( +

+ {maxDecimals(satoshiToBtc(btcWalletBalanceSat), 8)}{" "} + {coinName} +

+ ) : ( + + )} +
diff --git a/src/app/context/api/DelegationsPointsProvider.tsx b/src/app/context/api/DelegationsPointsProvider.tsx index 4f96f849..ebd2f437 100644 --- a/src/app/context/api/DelegationsPointsProvider.tsx +++ b/src/app/context/api/DelegationsPointsProvider.tsx @@ -1,10 +1,7 @@ import { useQuery } from "@tanstack/react-query"; import React, { createContext, useContext, useEffect, useState } from "react"; -import { - getDelegationPointsByStakingTxHashHexes, - PaginatedDelegationsPoints, -} from "@/app/api/getDelegationPoints"; +import { getDelegationPointsByStakingTxHashHexes } from "@/app/api/getDelegationPoints"; import { useHealthCheck } from "@/app/hooks/useHealthCheck"; import { Delegation } from "@/app/types/delegations"; @@ -44,23 +41,21 @@ export const DelegationsPointsProvider: React.FC< const { isApiNormal, isGeoBlocked } = useHealthCheck(); const fetchAllPoints = async () => { - let allPoints: PaginatedDelegationsPoints["data"] = []; - let paginationKey = ""; - const stakingTxHashHexes = delegationsAPI.map( (delegation) => delegation.stakingTxHashHex, ); - do { - const result = await getDelegationPointsByStakingTxHashHexes( - stakingTxHashHexes, - paginationKey, - ); - allPoints = [...allPoints, ...result.data]; - paginationKey = result.pagination.next_key; - } while (paginationKey !== ""); + const chunkSize = 100; + const chunks = []; + for (let i = 0; i < stakingTxHashHexes.length; i += chunkSize) { + chunks.push(stakingTxHashHexes.slice(i, i + chunkSize)); + } + + const results = await Promise.all( + chunks.map((chunk) => getDelegationPointsByStakingTxHashHexes(chunk)), + ); - return allPoints; + return results.flatMap((result) => result.data); }; const { data, isLoading, error } = useQuery({ From 4fbc1d68a18810648afbe222da265c66c7d0b404 Mon Sep 17 00:00:00 2001 From: jeremy-babylonchain Date: Thu, 12 Sep 2024 18:33:44 +0800 Subject: [PATCH 09/15] resolve comments --- src/app/api/apiWrapper.ts | 5 +-- src/app/api/getDelegations.ts | 1 - src/app/api/getFinalityProviders.ts | 1 - src/app/api/getGlobalParams.ts | 1 - .../{getDelegationPoints.ts => getPoints.ts} | 41 ++++++++--------- src/app/api/getStakers.ts | 1 - src/app/api/getStakersPoints.ts | 29 ------------ src/app/api/getStats.ts | 7 +-- src/app/api/getUnbondingEligibility.ts | 1 - src/app/api/pointsApiWrapper.ts | 45 +++++++++++++++++++ src/app/api/postFilterOrdinals.ts | 1 - src/app/api/postUnbonding.ts | 1 - src/app/components/Points/StakerPoints.tsx | 2 +- .../context/api/DelegationsPointsProvider.tsx | 2 +- 14 files changed, 69 insertions(+), 69 deletions(-) rename src/app/api/{getDelegationPoints.ts => getPoints.ts} (54%) delete mode 100644 src/app/api/getStakersPoints.ts create mode 100644 src/app/api/pointsApiWrapper.ts diff --git a/src/app/api/apiWrapper.ts b/src/app/api/apiWrapper.ts index 032aab35..ded5ed9f 100644 --- a/src/app/api/apiWrapper.ts +++ b/src/app/api/apiWrapper.ts @@ -2,8 +2,7 @@ import axios from "axios"; export const apiWrapper = async ( method: "GET" | "POST", - apiUrl: string, - endpoint: string, + path: string, generalErrorMessage: string, params?: any, timeout?: number, @@ -24,7 +23,7 @@ export const apiWrapper = async ( try { // destructure params in case of post request response = await handler( - `${apiUrl}${endpoint}`, + `${process.env.NEXT_PUBLIC_API_URL}${path}`, method === "POST" ? { ...params } : { diff --git a/src/app/api/getDelegations.ts b/src/app/api/getDelegations.ts index a1862b9d..135caceb 100644 --- a/src/app/api/getDelegations.ts +++ b/src/app/api/getDelegations.ts @@ -59,7 +59,6 @@ export const getDelegations = async ( const response = await apiWrapper( "GET", - process.env.NEXT_PUBLIC_API_URL || "", "/v1/staker/delegations", "Error getting delegations", params, diff --git a/src/app/api/getFinalityProviders.ts b/src/app/api/getFinalityProviders.ts index 6eaf92f0..59941156 100644 --- a/src/app/api/getFinalityProviders.ts +++ b/src/app/api/getFinalityProviders.ts @@ -47,7 +47,6 @@ export const getFinalityProviders = async ( const response = await apiWrapper( "GET", - process.env.NEXT_PUBLIC_API_URL || "", "/v1/finality-providers", "Error getting finality providers", params, diff --git a/src/app/api/getGlobalParams.ts b/src/app/api/getGlobalParams.ts index 4f0443f0..97c180c6 100644 --- a/src/app/api/getGlobalParams.ts +++ b/src/app/api/getGlobalParams.ts @@ -26,7 +26,6 @@ interface GlobalParamsDataResponse { export const getGlobalParams = async (): Promise => { const { data } = (await apiWrapper( "GET", - process.env.NEXT_PUBLIC_API_URL || "", "/v1/global-params", "Error getting global params", )) as AxiosResponse<{ data: GlobalParamsDataResponse }>; diff --git a/src/app/api/getDelegationPoints.ts b/src/app/api/getPoints.ts similarity index 54% rename from src/app/api/getDelegationPoints.ts rename to src/app/api/getPoints.ts index f851e363..f2058602 100644 --- a/src/app/api/getDelegationPoints.ts +++ b/src/app/api/getPoints.ts @@ -2,7 +2,12 @@ import { encode } from "url-safe-base64"; import { Pagination } from "../types/api"; -import { apiWrapper } from "./apiWrapper"; +import { pointsApiWrapper } from "./pointsApiWrapper"; + +export interface StakerPoints { + staker_btc_pk: string; + points: number; +} export interface DelegationPoints { staking_tx_hash_hex: string; @@ -28,40 +33,32 @@ export interface DelegationsPoints { data: DelegationPoints[]; } -// Get delegation points by staker BTC public key -export const getDelegationPointsByStakerBtcPk = async ( - stakerBtcPk: string, - paginationKey?: string, -): Promise => { - const params: Record = { - staker_btc_pk: encode(stakerBtcPk), - }; +export const getStakersPoints = async ( + stakerBtcPk: string[], +): Promise => { + const params: Record = {}; - if (paginationKey && paginationKey !== "") { - params.pagination_key = encode(paginationKey); - } + params.staker_btc_pk = + stakerBtcPk.length > 1 + ? stakerBtcPk.map(encode).join(",") + : encode(stakerBtcPk[0]); - const response = await apiWrapper( + const response = await pointsApiWrapper( "GET", - process.env.NEXT_PUBLIC_POINTS_API_URL || "", - "/v1/points/staker/delegations", - "Error getting delegation points by staker BTC public key", + "/v1/points/stakers", + "Error getting staker points", params, ); - return { - data: response.data.data, - pagination: response.data.pagination, - }; + return response.data; }; // Get delegation points by staking transaction hash hex export const getDelegationPointsByStakingTxHashHexes = async ( stakingTxHashHexes: string[], ): Promise => { - const response = await apiWrapper( + const response = await pointsApiWrapper( "POST", - process.env.NEXT_PUBLIC_POINTS_API_URL || "", "/v1/points/delegations", "Error getting delegation points by staking transaction hashes", { staking_tx_hash_hex: stakingTxHashHexes }, diff --git a/src/app/api/getStakers.ts b/src/app/api/getStakers.ts index dabf7b6a..218ac416 100644 --- a/src/app/api/getStakers.ts +++ b/src/app/api/getStakers.ts @@ -30,7 +30,6 @@ export const getStakers = async (): Promise => { const response = await apiWrapper( "GET", - process.env.NEXT_PUBLIC_API_URL || "", "/v1/stats/staker", "Error getting stakers", params, diff --git a/src/app/api/getStakersPoints.ts b/src/app/api/getStakersPoints.ts deleted file mode 100644 index 1d583b17..00000000 --- a/src/app/api/getStakersPoints.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { encode } from "url-safe-base64"; - -import { apiWrapper } from "./apiWrapper"; - -export interface StakerPoints { - staker_btc_pk: string; - points: number; -} - -export const getStakersPoints = async ( - stakerBtcPk: string[], -): Promise => { - const params: Record = {}; - - params.staker_btc_pk = - stakerBtcPk.length > 1 - ? stakerBtcPk.map(encode).join(",") - : encode(stakerBtcPk[0]); - - const response = await apiWrapper( - "GET", - process.env.NEXT_PUBLIC_POINTS_API_URL || "", - "/v1/points/stakers", - "Error getting staker points", - params, - ); - - return response.data; -}; diff --git a/src/app/api/getStats.ts b/src/app/api/getStats.ts index 43306dff..45a50e62 100644 --- a/src/app/api/getStats.ts +++ b/src/app/api/getStats.ts @@ -16,12 +16,7 @@ interface StatsAPI { } export const getStats = async (): Promise => { - const response = await apiWrapper( - "GET", - process.env.NEXT_PUBLIC_API_URL || "", - "/v1/stats", - "Error getting stats", - ); + const response = await apiWrapper("GET", "/v1/stats", "Error getting stats"); const statsAPIResponse: StatsAPIResponse = response.data; const statsAPI: StatsAPI = statsAPIResponse.data; diff --git a/src/app/api/getUnbondingEligibility.ts b/src/app/api/getUnbondingEligibility.ts index d319e934..6773cdbf 100644 --- a/src/app/api/getUnbondingEligibility.ts +++ b/src/app/api/getUnbondingEligibility.ts @@ -13,7 +13,6 @@ export const getUnbondingEligibility = async (txID: string) => { const response = await apiWrapper( "GET", - process.env.NEXT_PUBLIC_API_URL || "", "/v1/unbonding/eligibility", "Error checking unbonding eligibility", params, diff --git a/src/app/api/pointsApiWrapper.ts b/src/app/api/pointsApiWrapper.ts new file mode 100644 index 00000000..c48b9566 --- /dev/null +++ b/src/app/api/pointsApiWrapper.ts @@ -0,0 +1,45 @@ +import axios from "axios"; + +export const pointsApiWrapper = async ( + method: "GET" | "POST", + path: string, + generalErrorMessage: string, + params?: any, + 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"); + } + + try { + // destructure params in case of post request + response = await handler( + `${process.env.NEXT_PUBLIC_POINTS_API_URL}${path}`, + method === "POST" + ? { ...params } + : { + params, + }, + { + timeout: timeout || 0, // 0 is no timeout + }, + ); + } catch (error) { + if (axios.isAxiosError(error)) { + const message = error?.response?.data?.message; + throw new Error(message || generalErrorMessage); + } else { + throw new Error(generalErrorMessage); + } + } + return response; +}; diff --git a/src/app/api/postFilterOrdinals.ts b/src/app/api/postFilterOrdinals.ts index 3dfdfcba..306a452c 100644 --- a/src/app/api/postFilterOrdinals.ts +++ b/src/app/api/postFilterOrdinals.ts @@ -21,7 +21,6 @@ export const postVerifyUtxoOrdinals = async ( utxoChunks.map((chunk) => apiWrapper( "POST", - process.env.NEXT_PUBLIC_API_URL || "", "/v1/ordinals/verify-utxos", "Error verifying utxos ordinals", { diff --git a/src/app/api/postUnbonding.ts b/src/app/api/postUnbonding.ts index 574e0773..f4e216af 100644 --- a/src/app/api/postUnbonding.ts +++ b/src/app/api/postUnbonding.ts @@ -22,7 +22,6 @@ export const postUnbonding = async ( const response = await apiWrapper( "POST", - process.env.NEXT_PUBLIC_API_URL || "", "/v1/unbonding", "Error submitting unbonding request", payload, diff --git a/src/app/components/Points/StakerPoints.tsx b/src/app/components/Points/StakerPoints.tsx index 0d145f23..3ba4e58f 100644 --- a/src/app/components/Points/StakerPoints.tsx +++ b/src/app/components/Points/StakerPoints.tsx @@ -1,7 +1,7 @@ import { useQuery } from "@tanstack/react-query"; import React from "react"; -import { getStakersPoints } from "@/app/api/getStakersPoints"; +import { getStakersPoints } from "@/app/api/getPoints"; interface StakerPointsProps { publicKeyNoCoord: string; diff --git a/src/app/context/api/DelegationsPointsProvider.tsx b/src/app/context/api/DelegationsPointsProvider.tsx index ebd2f437..3560b730 100644 --- a/src/app/context/api/DelegationsPointsProvider.tsx +++ b/src/app/context/api/DelegationsPointsProvider.tsx @@ -1,7 +1,7 @@ import { useQuery } from "@tanstack/react-query"; import React, { createContext, useContext, useEffect, useState } from "react"; -import { getDelegationPointsByStakingTxHashHexes } from "@/app/api/getDelegationPoints"; +import { getDelegationPointsByStakingTxHashHexes } from "@/app/api/getPoints"; import { useHealthCheck } from "@/app/hooks/useHealthCheck"; import { Delegation } from "@/app/types/delegations"; From bc3f7a8b3107d2d874231f8927f3ec567ad3f56c Mon Sep 17 00:00:00 2001 From: jeremy-babylonchain Date: Fri, 13 Sep 2024 16:42:52 +0800 Subject: [PATCH 10/15] resolve comments --- src/app/api/getPoints.ts | 8 ++++---- src/app/components/Points/DelegationPoints.tsx | 9 ++------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/app/api/getPoints.ts b/src/app/api/getPoints.ts index f2058602..cd3cfc20 100644 --- a/src/app/api/getPoints.ts +++ b/src/app/api/getPoints.ts @@ -34,14 +34,14 @@ export interface DelegationsPoints { } export const getStakersPoints = async ( - stakerBtcPk: string[], + stakerBtcPks: string[], ): Promise => { const params: Record = {}; params.staker_btc_pk = - stakerBtcPk.length > 1 - ? stakerBtcPk.map(encode).join(",") - : encode(stakerBtcPk[0]); + stakerBtcPks.length > 1 + ? stakerBtcPks.map(encode).join(",") + : encode(stakerBtcPks[0]); const response = await pointsApiWrapper( "GET", diff --git a/src/app/components/Points/DelegationPoints.tsx b/src/app/components/Points/DelegationPoints.tsx index 1a9a0edd..387cef12 100644 --- a/src/app/components/Points/DelegationPoints.tsx +++ b/src/app/components/Points/DelegationPoints.tsx @@ -1,5 +1,4 @@ import React from "react"; -import { useMediaQuery } from "usehooks-ts"; import { useDelegationsPoints } from "@/app/context/api/DelegationsPointsProvider"; @@ -11,7 +10,6 @@ export const DelegationPoints: React.FC = ({ stakingTxHash, }) => { const { delegationPoints, isLoading } = useDelegationsPoints(); - const isMobile = useMediaQuery("(max-width:1000px)"); const points = delegationPoints.get(stakingTxHash); @@ -26,11 +24,8 @@ export const DelegationPoints: React.FC = ({ return (

- {isMobile - ? `Points: ${points !== undefined ? points : 0}` - : points !== undefined - ? points - : 0} + Points: + {points !== undefined ? points : 0}

); From 3c02374b396f52f1e05a8b33190752d024026f22 Mon Sep 17 00:00:00 2001 From: jeremy-babylonchain Date: Fri, 13 Sep 2024 17:06:55 +0800 Subject: [PATCH 11/15] resolve comments --- src/app/components/Delegations/Delegations.tsx | 1 + src/app/context/api/DelegationsPointsProvider.tsx | 11 +++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/app/components/Delegations/Delegations.tsx b/src/app/components/Delegations/Delegations.tsx index 591a31f6..2a47465e 100644 --- a/src/app/components/Delegations/Delegations.tsx +++ b/src/app/components/Delegations/Delegations.tsx @@ -62,6 +62,7 @@ export const Delegations: React.FC = ({ publicKeyNoCoord={publicKeyNoCoord} delegationsAPI={delegationsAPI} isWalletConnected={isWalletConnected} + address={address} > = ({ children, publicKeyNoCoord, delegationsAPI, isWalletConnected }) => { +> = ({ + children, + publicKeyNoCoord, + delegationsAPI, + isWalletConnected, + address, +}) => { const [delegationPoints, setDelegationPoints] = useState>( new Map(), ); @@ -59,7 +66,7 @@ export const DelegationsPointsProvider: React.FC< }; const { data, isLoading, error } = useQuery({ - queryKey: ["delegationPoints", publicKeyNoCoord, delegationsAPI], + queryKey: ["delegationPoints", address, publicKeyNoCoord], queryFn: fetchAllPoints, enabled: isWalletConnected && From 9e4466645851bbd9cec1287cce821ec6acdf18ec Mon Sep 17 00:00:00 2001 From: jeremy-babylonchain Date: Fri, 13 Sep 2024 18:21:28 +0800 Subject: [PATCH 12/15] resolve comments --- src/app/api/getPoints.ts | 4 +-- .../context/api/DelegationsPointsProvider.tsx | 28 +++++++++++++------ 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/app/api/getPoints.ts b/src/app/api/getPoints.ts index cd3cfc20..6666d20d 100644 --- a/src/app/api/getPoints.ts +++ b/src/app/api/getPoints.ts @@ -64,7 +64,5 @@ export const getDelegationPointsByStakingTxHashHexes = async ( { staking_tx_hash_hex: stakingTxHashHexes }, ); - return { - data: response.data, - }; + return response.data; }; diff --git a/src/app/context/api/DelegationsPointsProvider.tsx b/src/app/context/api/DelegationsPointsProvider.tsx index d90d50ab..1e4ee960 100644 --- a/src/app/context/api/DelegationsPointsProvider.tsx +++ b/src/app/context/api/DelegationsPointsProvider.tsx @@ -33,6 +33,8 @@ interface DelegationsPointsProviderProps { address: string; } +const MAX_DELEGATION_POINTS_BATCH_SIZE = 60; + export const DelegationsPointsProvider: React.FC< DelegationsPointsProviderProps > = ({ @@ -48,14 +50,19 @@ export const DelegationsPointsProvider: React.FC< const { isApiNormal, isGeoBlocked } = useHealthCheck(); const fetchAllPoints = async () => { - const stakingTxHashHexes = delegationsAPI.map( - (delegation) => delegation.stakingTxHashHex, - ); + const stakingTxHashHexes = delegationsAPI + .filter((delegation) => !delegation.isOverflow) + .map((delegation) => delegation.stakingTxHashHex); - const chunkSize = 100; const chunks = []; - for (let i = 0; i < stakingTxHashHexes.length; i += chunkSize) { - chunks.push(stakingTxHashHexes.slice(i, i + chunkSize)); + for ( + let i = 0; + i < stakingTxHashHexes.length; + i += MAX_DELEGATION_POINTS_BATCH_SIZE + ) { + chunks.push( + stakingTxHashHexes.slice(i, i + MAX_DELEGATION_POINTS_BATCH_SIZE), + ); } const results = await Promise.all( @@ -73,7 +80,7 @@ export const DelegationsPointsProvider: React.FC< delegationsAPI.length > 0 && isApiNormal && !isGeoBlocked, - refetchInterval: 60000, // Refetch every 60 seconds + refetchInterval: 300000, // Refetch every 5 minutes refetchOnWindowFocus: false, }); @@ -81,7 +88,12 @@ export const DelegationsPointsProvider: React.FC< if (data) { const newDelegationPoints = new Map(); data.forEach((point) => { - newDelegationPoints.set(point.staking_tx_hash_hex, point.staker.points); + if (point) { + newDelegationPoints.set( + point.staking_tx_hash_hex, + point.staker.points, + ); + } }); setDelegationPoints(newDelegationPoints); } From ba0caec10c178d85a6d544bc2f5658112c8d0125 Mon Sep 17 00:00:00 2001 From: jeremy-babylonchain Date: Sat, 14 Sep 2024 19:01:28 +0700 Subject: [PATCH 13/15] fix getStakers --- src/app/api/getPoints.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/app/api/getPoints.ts b/src/app/api/getPoints.ts index 6666d20d..6619885e 100644 --- a/src/app/api/getPoints.ts +++ b/src/app/api/getPoints.ts @@ -36,12 +36,11 @@ export interface DelegationsPoints { export const getStakersPoints = async ( stakerBtcPks: string[], ): Promise => { - const params: Record = {}; + const params = new URLSearchParams(); - params.staker_btc_pk = - stakerBtcPks.length > 1 - ? stakerBtcPks.map(encode).join(",") - : encode(stakerBtcPks[0]); + stakerBtcPks.forEach((pk) => { + params.append("staker_btc_pk", encode(pk)); + }); const response = await pointsApiWrapper( "GET", From be52fbcedcb0e244f7376b7f94ffb72d81be7f09 Mon Sep 17 00:00:00 2001 From: jeremy-babylonchain Date: Sat, 14 Sep 2024 19:03:25 +0700 Subject: [PATCH 14/15] adjust refetch interval --- src/app/components/Points/StakerPoints.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/components/Points/StakerPoints.tsx b/src/app/components/Points/StakerPoints.tsx index 3ba4e58f..d41a3cf9 100644 --- a/src/app/components/Points/StakerPoints.tsx +++ b/src/app/components/Points/StakerPoints.tsx @@ -14,8 +14,9 @@ export const StakerPoints: React.FC = ({ queryKey: ["stakerPoints", publicKeyNoCoord], queryFn: () => getStakersPoints([publicKeyNoCoord]), enabled: !!publicKeyNoCoord, - refetchInterval: 60000, // Refresh every minute + refetchInterval: 300000, // Refresh every 5 minutes refetchOnWindowFocus: false, + retry: 1, }); if (isLoading) { From ff8b65e654bc195d4a86a5eefa2375282ca05cdc Mon Sep 17 00:00:00 2001 From: jeremy-babylonchain Date: Sat, 14 Sep 2024 19:07:15 +0700 Subject: [PATCH 15/15] resolve comments --- .../context/api/DelegationsPointsProvider.tsx | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/app/context/api/DelegationsPointsProvider.tsx b/src/app/context/api/DelegationsPointsProvider.tsx index 1e4ee960..6b058d04 100644 --- a/src/app/context/api/DelegationsPointsProvider.tsx +++ b/src/app/context/api/DelegationsPointsProvider.tsx @@ -11,6 +11,15 @@ interface PointsContextType { error: Error | null; } +interface DelegationsPointsProviderProps { + children: React.ReactNode; + publicKeyNoCoord: string; + delegationsAPI: Delegation[]; + isWalletConnected: boolean; + address: string; +} + +const MAX_DELEGATION_POINTS_BATCH_SIZE = 60; const DelegationsPointsContext = createContext( undefined, ); @@ -25,16 +34,6 @@ export const useDelegationsPoints = () => { return context; }; -interface DelegationsPointsProviderProps { - children: React.ReactNode; - publicKeyNoCoord: string; - delegationsAPI: Delegation[]; - isWalletConnected: boolean; - address: string; -} - -const MAX_DELEGATION_POINTS_BATCH_SIZE = 60; - export const DelegationsPointsProvider: React.FC< DelegationsPointsProviderProps > = ({ @@ -82,6 +81,7 @@ export const DelegationsPointsProvider: React.FC< !isGeoBlocked, refetchInterval: 300000, // Refetch every 5 minutes refetchOnWindowFocus: false, + retry: 1, }); useEffect(() => {