Skip to content

Commit 34673f5

Browse files
authored
feat: add staked amount in view project details page (#3816)
* feat: new hook to get total stakes for a round application * chore: integrate hook to display staked amount in application view page
1 parent 9b462d7 commit 34673f5

File tree

2 files changed

+148
-2
lines changed

2 files changed

+148
-2
lines changed

packages/grant-explorer/src/features/round/ViewProjectDetails/ViewProjectDetails.tsx

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ import {
3333
ProjectLogo,
3434
StakingBannerAndModal,
3535
} from "./components";
36-
36+
import { useGetApplicationStakes } from "./hooks/useGetApplicationStakes";
37+
import { useIsStakable } from "./components/StakingBannerAndModal/hooks/useIsStakable";
3738
export default function ViewProjectDetails() {
3839
const [selectedTab, setSelectedTab] = useState(0);
3940

@@ -58,6 +59,11 @@ export default function ViewProjectDetails() {
5859
applicationId = paramApplicationId;
5960
}
6061

62+
const isStakableRound = useIsStakable({
63+
chainId: Number(chainId),
64+
roundId,
65+
});
66+
6167
const {
6268
data: application,
6369
error,
@@ -70,6 +76,13 @@ export default function ViewProjectDetails() {
7076
},
7177
dataLayer
7278
);
79+
const { data: totalStaked } = useGetApplicationStakes(
80+
Number(chainId),
81+
Number(roundId),
82+
application?.anchorAddress ?? "",
83+
isStakableRound
84+
);
85+
7386
const { round: roundDetails } = useRoundById(Number(chainId), roundId);
7487

7588
const projectToRender = application && mapApplicationToProject(application);
@@ -218,12 +231,15 @@ export default function ViewProjectDetails() {
218231
</div>
219232
)}
220233
</div>
221-
<div className="mb-4">
234+
<div className="mb-4 relative">
222235
<ProjectBanner
223236
bannerImgCid={bannerImg ?? null}
224237
classNameOverride="h-32 w-full object-cover lg:h-80 rounded md:rounded-3xl"
225238
resizeHeight={320}
226239
/>
240+
{totalStaked && Number(totalStaked) > 0 && (
241+
<StakedAmountCard totalStaked={Number(totalStaked)} />
242+
)}
227243
<div className="pl-4 sm:pl-6 lg:pl-8">
228244
<div className="sm:flex sm:items-end sm:space-x-5">
229245
<div className="flex">
@@ -275,3 +291,40 @@ export default function ViewProjectDetails() {
275291
</>
276292
);
277293
}
294+
295+
const StakedAmountCard = ({ totalStaked }: { totalStaked: number }) => {
296+
return (
297+
<div className="p-2 bg-white/80 rounded-2xl backdrop-blur-sm inline-flex justify-start items-center gap-2 absolute top-4 right-4">
298+
<div data-svg-wrapper className="relative">
299+
<svg
300+
width="20"
301+
height="20"
302+
viewBox="0 0 20 20"
303+
fill="none"
304+
xmlns="http://www.w3.org/2000/svg"
305+
>
306+
<path
307+
d="M10.8334 8.33333V2.5L3.33337 11.6667H9.16671L9.16671 17.5L16.6667 8.33333L10.8334 8.33333Z"
308+
stroke="#7D67EB"
309+
stroke-width="2"
310+
stroke-linecap="round"
311+
stroke-linejoin="round"
312+
/>
313+
</svg>
314+
</div>
315+
<div className="inline-flex flex-col justify-start items-start">
316+
<div className="self-stretch inline-flex justify-start items-center gap-1">
317+
<div className="justify-start text-text-primary text-lg font-medium font-['DM_Mono'] leading-normal">
318+
{totalStaked}
319+
</div>
320+
<div className="justify-start text-text-primary text-lg font-medium font-['DM_Mono'] leading-normal">
321+
GTC
322+
</div>
323+
</div>
324+
<div className="self-stretch justify-start text-text-primary text-sm font-normal font-['DM_Mono'] leading-[14px]">
325+
Total staked
326+
</div>
327+
</div>
328+
</div>
329+
);
330+
};
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { useQuery } from "@tanstack/react-query";
2+
import { gql } from "graphql-request";
3+
4+
const endpoint = "https://indexer.hyperindex.xyz/98cb471/v1/graphql";
5+
6+
const getStakesQuery = gql`
7+
query getStakes($chainId: numeric!, $poolId: numeric!, $recipient: String!) {
8+
TokenLock_Locked(
9+
where: {
10+
chainId: { _eq: $chainId }
11+
poolId: { _eq: $poolId }
12+
recipient: { _eq: $recipient }
13+
}
14+
) {
15+
amount
16+
chainId
17+
poolId
18+
recipient
19+
sender
20+
}
21+
}
22+
`;
23+
24+
export const useGetApplicationStakes = (
25+
chainId: number,
26+
poolId: number,
27+
recipient: string,
28+
isStakableRound: boolean
29+
) => {
30+
const query = useQuery({
31+
enabled: isStakableRound,
32+
queryKey: ["getApplicationStakes", chainId, poolId, recipient],
33+
queryFn: () => getApplicationStakes(chainId, poolId, recipient),
34+
});
35+
36+
return {
37+
data: query.data,
38+
isLoading: query.isLoading,
39+
isError: query.isError,
40+
error: query.error,
41+
refetch: query.refetch,
42+
};
43+
};
44+
45+
const GET = async (chainId: number, poolId: number, recipient: string) => {
46+
const response = await fetch(endpoint, {
47+
method: "POST",
48+
headers: {
49+
"Content-Type": "application/json",
50+
},
51+
body: JSON.stringify({
52+
query: getStakesQuery,
53+
variables: { chainId, poolId, recipient },
54+
}),
55+
});
56+
57+
if (!response.ok) {
58+
const errorData = await response.json();
59+
throw new Error(
60+
`Error: ${response.status} - ${errorData.message || "Unknown error"}`
61+
);
62+
}
63+
64+
return response.json();
65+
};
66+
67+
interface ApplicationStake {
68+
amount: string;
69+
chainId: string;
70+
poolId: string;
71+
recipient: string;
72+
sender: string;
73+
}
74+
75+
export async function getApplicationStakes(
76+
chainId: number,
77+
poolId: number,
78+
recipient: string
79+
): Promise<string> {
80+
try {
81+
const response = (await GET(chainId, poolId, recipient)).data
82+
.TokenLock_Locked as ApplicationStake[];
83+
const totalStakes = response.reduce(
84+
(acc, stake) => acc + Number(stake.amount),
85+
0
86+
);
87+
88+
return (totalStakes / 10 ** 18).toFixed(3);
89+
} catch (error) {
90+
console.error("Error fetching pool info and stakes:", error);
91+
throw error;
92+
}
93+
}

0 commit comments

Comments
 (0)