-
Notifications
You must be signed in to change notification settings - Fork 88
feat: add prime total rewards card #5626
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: feat/prime-leaderboard-table
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| import type { SVGProps } from 'react'; | ||
|
|
||
| const SvgDotShortcut = (props: SVGProps<SVGSVGElement>) => ( | ||
| <svg | ||
| width="16" | ||
| height="16" | ||
| viewBox="0 0 16 16" | ||
| fill="none" | ||
| xmlns="http://www.w3.org/2000/svg" | ||
| {...props} | ||
| > | ||
| <rect x="0.5" y="0.5" width="15" height="15" rx="7.5" stroke="currentColor" /> | ||
| <path | ||
| d="M4 9C4.55228 9 5 8.55228 5 8C5 7.44772 4.55228 7 4 7C3.44772 7 3 7.44772 3 8C3 8.55228 3.44772 9 4 9Z" | ||
| fill="currentColor" | ||
| /> | ||
| <path | ||
| d="M8 9C8.55228 9 9 8.55228 9 8C9 7.44772 8.55228 7 8 7C7.44772 7 7 7.44772 7 8C7 8.55228 7.44772 9 8 9Z" | ||
| fill="currentColor" | ||
| /> | ||
| <path | ||
| d="M12 9C12.5523 9 13 8.55228 13 8C13 7.44772 12.5523 7 12 7C11.4477 7 11 7.44772 11 8C11 8.55228 11.4477 9 12 9Z" | ||
| fill="currentColor" | ||
| /> | ||
| </svg> | ||
| ); | ||
|
|
||
| export default SvgDotShortcut; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| import type { SVGProps } from 'react'; | ||
|
|
||
| const SvgSparkle = (props: SVGProps<SVGSVGElement>) => ( | ||
| <svg | ||
| width="16" | ||
| height="16" | ||
| viewBox="0 0 16 16" | ||
| fill="none" | ||
| xmlns="http://www.w3.org/2000/svg" | ||
| {...props} | ||
| > | ||
| <path | ||
| d="M3.73138 8.51568C3.84319 8.29954 4.15265 8.29967 4.26459 8.51568L5.31732 10.5528C5.34582 10.6078 5.39121 10.6522 5.44623 10.6807L7.48236 11.7344C7.69864 11.8463 7.69864 12.1548 7.48236 12.2667L5.44623 13.3204C5.39121 13.3489 5.34582 13.3933 5.31732 13.4483L4.26459 15.4854C4.15265 15.7014 3.84319 15.7015 3.73138 15.4854L2.67865 13.4483C2.6501 13.3933 2.60481 13.3488 2.54974 13.3204L0.51361 12.2667C0.297547 12.1548 0.297547 11.8463 0.51361 11.7344L2.54974 10.6807C2.60481 10.6522 2.6501 10.6078 2.67865 10.5528L3.73138 8.51568ZM10.2236 1.62505C10.3287 1.38611 10.6673 1.38614 10.7724 1.62505L11.8652 4.10845C12.0667 4.56644 12.4325 4.93239 12.8906 5.13384L15.374 6.22564C15.6129 6.33074 15.6129 6.67034 15.374 6.77544L12.8906 7.86724C12.4325 8.06869 12.0667 8.43463 11.8652 8.89263L10.7724 11.376C10.6673 11.6149 10.3287 11.615 10.2236 11.376L9.13177 8.89263C8.93027 8.43455 8.5635 8.06872 8.10541 7.86724L5.62298 6.77544C5.38403 6.67034 5.38403 6.33074 5.62298 6.22564L8.10541 5.13384C8.5635 4.93236 8.93027 4.56653 9.13177 4.10845L10.2236 1.62505ZM3.90912 0.171926C3.94641 0.100177 4.04943 0.10031 4.08685 0.171926L4.66498 1.29107C4.67443 1.30923 4.68979 1.32358 4.70795 1.33306L5.82611 1.91216C5.8982 1.94944 5.8982 2.05164 5.82611 2.08892L4.70795 2.66802C4.68979 2.6775 4.67443 2.69184 4.66498 2.71001L4.08685 3.82915C4.04943 3.90077 3.94641 3.9009 3.90912 3.82915L3.33099 2.71001C3.32147 2.69182 3.30627 2.67746 3.28802 2.66802L2.16986 2.08892C2.09819 2.05156 2.09819 1.94952 2.16986 1.91216L3.28802 1.33306C3.30627 1.32362 3.32147 1.30926 3.33099 1.29107L3.90912 0.171926Z" | ||
| fill="currentColor" | ||
| /> | ||
| </svg> | ||
| ); | ||
|
|
||
| export default SvgSparkle; |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -1276,7 +1276,16 @@ | |||||
| } | ||||||
| }, | ||||||
| "tablesRefreshNote": "Refreshed hourly · Last refresh: {{date, distanceToNow}} ago", | ||||||
| "title": "Prime leaderboard" | ||||||
| "title": "Prime leaderboard", | ||||||
| "totalRewards": { | ||||||
| "title": "Total Prime rewards distributed this cycle" | ||||||
| }, | ||||||
| "userRewards": { | ||||||
| "eligibleMessage": "You are currently eligible for sharing the Prime rewards during this cycle. Supply assets below to share the rewards.", | ||||||
| "marketActions": "Open market actions", | ||||||
| "notEligibleMessage": "You are currently NOT eligible for sharing the Prime rewards during this cycle. Stake XVS to compete for Prime for next cycle.", | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This content wasn't proofread it seems:
Suggested change
|
||||||
| "title": "Your Prime rewards this cycle" | ||||||
| } | ||||||
| }, | ||||||
| "primeStatusBanner": { | ||||||
| "becomePrimeTitle": "You can now become a Prime user", | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| import { fireEvent, screen } from '@testing-library/react'; | ||
|
|
||
| import { xvs } from '__mocks__/models/tokens'; | ||
| import { renderComponent } from 'testUtils/render'; | ||
|
|
||
| import { MarketActions } from '..'; | ||
|
|
||
| vi.mock('pages/Market/OperationForm', () => ({ | ||
| OperationForm: () => <div data-testid="operation-form" />, | ||
| })); | ||
|
|
||
| describe('pages/PrimeLeaderboard/MarketActions', () => { | ||
| it('opens the market operation modal when clicked', async () => { | ||
| renderComponent(<MarketActions token={xvs} />); | ||
|
|
||
| fireEvent.click(screen.getByLabelText('Open market actions')); | ||
|
|
||
| expect(await screen.findByTestId('operation-form')).toBeInTheDocument(); | ||
| }); | ||
| }); |
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What do you think about moving this component to the |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,57 @@ | ||||||
| import { useState } from 'react'; | ||||||
|
|
||||||
| import { useGetPools } from 'clients/api'; | ||||||
| import { Icon, Modal } from 'components'; | ||||||
| import { useTranslation } from 'libs/translations'; | ||||||
| import { useAccountAddress } from 'libs/wallet'; | ||||||
| import { OperationForm } from 'pages/Market/OperationForm'; | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||
| import type { Token } from 'types'; | ||||||
| import { areAddressesEqual } from 'utilities'; | ||||||
|
|
||||||
| export interface MarketActionsProps { | ||||||
| token: Token; | ||||||
| } | ||||||
|
|
||||||
| export const MarketActions: React.FC<MarketActionsProps> = ({ token }) => { | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd recommend using a more explicit name for this. Maybe |
||||||
| const { t } = useTranslation(); | ||||||
| const { accountAddress } = useAccountAddress(); | ||||||
| const { data: getPoolsData } = useGetPools({ accountAddress }); | ||||||
| const [isModalOpen, setIsModalOpen] = useState(false); | ||||||
|
|
||||||
| const market = getPoolsData?.pools | ||||||
| .flatMap(pool => | ||||||
| pool.assets.map(asset => ({ asset, poolComptrollerAddress: pool.comptrollerAddress })), | ||||||
| ) | ||||||
| .find(({ asset }) => areAddressesEqual(asset.vToken.underlyingToken.address, token.address)); | ||||||
|
|
||||||
| if (!market) { | ||||||
| return undefined; | ||||||
| } | ||||||
|
Comment on lines
+18
to
+29
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All you need from the market is |
||||||
|
|
||||||
| return ( | ||||||
| <> | ||||||
| <button | ||||||
| type="button" | ||||||
| aria-label={t('primeLeaderboard.userRewards.marketActions')} | ||||||
| className="ml-2 shrink-0" | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To make it a bit more lively:
Suggested change
|
||||||
| onClick={() => setIsModalOpen(true)} | ||||||
| > | ||||||
| <Icon name="dotShortcut" className="text-light-grey" /> | ||||||
| </button> | ||||||
|
|
||||||
| {isModalOpen && ( | ||||||
| <Modal | ||||||
| isOpen | ||||||
| title={market.asset.vToken.underlyingToken.symbol} | ||||||
| handleClose={() => setIsModalOpen(false)} | ||||||
| > | ||||||
| <OperationForm | ||||||
| vToken={market.asset.vToken} | ||||||
| poolComptrollerAddress={market.poolComptrollerAddress} | ||||||
| onSubmitSuccess={() => setIsModalOpen(false)} | ||||||
| /> | ||||||
| </Modal> | ||||||
| )} | ||||||
|
Comment on lines
+42
to
+54
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I recommend creating a This will allow keeping one source of truth and avoid issues such as one we have here which is that if the market has protection mode enabled, it won't be displayed in the title (unlike the modal used inside the |
||||||
| </> | ||||||
| ); | ||||||
| }; | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| import { screen } from '@testing-library/react'; | ||
|
|
||
| import { usdc } from '__mocks__/models/tokens'; | ||
| import { renderComponent } from 'testUtils/render'; | ||
| import { MarketRewardRow } from '..'; | ||
|
|
||
| describe('pages/PrimeLeaderboard/MarketRewardRow', () => { | ||
| it('renders the token, reward amount and trailing content', () => { | ||
| renderComponent( | ||
| <MarketRewardRow token={usdc} rewardsCents={28_040_000} totalRewardsCents={46_230_000}> | ||
| <span>3.78%</span> | ||
| </MarketRewardRow>, | ||
| ); | ||
|
|
||
| expect(screen.getByText(usdc.symbol)).toBeInTheDocument(); | ||
| expect(screen.getByText('$280.4K')).toBeInTheDocument(); | ||
| expect(screen.getByText('3.78%')).toBeInTheDocument(); | ||
| }); | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| import { TokenIconWithSymbol } from 'components'; | ||
| import type { Token } from 'types'; | ||
| import { formatCentsToReadableValue } from 'utilities'; | ||
|
|
||
| export interface MarketRewardRowProps { | ||
| token: Token; | ||
| rewardsCents: number; | ||
| totalRewardsCents: number; | ||
| children?: React.ReactNode; | ||
| } | ||
|
|
||
| export const MarketRewardRow: React.FC<MarketRewardRowProps> = ({ | ||
| token, | ||
| rewardsCents, | ||
| totalRewardsCents, | ||
| children, | ||
| }) => { | ||
| const progressPercentage = | ||
| totalRewardsCents > 0 ? Math.min(100, (rewardsCents / totalRewardsCents) * 100) : 0; | ||
|
|
||
| return ( | ||
| <div className="flex items-center"> | ||
| <TokenIconWithSymbol token={token} className="shrink-0 text-b1s text-white" /> | ||
|
|
||
| <span className="ml-auto text-p3r text-white"> | ||
| {formatCentsToReadableValue({ value: rewardsCents })} | ||
| </span> | ||
|
|
||
| <div className="ml-1 h-1.5 w-1/4 shrink-0 overflow-hidden rounded-full bg-lightGrey"> | ||
| <div className="h-full rounded-full bg-green" style={{ width: `${progressPercentage}%` }} /> | ||
| </div> | ||
|
Comment on lines
+29
to
+31
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We already have a component for this: https://github.com/VenusProtocol/venus-protocol-interface/blob/59c235eeb9aa0db5814952bb069cc23ca07cba99/apps/evm/src/components/ProgressBar/index.tsx |
||
|
|
||
| {children} | ||
| </div> | ||
| ); | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| import { screen } from '@testing-library/react'; | ||
|
|
||
| import { usdc, xvs } from '__mocks__/models/tokens'; | ||
| import { renderComponent } from 'testUtils/render'; | ||
| import { TotalRewardsCard } from '..'; | ||
|
|
||
| describe('pages/PrimeLeaderboard/TotalRewardsCard', () => { | ||
| it('renders the total and per-market rewards', () => { | ||
| renderComponent( | ||
| <TotalRewardsCard | ||
| totalRewardsCents={46_230_000} | ||
| marketRewards={[ | ||
| { token: usdc, rewardsCents: 28_040_000 }, | ||
| { token: xvs, rewardsCents: 17_190_000 }, | ||
| ]} | ||
| />, | ||
| ); | ||
|
|
||
| expect(screen.getByText('Total Prime rewards distributed this cycle')).toBeInTheDocument(); | ||
| expect(screen.getByText('$462.3K')).toBeInTheDocument(); | ||
| expect(screen.getByText('$280.4K')).toBeInTheDocument(); | ||
| expect(screen.getByText('$171.9K')).toBeInTheDocument(); | ||
| expect(screen.getByText(usdc.symbol)).toBeInTheDocument(); | ||
| expect(screen.getByText(xvs.symbol)).toBeInTheDocument(); | ||
| }); | ||
| }); |

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This icon isn't used anywhere.