Skip to content

Commit

Permalink
feat: get prime status from contract
Browse files Browse the repository at this point in the history
  • Loading branch information
gleiser-oliveira committed Oct 31, 2023
1 parent ff225d4 commit 68b861c
Show file tree
Hide file tree
Showing 13 changed files with 300 additions and 22 deletions.
2 changes: 1 addition & 1 deletion src/App/Router/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import React, { lazy, useEffect } from 'react';
import { Navigate, Route, Routes, useLocation, useNavigate } from 'react-router-dom';

import { PAGE_CONTAINER_ID } from 'constants/layout';
import { routes } from 'constants/routing';
import { useAuth } from 'context/AuthContext';

import PageSuspense from './PageSuspense';
import { PAGE_CONTAINER_ID } from 'constants/layout';

const Dashboard = lazy(() => import('pages/Dashboard'));
const Account = lazy(() => import('pages/Account'));
Expand Down
10 changes: 9 additions & 1 deletion src/clients/api/__mocks__/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,10 @@ export const getXvsVaultLockedDeposits = vi.fn();
export const useGetXvsVaultLockedDeposits = () =>
useQuery(FunctionKey.GET_XVS_VAULT_WITHDRAWAL_REQUESTS, getXvsVaultLockedDeposits);

export const getXvsVaultUserInfo = vi.fn();
export const getXvsVaultUserInfo = vi.fn(() => ({
stakedAmountWei: new BigNumber('1000000'),
}));

export const useGetXvsVaultUserInfo = () =>
useQuery(FunctionKey.GET_XVS_VAULT_USER_INFO, getXvsVaultUserInfo);

Expand Down Expand Up @@ -260,6 +263,11 @@ export const useGetIsAddressPrime = vi.fn(() =>
useQuery(FunctionKey.GET_IS_ACCOUNT_PRIME, getIsAddressPrime),
);

export const getPrimeStatus = vi.fn(async () => ({}));
export const useGetPrimeStatus = vi.fn(() =>
useQuery(FunctionKey.GET_PRIME_STATUS, getPrimeStatus),
);

export const getHypotheticalPrimeApys = vi.fn();
export const useGetHypotheticalPrimeApys = vi.fn(() =>
useQuery(FunctionKey.GET_HYPOTHETICAL_PRIME_APYS, getHypotheticalPrimeApys),
Expand Down
4 changes: 4 additions & 0 deletions src/clients/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -341,3 +341,7 @@ export { default as useGetIsAddressPrime } from './queries/getIsAddressPrime/use
export { default as getHypotheticalPrimeApys } from './queries/getHypotheticalPrimeApys';
export * from './queries/getHypotheticalPrimeApys';
export { default as useGetHypotheticalPrimeApys } from './queries/getHypotheticalPrimeApys/useGetHypotheticalPrimeApys';

export { default as getPrimeStatus } from './queries/getPrimeStatus';
export * from './queries/getPrimeStatus';
export { default as useGetPrimeStatus } from './queries/getPrimeStatus/useGetPrimeStatus';
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`getPrimeStatus > returns the the data describing the status of the Prime contract 1`] = `
{
"claimWaitingPeriod": 600,
"claimedPrimeTokenCount": 1000,
"primeMinimumStakedXvsMantissa": "1000",
"primeTokenLimit": 300,
"rewardTokenAddress": "",
"xvsVault": "",
"xvsVaultPoolId": 1,
}
`;
39 changes: 39 additions & 0 deletions src/clients/api/queries/getPrimeStatus/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import BigNumber from 'bignumber.js';
import { Prime } from 'packages/contracts';

import getPrimeClaimWaitingPeriod from '.';

vi.mock('packages/contracts');

