Skip to content

Commit 9751dbb

Browse files
authored
/feat/points-apr
Feat/points apr
2 parents 4bbb3af + c56a8dc commit 9751dbb

18 files changed

Lines changed: 339 additions & 42 deletions

File tree

src/clients/api/queries/getPointLeaderBoard/index.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import config from 'config';
22
import { GetLeaderBoardPayload, Leaderboard } from 'types';
33

4+
import getLeaderboardSubsquid from 'utilities/getLeaderboardSubsquid';
5+
46
export const getLeaderBoard = async ({ page, limit }: GetLeaderBoardPayload) => {
57
try {
68
const response = await fetch(
@@ -33,3 +35,25 @@ export const getLeaderBoard = async ({ page, limit }: GetLeaderBoardPayload) =>
3335
};
3436
}
3537
};
38+
39+
export const getSquidLeaderboard = async ({ page, limit }: GetLeaderBoardPayload) => {
40+
try {
41+
const data = await getLeaderboardSubsquid(config.chainId, { page, limit });
42+
return {
43+
data: data.results as Leaderboard[],
44+
totalPage: data.meta.totalPages,
45+
page: data.meta.page,
46+
limit: data.meta.limit,
47+
total: data.meta.total,
48+
};
49+
} catch (error) {
50+
console.error('Error fetching squid leaderboard:', error);
51+
return {
52+
data: [],
53+
totalPage: 0,
54+
page: 1,
55+
limit: 10,
56+
total: 0,
57+
};
58+
}
59+
};

src/clients/api/queries/getPointLeaderBoard/useGetLeaderBoard.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,20 @@ import { GetLeaderBoardPayload } from 'types';
33

44
import FunctionKey from 'constants/functionKey';
55

6-
import { getLeaderBoard } from '.';
6+
import { getLeaderBoard, getSquidLeaderboard } from '.';
77

8-
const useGetLeaderBoard = ({ page, limit }: GetLeaderBoardPayload) =>
8+
export const useGetLeaderBoard = ({ page, limit }: GetLeaderBoardPayload) =>
99
useQuery([FunctionKey.GET_LEADER_BOARD, page, limit], () => getLeaderBoard({ page, limit }), {
1010
refetchOnWindowFocus: false,
1111
refetchInterval: 10000,
1212
});
13-
export default useGetLeaderBoard;
13+
14+
export const useGetSquidLeaderboard = ({ page, limit }: GetLeaderBoardPayload) =>
15+
useQuery(
16+
[FunctionKey.GET_SQUID_LEADER_BOARD, page, limit],
17+
() => getSquidLeaderboard({ page, limit }),
18+
{
19+
refetchOnWindowFocus: false,
20+
refetchInterval: 10000,
21+
},
22+
);
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import BigNumber from 'bignumber.js';
2+
import config from 'config';
3+
4+
import { TOKENS } from 'constants/tokens';
5+
import getPointsInfoSubsquid from 'utilities/getPointsInfoSubsquid';
6+
7+
export interface GetPointsAprInput {
8+
totalStaked: BigNumber;
9+
}
10+
11+
const fromWei = (value: BigNumber, decimals: number) =>
12+
value.dividedBy(new BigNumber(10).pow(decimals)).decimalPlaces(decimals);
13+
14+
const getPointsApr = async ({ totalStaked }: GetPointsAprInput): Promise<BigNumber> => {
15+
try {
16+
if (totalStaked.isZero()) {
17+
return new BigNumber(0);
18+
}
19+
20+
const data = await getPointsInfoSubsquid(config.chainId);
21+
const { weight, pointsPerDay } = data;
22+
const totalStakedFromWei = fromWei(totalStaked, TOKENS.xcn.decimals);
23+
24+
return new BigNumber(pointsPerDay ?? 0)
25+
.times(weight ?? 0)
26+
.times(365)
27+
.times(100)
28+
.div(totalStakedFromWei);
29+
} catch (error) {
30+
console.error('Error fetching points apr:', error);
31+
return new BigNumber(0);
32+
}
33+
};
34+
35+
export default getPointsApr;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import BigNumber from 'bignumber.js';
2+
import { useQuery } from 'react-query';
3+
4+
import FunctionKey from 'constants/functionKey';
5+
6+
import getPointsApr from '.';
7+
8+
const useGetPointsApr = (totalStaked: BigNumber) =>
9+
useQuery([FunctionKey.GET_POINTS_APR, totalStaked.toString()], () =>
10+
getPointsApr({ totalStaked }),
11+
);
12+
13+
export default useGetPointsApr;

