From dadc6141e8e05b665a7e3ffbf0f31cd2e68a4924 Mon Sep 17 00:00:00 2001 From: Madalina Raicu Date: Fri, 8 Dec 2023 09:20:22 +0000 Subject: [PATCH] feat: initial version rewards --- apps/trading/.env | 12 +- .../rewards-container/Rewards.graphql | 20 + .../__generated__/Rewards.ts | 56 +++ .../rewards-container/activity-rewards.tsx | 347 ++++++++++++++++++ .../rewards-container/rewards-container.tsx | 26 ++ .../rewards-container/rewards-history.tsx | 10 +- libs/positions/src/lib/positions-table.tsx | 2 +- .../components/protocol-upgrade-countdown.tsx | 2 +- ...ion-mark.tsx => icon-exclamation-mark.tsx} | 2 +- .../icon/vega-icons/svg-icons/icon-man.tsx | 28 ++ .../icon/vega-icons/svg-icons/icon-streak.tsx | 32 ++ .../icon/vega-icons/svg-icons/icon-team.tsx | 19 + .../icon/vega-icons/vega-icon-record.ts | 15 +- 13 files changed, 560 insertions(+), 11 deletions(-) create mode 100644 apps/trading/components/rewards-container/activity-rewards.tsx rename libs/ui-toolkit/src/components/icon/vega-icons/svg-icons/{icon-exclaimation-mark.tsx => icon-exclamation-mark.tsx} (73%) create mode 100644 libs/ui-toolkit/src/components/icon/vega-icons/svg-icons/icon-man.tsx create mode 100644 libs/ui-toolkit/src/components/icon/vega-icons/svg-icons/icon-streak.tsx create mode 100644 libs/ui-toolkit/src/components/icon/vega-icons/svg-icons/icon-team.tsx diff --git a/apps/trading/.env b/apps/trading/.env index 9f81c084d..853689764 100644 --- a/apps/trading/.env +++ b/apps/trading/.env @@ -3,16 +3,17 @@ NX_ETHERSCAN_URL=https://sepolia.etherscan.io NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz NX_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.sentry.io/6300613 -NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/stagnet1/vegawallet-stagnet1.toml -NX_VEGA_ENV=STAGNET1 -NX_VEGA_EXPLORER_URL=https://explorer.stagnet1.vega.rocks +NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/fairground/vegawallet-fairground.toml +NX_VEGA_ENV=TESTNET +NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf NX_VEGA_NETWORKS={\"MAINNET\":\"https://console.vega.xyz\",\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\"} -NX_VEGA_TOKEN_URL=https://governance.stagnet1.vega.rocks +NX_VEGA_TOKEN_URL=https://governance.fairground.wtf NX_VEGA_WALLET_URL=http://localhost:1789 NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports +NX_VEGA_CONSOLE_URL=https://console.fairground.wtf NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json @@ -25,3 +26,6 @@ NX_ICEBERG_ORDERS=true # NX_PRODUCT_PERPETUALS NX_METAMASK_SNAPS=true NX_REFERRALS=true + +NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz +NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket diff --git a/apps/trading/components/rewards-container/Rewards.graphql b/apps/trading/components/rewards-container/Rewards.graphql index af44cb177..d983b50d4 100644 --- a/apps/trading/components/rewards-container/Rewards.graphql +++ b/apps/trading/components/rewards-container/Rewards.graphql @@ -36,6 +36,26 @@ query RewardsPage($partyId: ID!) { } } +query ActivityStreak($partyId: ID!) { + partiesConnection(id: $partyId) { + edges { + node { + id + activityStreak { + activeFor + isActive + inactiveFor + rewardDistributionMultiplier + rewardVestingMultiplier + epoch + tradedVolume + openVolume + } + } + } + } +} + query RewardsHistory( $partyId: ID! $epochRewardSummariesPagination: Pagination diff --git a/apps/trading/components/rewards-container/__generated__/Rewards.ts b/apps/trading/components/rewards-container/__generated__/Rewards.ts index 3b28499ce..3b9a8f588 100644 --- a/apps/trading/components/rewards-container/__generated__/Rewards.ts +++ b/apps/trading/components/rewards-container/__generated__/Rewards.ts @@ -10,6 +10,13 @@ export type RewardsPageQueryVariables = Types.Exact<{ export type RewardsPageQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, vestingStats?: { __typename?: 'PartyVestingStats', rewardBonusMultiplier: string } | null, activityStreak?: { __typename?: 'PartyActivityStreak', rewardVestingMultiplier: string, rewardDistributionMultiplier: string } | null, vestingBalancesSummary: { __typename?: 'PartyVestingBalancesSummary', epoch?: number | null, vestingBalances?: Array<{ __typename?: 'PartyVestingBalance', balance: string, asset: { __typename?: 'Asset', id: string, symbol: string, decimals: number, quantum: string } }> | null, lockedBalances?: Array<{ __typename?: 'PartyLockedBalance', balance: string, untilEpoch: number, asset: { __typename?: 'Asset', id: string, symbol: string, decimals: number, quantum: string } }> | null } } | null }; +export type ActivityStreakQueryVariables = Types.Exact<{ + partyId: Types.Scalars['ID']; +}>; + + +export type ActivityStreakQuery = { __typename?: 'Query', partiesConnection?: { __typename?: 'PartyConnection', edges: Array<{ __typename?: 'PartyEdge', node: { __typename?: 'Party', id: string, activityStreak?: { __typename?: 'PartyActivityStreak', activeFor: number, isActive: boolean, inactiveFor: number, rewardDistributionMultiplier: string, rewardVestingMultiplier: string, epoch: number, tradedVolume: string, openVolume: string } | null } }> } | null }; + export type RewardsHistoryQueryVariables = Types.Exact<{ partyId: Types.Scalars['ID']; epochRewardSummariesPagination?: Types.InputMaybe; @@ -91,6 +98,55 @@ export function useRewardsPageLazyQuery(baseOptions?: Apollo.LazyQueryHookOption export type RewardsPageQueryHookResult = ReturnType; export type RewardsPageLazyQueryHookResult = ReturnType; export type RewardsPageQueryResult = Apollo.QueryResult; +export const ActivityStreakDocument = gql` + query ActivityStreak($partyId: ID!) { + partiesConnection(id: $partyId) { + edges { + node { + id + activityStreak { + activeFor + isActive + inactiveFor + rewardDistributionMultiplier + rewardVestingMultiplier + epoch + tradedVolume + openVolume + } + } + } + } +} + `; + +/** + * __useActivityStreakQuery__ + * + * To run a query within a React component, call `useActivityStreakQuery` and pass it any options that fit your needs. + * When your component renders, `useActivityStreakQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useActivityStreakQuery({ + * variables: { + * partyId: // value for 'partyId' + * }, + * }); + */ +export function useActivityStreakQuery(baseOptions: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(ActivityStreakDocument, options); + } +export function useActivityStreakLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(ActivityStreakDocument, options); + } +export type ActivityStreakQueryHookResult = ReturnType; +export type ActivityStreakLazyQueryHookResult = ReturnType; +export type ActivityStreakQueryResult = Apollo.QueryResult; export const RewardsHistoryDocument = gql` query RewardsHistory($partyId: ID!, $epochRewardSummariesPagination: Pagination, $partyRewardsPagination: Pagination, $fromEpoch: Int, $toEpoch: Int) { epochRewardSummaries( diff --git a/apps/trading/components/rewards-container/activity-rewards.tsx b/apps/trading/components/rewards-container/activity-rewards.tsx new file mode 100644 index 000000000..43007b90f --- /dev/null +++ b/apps/trading/components/rewards-container/activity-rewards.tsx @@ -0,0 +1,347 @@ +import type { AssetFieldsFragment } from '@vegaprotocol/assets'; +import { useRewardsHistoryQuery } from './__generated__/Rewards'; +import { useReferralProgram } from '../../client-pages/referrals/hooks/use-referral-program'; +import { useState, useEffect } from 'react'; +import { useT } from '../../lib/use-t'; +import { useRewardsRowData } from './use-reward-row-data'; +import { addDecimalsFormatNumber } from '@vegaprotocol/utils'; +import classNames from 'classnames'; +import { + Icon, + type IconName, + Intent, + Tooltip, + VegaIcon, + VegaIconNames, +} from '@vegaprotocol/ui-toolkit'; +import { IconNames } from '@blueprintjs/icons'; + +export const ActiveRewards = ({ + epoch, + pubKey, + assets, +}: { + pubKey: string | null; + epoch: number; + assets: Record; +}) => { + const [epochVariables] = useState(() => ({ + from: epoch - 1, + to: epoch, + })); + + // No need to specify the fromEpoch as it will by default give you the last + const { refetch, data } = useRewardsHistoryQuery({ + variables: { + partyId: pubKey || '', + fromEpoch: epochVariables.from, + toEpoch: epochVariables.to, + }, + }); + + useEffect(() => { + const interval = setInterval(refetch, 30000); + return () => clearInterval(interval); + }, [refetch]); + + const rowData = useRewardsRowData({ + epochRewardSummaries: data?.epochRewardSummaries, + partyRewards: data?.party?.rewardsConnection, + assets, + partyId: pubKey, + }); + + const t = useT(); + if (!pubKey) { + return ( +
+