describe('getPrimeStatus', () => {
it('returns the the data describing the status of the Prime contract', async () => {
const mockPeriodInSeconds = 600;
const mockMinimumStakedXvs = 1000;
const tokenLimit = 1000;
const claimedTokens = 300;

const fakePrimeContract = {
STAKING_PERIOD: vi.fn(() => new BigNumber(mockPeriodInSeconds)),
MINIMUM_STAKED_XVS: vi.fn(() => new BigNumber(mockMinimumStakedXvs)),
totalRevocable: vi.fn(() => new BigNumber(tokenLimit)),
revocableLimit: vi.fn(() => new BigNumber(claimedTokens)),
xvsVault: vi.fn(() => ''),
xvsVaultPoolId: vi.fn(() => new BigNumber(1)),
xvsVaultRewardToken: vi.fn(() => ''),
} as unknown as Prime;

const response = await getPrimeClaimWaitingPeriod({
primeContract: fakePrimeContract,
});

expect(fakePrimeContract.STAKING_PERIOD).toHaveBeenCalledTimes(1);
expect(fakePrimeContract.MINIMUM_STAKED_XVS).toHaveBeenCalledTimes(1);
expect(fakePrimeContract.totalRevocable).toHaveBeenCalledTimes(1);
expect(fakePrimeContract.revocableLimit).toHaveBeenCalledTimes(1);
expect(fakePrimeContract.xvsVault).toHaveBeenCalledTimes(1);
expect(fakePrimeContract.xvsVaultPoolId).toHaveBeenCalledTimes(1);
expect(fakePrimeContract.xvsVaultRewardToken).toHaveBeenCalledTimes(1);

expect(response).toMatchSnapshot();
});
});
50 changes: 50 additions & 0 deletions src/clients/api/queries/getPrimeStatus/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import BigNumber from 'bignumber.js';
import { Prime } from 'packages/contracts';

export interface GetPrimeClaimWaitingPeriodInput {
primeContract: Prime;
}

export interface GetPrimeClaimWaitingPeriodOutput {
claimWaitingPeriod: number;
claimedPrimeTokenCount: number;
primeTokenLimit: number;
primeMinimumStakedXvsMantissa: BigNumber;
xvsVault: string;
xvsVaultPoolId: number;
rewardTokenAddress: string;
}

const getPrimeStatus = async ({
primeContract,
}: GetPrimeClaimWaitingPeriodInput): Promise<GetPrimeClaimWaitingPeriodOutput> => {
const [
claimWaitingPeriod,
primeMinimumStakedXvsMantissa,
claimedPrimeTokens,
revocableLimit,
xvsVault,
xvsVaultPoolId,
rewardTokenAddress,
] = await Promise.all([
primeContract.STAKING_PERIOD(),
primeContract.MINIMUM_STAKED_XVS(),
primeContract.totalRevocable(),
primeContract.revocableLimit(),
primeContract.xvsVault(),
primeContract.xvsVaultPoolId(),
primeContract.xvsVaultRewardToken(),
]);

return {
claimWaitingPeriod: claimWaitingPeriod.toNumber(),
primeMinimumStakedXvsMantissa: new BigNumber(primeMinimumStakedXvsMantissa.toString()),
claimedPrimeTokenCount: claimedPrimeTokens.toNumber(),
primeTokenLimit: revocableLimit.toNumber(),
xvsVault,
xvsVaultPoolId: xvsVaultPoolId.toNumber(),
rewardTokenAddress,
};
};

export default getPrimeStatus;
31 changes: 31 additions & 0 deletions src/clients/api/queries/getPrimeStatus/useGetPrimeStatus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { useGetPrimeContract } from 'packages/contracts';
import { QueryObserverOptions, useQuery } from 'react-query';
import { callOrThrow } from 'utilities';

import { GetPrimeClaimWaitingPeriodOutput, getPrimeStatus } from 'clients/api';
import FunctionKey from 'constants/functionKey';
import { useIsFeatureEnabled } from 'hooks/useIsFeatureEnabled';

type Options = QueryObserverOptions<
GetPrimeClaimWaitingPeriodOutput,
Error,
GetPrimeClaimWaitingPeriodOutput,
GetPrimeClaimWaitingPeriodOutput,
FunctionKey.GET_PRIME_STATUS
>;

const useGetPrimeClaimWaitingPeriod = (options?: Options) => {
const isPrimeEnabled = useIsFeatureEnabled({ name: 'prime' });
const primeContract = useGetPrimeContract();

return useQuery(
FunctionKey.GET_PRIME_STATUS,
() => callOrThrow({ primeContract }, params => getPrimeStatus(params)),
{
...options,
enabled: (options?.enabled === undefined || options?.enabled) && isPrimeEnabled,
},
);
};

export default useGetPrimeClaimWaitingPeriod;
3 changes: 1 addition & 2 deletions src/components/ApproveTokenSteps/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
/** @jsxImportSource @emotion/react */
import { Typography } from '@mui/material';
import { displayMutationError } from 'errors';
import React from 'react';
import { useTranslation } from 'translation';
import { Token } from 'types';