src/clients/api/queries/getUserInfo/index.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
import config from 'config';
12
import { VError } from 'errors';
23
import { restService } from 'utilities';
34

5+
import getUserPointsSubsquid from 'utilities/getUserPointsSubsquid';
6+
47
import formatUserInfoResponse from './formatUserInfoResponse';
58
import { GetUserInfoResponse } from './types';
69

@@ -12,7 +15,7 @@ export interface GetUserInfoOutput {
1215
points: number;
1316
}
1417

15-
const getUserInfo = async ({ address }: GetUserInfoInput): Promise<GetUserInfoOutput> => {
18+
export const getUserInfo = async ({ address }: GetUserInfoInput): Promise<GetUserInfoOutput> => {
1619
const response = await restService<GetUserInfoResponse>({
1720
endpoint: `/users/${address}`,
1821
method: 'GET',
@@ -37,4 +40,7 @@ const getUserInfo = async ({ address }: GetUserInfoInput): Promise<GetUserInfoOu
3740
return formatUserInfoResponse(payload);
3841
};
3942

40-
export default getUserInfo;
43+
export const getUserPoints = async (input: GetUserInfoInput): Promise<GetUserInfoOutput> => {
44+
const data = await getUserPointsSubsquid(config.chainId, input);
45+
return formatUserInfoResponse(data);
46+
};

src/clients/api/queries/getUserInfo/useGetUserInfo.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { QueryObserverOptions, useQuery } from 'react-query';
33
import { DEFAULT_REFETCH_INTERVAL_MS } from 'constants/defaultRefetchInterval';
44
import FunctionKey from 'constants/functionKey';
55

6-
import getUserInfo, { GetUserInfoInput, GetUserInfoOutput } from '.';
6+
import { GetUserInfoInput, GetUserInfoOutput, getUserInfo } from '.';
77

88
type Options = QueryObserverOptions<
99
GetUserInfoOutput,
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { QueryObserverOptions, useQuery } from 'react-query';
2+
3+
import { DEFAULT_REFETCH_INTERVAL_MS } from 'constants/defaultRefetchInterval';
4+
import FunctionKey from 'constants/functionKey';
5+
6+
import { GetUserInfoInput, GetUserInfoOutput, getUserPoints } from '.';
7+
8+
type Options = QueryObserverOptions<
9+
GetUserInfoOutput,
10+
Error,
11+
GetUserInfoOutput,
12+
GetUserInfoOutput,
13+
[FunctionKey.GET_USER_POINTS, GetUserInfoInput]
14+
>;
15+
16+
const useGetUserPoints = (params: GetUserInfoInput, options?: Options) =>
17+
// This endpoint is paginated so we keep the previous responses by default to create a more seamless paginating experience
18+
useQuery([FunctionKey.GET_USER_POINTS, params], () => getUserPoints(params), {
19+
keepPreviousData: false,
20+
refetchInterval: DEFAULT_REFETCH_INTERVAL_MS,
21+
...options,
22+
});
23+
24+
export default useGetUserPoints;

src/constants/endpoints.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ export const SUBGRAPH_LINKS: { [key: number]: { [key: string]: string } } = {
3030
},
3131
};
3232

33+
export const SUBSQUID_LINKS = {
34+
[EthChainId.MAINNET]: 'https://pnt-squid.onyx.org/graphql',
35+
[EthChainId.TESTNET]: 'https://pnt-squid.onyx.org/graphql',
36+
};
37+
3338
export const POINTS_API_ENDPOINTS = {
3439
[EthChainId.MAINNET]: 'https://pnt.onyx.org/api/v1',
3540
[EthChainId.TESTNET]: 'https://testpnt.onyx.org/api/v1',

src/constants/functionKey.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ enum FunctionKey {
2525
GET_PRIOR_VOTES = 'GET_PRIOR_VOTES',
2626
GET_STAKING_INFOS = 'GET_STAKING_INFOS',
2727
GET_STAKING_APY = 'GET_STAKING_APY',
28+
GET_POINTS_APR = 'GET_POINTS_APR',
2829
GET_VOTERS = 'GET_VOTERS',
2930
GET_PENDING_XCN = 'GET_PENDING_XCN',
3031
GET_STAKE_HISTORIES = 'GET_STAKE_HISTORIES',
@@ -49,8 +50,10 @@ enum FunctionKey {
4950
ADD_LIQUIDITY = 'ADD_LIQUIDITY',
5051
GET_SIGNED_MESSSAGE = 'GET_SIGNED_MESSSAGE',
5152
GET_USER_INFO = 'GET_USER_INFO',
53+
GET_USER_POINTS = 'GET_USER_POINTS',
5254
VERIFY_SIGNATURE = 'VERIFY_SIGNATURE',
5355
GET_LEADER_BOARD = 'GET_LEADER_BOARD',
56+
GET_SQUID_LEADER_BOARD = 'GET_SQUID_LEADER_BOARD',
5457

5558
// Mutations
5659
ENTER_MARKETS = 'ENTER_MARKETS',

src/pages/Points/index.tsx

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { useTranslation } from 'translation';
99

1010
import { Farm, useGetFarms } from 'clients/api';
1111
import useGetUserInfo from 'clients/api/queries/getUserInfo/useGetUserInfo';
12+
import useGetUserPoints from 'clients/api/queries/getUserInfo/useGetUserPoints';
1213
import ConnectButton from 'components/Layout/ConnectButton';
1314
import { AuthContext } from 'context/AuthContext';
1415
import LeaderboardTable from 'pages/PointsLeaderboard/LeaderboardTable';
@@ -43,7 +44,14 @@ export const PointsUi: React.FC<PointsUiProps> = ({ farms, isInitialLoading }) =
4344
const { account } = React.useContext(AuthContext);
4445
const styles = useStyles();
4546
const { t } = useTranslation();
46-
const { data } = useGetUserInfo({ address: account?.address || '' }, { enabled: !!account });
47+
const { data: userEnrolled } = useGetUserInfo(
48+
{ address: account?.address || '' },
49+
{ enabled: !!account },
50+
);
51+
const { data: userStaked } = useGetUserPoints(
52+
{ address: account?.address || '' },
53+
{ enabled: !!account },
54+
);
4755

4856
const onEnroll = () => {
4957
setActiveModal('enroll');
@@ -82,49 +90,42 @@ export const PointsUi: React.FC<PointsUiProps> = ({ farms, isInitialLoading }) =
8290
<ConnectButton small fullWidth css={styles.enrollButton} />
8391
</Box>
8492
)}
85-
{account && data && (
93+
{account && (
8694
<Box css={styles.pointUserContainer}>
87-
<Box
88-
sx={{
89-
display: 'flex',
90-
flexDirection: 'column',
91-
gap: 2,
92-
alignItems: 'center',
93-
justifyContent: 'center',
94-
}}
95-
>
96-
<Typography color="text.secondary" sx={{ fontSize: '12px' }}>
95+
<Box css={styles.pointsUserEnrollContainer}>
96+
<Typography color="text.secondary" fontSize={14}>
9797
{t('pointsUi.pointsUser.pointsSubtitle')}
9898
</Typography>
9999
<Box css={styles.pointsContainer}>
100100
<Typography component="h2" css={styles.pointsText}>
101-
{formatPoint(data.points, 2)}
101+
{formatPoint(userStaked?.points || 0, 2)}
102102
</Typography>
103103
<Typography component="h2" css={styles.liquidText1}>
104-
{formatPoint(data.points, 2)}
104+
{formatPoint(userStaked?.points || 0, 2)}
105105
</Typography>
106106
<Typography component="h2" css={styles.liquidText2}>
107-
{formatPoint(data.points, 2)}
107+
{formatPoint(userStaked?.points || 0, 2)}
108108
</Typography>
109109
<Typography component="h2" css={styles.liquidText3}>
110-
{formatPoint(data.points, 2)}
110+
{formatPoint(userStaked?.points || 0, 2)}
111111
</Typography>
112112
</Box>
113113
</Box>
114-
</Box>
115-
)}
116-
{account && !data && (
117-
<Box css={styles.pointUserContainer}>
118-
<Typography color="text.secondary" textAlign="center">
119-
{t('pointsUi.pointsUser.descriptionConnect')}
120-
</Typography>
121-
<Button
122-
css={styles.menuMobileButton}
123-
variant="secondaryConnectWallet"
124-
onClick={onEnroll}
125-
>
126-
{t('pointsUi.pointsUser.enrollButton')}
127-
</Button>
114+
115+
{!userEnrolled && (
116+
<Box css={styles.pointsUserEnrollContainer}>
117+
<Typography color="text.secondary" textAlign="center">
118+
{t('pointsUi.pointsUser.descriptionConnect')}
119+
</Typography>
120+
<Button
121+
css={styles.menuMobileButton}
122+
variant="secondaryConnectWallet"
123+
onClick={onEnroll}
124+
>
125+
{t('pointsUi.pointsUser.enrollButton')}
126+
</Button>
127+
</Box>
128+
)}
128129
</Box>
129130
)}
130131
</Box>

0 commit comments

Comments
 (0)