{t('Not connected')}

+
+ ); + } + + // TODO: extract card component - call it reward tiles + return ( +
+ {rowData.map((row, i) => { + // TODO: filter out 0 values + // const entries = Object.entries(row).filter(([key, value]) => { + // value !== 0; + // }); + return ( +
+
+
+
+
+ + + + + {t('Individual')} + +
+ +
+ + + {`${addDecimalsFormatNumber( + row.total, + row.asset?.decimals || 0 + )}`} + + + {`${row.asset?.symbol}`} + + + + {t('Pro rata')} + +
+ +
+ + + + + {t('11 epochs')} + +
+
+ + + + + {t('Price taking')} • {row.asset?.symbol} • {row.asset?.name} + + +
+ + {t('Ends in')} + {t('5 epochs')} + + + + + {t('Assessed over')} + + {t('5 epochs')} + +
+ + + {t( + 'Get rewards for taking prices on the order book and paying fees.' + )} + + + + + {/*
*/} +
+ + + {t('Entity scope')}{' '} + + + + + + + + + + + + + {t('Amount staked')}{' '} + + + {t('200 VEGA')} + + + + + + + {t('Average position')}{' '} + + + {t('100 USDT')} + + + +
+
+
+
+ ); + })} +
+ ); +}; + +// const getIconIntent = (status: string) => { +// switch (status) { +// case 'GOOD': +// return { icon: IconNames.TICK_CIRCLE, intent: Intent.Success }; +// case 'RETIRED': +// return { icon: IconNames.MOON, intent: Intent.None }; +// case 'UNKNOWN': +// return { icon: IconNames.HELP, intent: Intent.Primary }; +// case 'MALICIOUS': +// return { icon: IconNames.ERROR, intent: Intent.Danger }; +// case 'SUSPICIOUS': +// return { icon: IconNames.ERROR, intent: Intent.Danger }; +// case 'COMPROMISED': +// return { icon: IconNames.ERROR, intent: Intent.Danger }; +// default: +// return { icon: IconNames.HELP, intent: Intent.Primary }; +// } +// }; + +const StatusIndicator = ({ + intent, + icon, +}: { + intent: Intent; + icon: string; +}) => { + return ( + + + + ); +}; + +export const ActivityStreak = ({ + epoch, + pubKey, + assets, +}: { + pubKey: string | null; + epoch: number; + assets: Record; +}) => { + // const { data } = useActivityStreakQuery({ + // variables: { + // partyId: pubKey || '', + // }, + // }); + + const { benefitTiers } = useReferralProgram(); + + // const streaks = data?.partiesConnection?.edges?.map( + // (edge) => edge?.node?.activityStreak + // ); + + // @Input() + const progress = 30; + const total = 100; + + const safeProgress = () => { + return (progress / total) * 100; + }; + + const progressBarHeight = 'h-6'; + + return ( + <> +
+
+
+ {benefitTiers.map((tier, index) => { + return ( +
+ + + Tier {tier.tier} + 7 days + + + + Reward 1x + Vesting 1.5x + + + + +
+ ); + })} +
+
+ +
+ {benefitTiers.map((tier, index) => { + return ( +
+
+
+
+
+ ); + })} +
+ +
+ + + + 4 days streak + + 3 days  to Tier 1 + + +
+
+ {/*
{JSON.stringify(stakingTiers)}
*/} + {/*
{JSON.stringify(details)}
*/} + {/*
{JSON.stringify(benefitTiers)}
*/} + {/*
{JSON.stringify(streaks)}
*/} + + ); +}; diff --git a/apps/trading/components/rewards-container/rewards-container.tsx b/apps/trading/components/rewards-container/rewards-container.tsx index 40ab77210..a0d2decea 100644 --- a/apps/trading/components/rewards-container/rewards-container.tsx +++ b/apps/trading/components/rewards-container/rewards-container.tsx @@ -33,6 +33,7 @@ import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id'; import { RewardsHistoryContainer } from './rewards-history'; import { useT } from '../../lib/use-t'; import { useAssetsMapProvider } from '@vegaprotocol/assets'; +import { ActiveRewards, ActivityStreak } from './activity-rewards'; const ASSETS_WITH_INCORRECT_VESTING_REWARD_DATA = [ 'bf1e88d19db4b3ca0d1d5bdb73718a01686b18cf731ca26adedf3c8b83802bba', // USDT mainnet @@ -56,6 +57,7 @@ export const RewardsContainer = () => { // No need to specify the fromEpoch as it will by default give you the last // Note activityStreak in query will fail + const { data: rewardsData, loading: rewardsLoading } = useRewardsPageQuery({ variables: { partyId: pubKey || '', @@ -223,6 +225,30 @@ export const RewardsContainer = () => { ); })} + + + + + + + + +