import { displayMutationError } from 'errors';

import { PrimaryButton } from '../Button';
import { Icon } from '../Icon';
import { Tooltip } from '../Tooltip';
Expand Down
1 change: 1 addition & 0 deletions src/constants/functionKey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ enum FunctionKey {
GET_VTOKENS = 'GET_VTOKENS',
GET_IS_ACCOUNT_PRIME = 'GET_IS_ACCOUNT_PRIME',
GET_HYPOTHETICAL_PRIME_APYS = 'GET_HYPOTHETICAL_PRIME_APYS',
GET_PRIME_STATUS = 'GET_PRIME_STATUS',

// Mutations
MINT_VAI = 'MINT_VAI',
Expand Down
82 changes: 81 additions & 1 deletion src/containers/PrimeStatusBanner/__tests__/index.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,38 @@
import { waitFor } from '@testing-library/dom';
import BigNumber from 'bignumber.js';
import Vi from 'vitest';

import { useGetIsAddressPrime } from 'clients/api';
import { useGetIsAddressPrime, useGetPrimeStatus } from 'clients/api';
import renderComponent from 'testUtils/renderComponent';

import PrimeStatusBanner from '..';
import TEST_IDS from '../testIds';

vi.useFakeTimers();

describe('PrimeStatusBanner', () => {
const MOCK_DEFAULT_PRIME_STATUS = {
claimWaitingPeriod: 0,
claimedPrimeTokenCount: 0,
primeTokenLimit: 1000,
primeMinimumStakedXvsMantissa: new BigNumber('1000000'),
xvsVault: '',
xvsVaultPoolId: 1,
rewardTokenAddress: '',
};

beforeEach(() => {
(useGetIsAddressPrime as Vi.Mock).mockImplementation(() => ({
data: {
isPrime: false,
},
}));
});

afterAll(() => {
vi.clearAllMocks();
});

it('renders without crashing', () => {
renderComponent(<PrimeStatusBanner />);
});
Expand All @@ -22,4 +48,58 @@ describe('PrimeStatusBanner', () => {

expect(queryByTestId(TEST_IDS.primeStatusBannerContainer)).toBeNull();
});

it('informs the user the requirements to be a Prime user', () => {
(useGetPrimeStatus as Vi.Mock).mockImplementation(() => ({
data: MOCK_DEFAULT_PRIME_STATUS,
}));
const { queryByTestId } = renderComponent(<PrimeStatusBanner />);

expect(queryByTestId(TEST_IDS.primeStatusBannerContainer)).toBeNull();
});

it('displays a warning when there are less than 5% of Prime tokens left', async () => {
(useGetPrimeStatus as Vi.Mock).mockImplementation(() => ({
data: {
...MOCK_DEFAULT_PRIME_STATUS,
claimedPrimeTokenCount: 999,
},
}));

const { queryByTestId } = renderComponent(<PrimeStatusBanner />);
await waitFor(() => queryByTestId(TEST_IDS.primeTokensLeftWarning));

expect(queryByTestId(TEST_IDS.primeTokensLeftWarning)).toBeVisible();
});

it('displays the time remaining to be a Prime user, when a user has staked enough XVS', async () => {
const text = '10 minutes until you can become a Prime user';
(useGetPrimeStatus as Vi.Mock).mockImplementation(() => ({
data: {
...MOCK_DEFAULT_PRIME_STATUS,
claimWaitingPeriod: 600,
},
}));

const { queryByTestId, queryByText } = renderComponent(<PrimeStatusBanner />);
await waitFor(() => queryByText(text));

expect(queryByTestId(TEST_IDS.claimPrimeTokenButton)).toBeNull();
expect(queryByText(text)).toBeVisible();
});

it('allows the user to claim a Prime token if all the criteria match', async () => {
(useGetPrimeStatus as Vi.Mock).mockImplementation(() => ({
data: {
...MOCK_DEFAULT_PRIME_STATUS,
claimWaitingPeriod: 0,
},
}));

const { queryByTestId } = renderComponent(<PrimeStatusBanner />);
await waitFor(() => queryByTestId(TEST_IDS.claimPrimeTokenButton));

expect(queryByTestId(TEST_IDS.claimPrimeTokenButton)).toBeVisible();
expect(queryByTestId(TEST_IDS.claimPrimeTokenButton)).toBeEnabled();
});
});
Loading

0 comments on commit 68b861c

Please sign in to comment.