{t('Not connected')}

+
+ ); + } + return (
@@ -342,7 +350,7 @@ export const RewardHistoryTable = ({ ); }; -const EpochInput = ({ +export const EpochInput = ({ id, value, max, diff --git a/libs/positions/src/lib/positions-table.tsx b/libs/positions/src/lib/positions-table.tsx index c63de63cf..9dd9e0109 100644 --- a/libs/positions/src/lib/positions-table.tsx +++ b/libs/positions/src/lib/positions-table.tsx @@ -563,7 +563,7 @@ const WarningCell = ({
{showIcon && ( - + )} diff --git a/libs/proposals/src/components/protocol-upgrade-countdown.tsx b/libs/proposals/src/components/protocol-upgrade-countdown.tsx index 36d7af70c..b8c029bc3 100644 --- a/libs/proposals/src/components/protocol-upgrade-countdown.tsx +++ b/libs/proposals/src/components/protocol-upgrade-countdown.tsx @@ -94,7 +94,7 @@ export const ProtocolUpgradeCountdown = ({ } )} > - {' '} + {' '} {t('Network upgrade in {{countdown}}', { countdown })} diff --git a/libs/ui-toolkit/src/components/icon/vega-icons/svg-icons/icon-exclaimation-mark.tsx b/libs/ui-toolkit/src/components/icon/vega-icons/svg-icons/icon-exclamation-mark.tsx similarity index 73% rename from libs/ui-toolkit/src/components/icon/vega-icons/svg-icons/icon-exclaimation-mark.tsx rename to libs/ui-toolkit/src/components/icon/vega-icons/svg-icons/icon-exclamation-mark.tsx index e0d6db2f9..258e36686 100644 --- a/libs/ui-toolkit/src/components/icon/vega-icons/svg-icons/icon-exclaimation-mark.tsx +++ b/libs/ui-toolkit/src/components/icon/vega-icons/svg-icons/icon-exclamation-mark.tsx @@ -1,4 +1,4 @@ -export const IconExclaimationMark = ({ size = 16 }: { size: number }) => { +export const IconExclamationMark = ({ size = 16 }: { size: number }) => { return ( diff --git a/libs/ui-toolkit/src/components/icon/vega-icons/svg-icons/icon-man.tsx b/libs/ui-toolkit/src/components/icon/vega-icons/svg-icons/icon-man.tsx new file mode 100644 index 000000000..e59017cc4 --- /dev/null +++ b/libs/ui-toolkit/src/components/icon/vega-icons/svg-icons/icon-man.tsx @@ -0,0 +1,28 @@ +export const IconMan = ({ size = 14 }: { size: number }) => { + return ( + + + + + ); +}; diff --git a/libs/ui-toolkit/src/components/icon/vega-icons/svg-icons/icon-streak.tsx b/libs/ui-toolkit/src/components/icon/vega-icons/svg-icons/icon-streak.tsx new file mode 100644 index 000000000..eb98bc9ba --- /dev/null +++ b/libs/ui-toolkit/src/components/icon/vega-icons/svg-icons/icon-streak.tsx @@ -0,0 +1,32 @@ +export const IconStreak = ({ size = 14 }: { size: number }) => { + return ( + + + + + + + + + + + ); +}; diff --git a/libs/ui-toolkit/src/components/icon/vega-icons/svg-icons/icon-team.tsx b/libs/ui-toolkit/src/components/icon/vega-icons/svg-icons/icon-team.tsx new file mode 100644 index 000000000..15e2cf59f --- /dev/null +++ b/libs/ui-toolkit/src/components/icon/vega-icons/svg-icons/icon-team.tsx @@ -0,0 +1,19 @@ +export const IconTeam = ({ size = 14 }: { size: number }) => { + return ( + + + + + + ); +}; diff --git a/libs/ui-toolkit/src/components/icon/vega-icons/vega-icon-record.ts b/libs/ui-toolkit/src/components/icon/vega-icons/vega-icon-record.ts index df902fba9..3ac36babf 100644 --- a/libs/ui-toolkit/src/components/icon/vega-icons/vega-icon-record.ts +++ b/libs/ui-toolkit/src/components/icon/vega-icons/vega-icon-record.ts @@ -14,7 +14,7 @@ import { IconCopy } from './svg-icons/icon-copy'; import { IconCross } from './svg-icons/icon-cross'; import { IconDeposit } from './svg-icons/icon-deposit'; import { IconEdit } from './svg-icons/icon-edit'; -import { IconExclaimationMark } from './svg-icons/icon-exclaimation-mark'; +import { IconExclamationMark } from './svg-icons/icon-exclamation-mark'; import { IconEye } from './svg-icons/icon-eye'; import { IconEyeOff } from './svg-icons/icon-eye-off'; import { IconForum } from './svg-icons/icon-forum'; @@ -41,6 +41,9 @@ import { IconTwitter } from './svg-icons/icon-twitter'; import { IconVote } from './svg-icons/icon-vote'; import { IconWarning } from './svg-icons/icon-warning'; import { IconWithdraw } from './svg-icons/icon-withdraw'; +import { IconMan } from './svg-icons/icon-man'; +import { IconTeam } from './svg-icons/icon-team'; +import { IconStreak } from './svg-icons/icon-streak'; export enum VegaIconNames { ARROW_DOWN = 'arrow-down', @@ -59,7 +62,7 @@ export enum VegaIconNames { CROSS = 'cross', DEPOSIT = 'deposit', EDIT = 'edit', - EXCLAIMATION_MARK = 'exclaimation-mark', + EXCLAMATION_MARK = 'exclamation-mark', EYE = 'eye', EYE_OFF = 'eye-off', FORUM = 'forum', @@ -76,6 +79,7 @@ export enum VegaIconNames { QUESTION_MARK = 'question-mark', SEARCH = 'search', STAR = 'star', + STREAK = 'streak', SUN = 'sun', TICK = 'tick', TICKET = 'ticket', @@ -86,6 +90,8 @@ export enum VegaIconNames { VOTE = 'vote', WITHDRAW = 'withdraw', WARNING = 'warning', + MAN = 'man', + TEAM = 'team', } export const VegaIconNameMap: Record< @@ -102,7 +108,7 @@ export const VegaIconNameMap: Record< 'chevron-right': IconChevronRight, 'chevron-up': IconChevronUp, 'eye-off': IconEyeOff, - 'exclaimation-mark': IconExclaimationMark, + 'exclamation-mark': IconExclamationMark, 'open-external': IconOpenExternal, 'question-mark': IconQuestionMark, 'trend-down': IconTrendDown, @@ -135,4 +141,7 @@ export const VegaIconNameMap: Record< vote: IconVote, withdraw: IconWithdraw, warning: IconWarning, + man: IconMan, + team: IconTeam, + streak: IconStreak, };