fix(trading): referrer stats tiles (#5143)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
This commit is contained in:
parent
0580e90171
commit
3072b7824f
@ -18,6 +18,7 @@ import { Routes } from '../../lib/links';
|
||||
import { useTransactionEventSubscription } from '@vegaprotocol/web3';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { Statistics } from './referral-statistics';
|
||||
import { useReferralProgram } from './hooks/use-referral-program';
|
||||
|
||||
const RELOAD_DELAY = 3000;
|
||||
|
||||
@ -32,6 +33,7 @@ const validateCode = (value: string) => {
|
||||
};
|
||||
|
||||
export const ApplyCodeForm = () => {
|
||||
const program = useReferralProgram();
|
||||
const navigate = useNavigate();
|
||||
const openWalletDialog = useVegaWalletDialogStore(
|
||||
(store) => store.openVegaWalletDialog
|
||||
@ -237,7 +239,7 @@ export const ApplyCodeForm = () => {
|
||||
{previewData ? (
|
||||
<div className="mt-10">
|
||||
<h2 className="text-2xl mb-5">{t('You are joining')}</h2>
|
||||
<Statistics data={previewData} as="referee" />
|
||||
<Statistics data={previewData} program={program} as="referee" />
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
|
@ -1,5 +1,5 @@
|
||||
query Referees($code: ID!, $aggregationDays: Int) {
|
||||
referralSetReferees(id: $code, aggregationDays: $aggregationDays) {
|
||||
query Referees($code: ID!, $aggregationEpochs: Int) {
|
||||
referralSetReferees(id: $code, aggregationEpochs: $aggregationEpochs) {
|
||||
edges {
|
||||
node {
|
||||
referralSetId
|
||||
|
@ -10,6 +10,7 @@ query ReferralSetStats($code: ID!, $epoch: Int) {
|
||||
referralSetRunningNotionalTakerVolume
|
||||
rewardsMultiplier
|
||||
rewardsFactorMultiplier
|
||||
referrerTakerVolume
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type RefereesQueryVariables = Types.Exact<{
|
||||
code: Types.Scalars['ID'];
|
||||
aggregationDays?: Types.InputMaybe<Types.Scalars['Int']>;
|
||||
aggregationEpochs?: Types.InputMaybe<Types.Scalars['Int']>;
|
||||
}>;
|
||||
|
||||
|
||||
@ -13,8 +13,8 @@ export type RefereesQuery = { __typename?: 'Query', referralSetReferees: { __typ
|
||||
|
||||
|
||||
export const RefereesDocument = gql`
|
||||
query Referees($code: ID!, $aggregationDays: Int) {
|
||||
referralSetReferees(id: $code, aggregationDays: $aggregationDays) {
|
||||
query Referees($code: ID!, $aggregationEpochs: Int) {
|
||||
referralSetReferees(id: $code, aggregationEpochs: $aggregationEpochs) {
|
||||
edges {
|
||||
node {
|
||||
referralSetId
|
||||
@ -42,7 +42,7 @@ export const RefereesDocument = gql`
|
||||
* const { data, loading, error } = useRefereesQuery({
|
||||
* variables: {
|
||||
* code: // value for 'code'
|
||||
* aggregationDays: // value for 'aggregationDays'
|
||||
* aggregationEpochs: // value for 'aggregationEpochs'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
|
@ -9,7 +9,7 @@ export type ReferralSetStatsQueryVariables = Types.Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type ReferralSetStatsQuery = { __typename?: 'Query', referralSetStats: { __typename?: 'ReferralSetStatsConnection', edges: Array<{ __typename?: 'ReferralSetStatsEdge', node: { __typename?: 'ReferralSetStats', atEpoch: number, partyId: string, discountFactor: string, rewardFactor: string, epochNotionalTakerVolume: string, referralSetRunningNotionalTakerVolume: string, rewardsMultiplier: string, rewardsFactorMultiplier: string } } | null> } };
|
||||
export type ReferralSetStatsQuery = { __typename?: 'Query', referralSetStats: { __typename?: 'ReferralSetStatsConnection', edges: Array<{ __typename?: 'ReferralSetStatsEdge', node: { __typename?: 'ReferralSetStats', atEpoch: number, partyId: string, discountFactor: string, rewardFactor: string, epochNotionalTakerVolume: string, referralSetRunningNotionalTakerVolume: string, rewardsMultiplier: string, rewardsFactorMultiplier: string, referrerTakerVolume: string } } | null> } };
|
||||
|
||||
|
||||
export const ReferralSetStatsDocument = gql`
|
||||
@ -25,6 +25,7 @@ export const ReferralSetStatsDocument = gql`
|
||||
referralSetRunningNotionalTakerVolume
|
||||
rewardsMultiplier
|
||||
rewardsFactorMultiplier
|
||||
referrerTakerVolume
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,14 +5,14 @@ import compact from 'lodash/compact';
|
||||
import type { ReferralSetsQueryVariables } from './__generated__/ReferralSets';
|
||||
import { useReferralSetsQuery } from './__generated__/ReferralSets';
|
||||
|
||||
const DEFAULT_AGGREGATION_DAYS = 30;
|
||||
export const DEFAULT_AGGREGATION_DAYS = 30;
|
||||
|
||||
export type Role = 'referrer' | 'referee';
|
||||
type UseReferralArgs = (
|
||||
| { code: string }
|
||||
| { pubKey: string | null; role: Role }
|
||||
) & {
|
||||
aggregationDays?: number;
|
||||
aggregationEpochs?: number;
|
||||
};
|
||||
|
||||
const prepareVariables = (
|
||||
@ -70,9 +70,9 @@ export const useReferral = (args: UseReferralArgs) => {
|
||||
} = useRefereesQuery({
|
||||
variables: {
|
||||
code: referralSet?.id as string,
|
||||
aggregationDays:
|
||||
args.aggregationDays != null
|
||||
? args.aggregationDays
|
||||
aggregationEpochs:
|
||||
args.aggregationEpochs != null
|
||||
? args.aggregationEpochs
|
||||
: DEFAULT_AGGREGATION_DAYS,
|
||||
},
|
||||
skip: !referralSet?.id,
|
||||
|
@ -6,7 +6,7 @@ import {
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useReferral } from './hooks/use-referral';
|
||||
import { DEFAULT_AGGREGATION_DAYS, useReferral } from './hooks/use-referral';
|
||||
import { CreateCodeContainer } from './create-code-form';
|
||||
import classNames from 'classnames';
|
||||
import { Table } from './table';
|
||||
@ -32,21 +32,25 @@ import maxBy from 'lodash/maxBy';
|
||||
export const ReferralStatistics = () => {
|
||||
const { pubKey } = useVegaWallet();
|
||||
|
||||
const program = useReferralProgram();
|
||||
|
||||
const { data: referee } = useReferral({
|
||||
pubKey,
|
||||
role: 'referee',
|
||||
aggregationEpochs: program.details?.windowLength,
|
||||
});
|
||||
const { data: referrer } = useReferral({
|
||||
pubKey,
|
||||
role: 'referrer',
|
||||
aggregationEpochs: program.details?.windowLength,
|
||||
});
|
||||
|
||||
if (referee?.code) {
|
||||
return <Statistics data={referee} as="referee" />;
|
||||
return <Statistics data={referee} program={program} as="referee" />;
|
||||
}
|
||||
|
||||
if (referrer?.code) {
|
||||
return <Statistics data={referrer} as="referrer" />;
|
||||
return <Statistics data={referrer} program={program} as="referrer" />;
|
||||
}
|
||||
|
||||
return <CreateCodeContainer />;
|
||||
@ -54,14 +58,16 @@ export const ReferralStatistics = () => {
|
||||
|
||||
export const Statistics = ({
|
||||
data,
|
||||
program,
|
||||
as,
|
||||
}: {
|
||||
data: NonNullable<ReturnType<typeof useReferral>['data']>;
|
||||
program: ReturnType<typeof useReferralProgram>;
|
||||
as: 'referrer' | 'referee';
|
||||
}) => {
|
||||
const { benefitTiers, details } = program;
|
||||
const { data: epochData } = useCurrentEpochInfoQuery();
|
||||
const { stakeAvailable } = useStakeAvailable();
|
||||
const { benefitTiers } = useReferralProgram();
|
||||
const { data: statsData } = useReferralSetStatsQuery({
|
||||
variables: {
|
||||
code: data.code,
|
||||
@ -72,6 +78,13 @@ export const Statistics = ({
|
||||
|
||||
const currentEpoch = Number(epochData?.epoch.id);
|
||||
|
||||
const compactNumFormat = new Intl.NumberFormat(getUserLocale(), {
|
||||
minimumFractionDigits: 0,
|
||||
maximumFractionDigits: 2,
|
||||
notation: 'compact',
|
||||
compactDisplay: 'short',
|
||||
});
|
||||
|
||||
const stats =
|
||||
statsData?.referralSetStats.edges &&
|
||||
compact(removePaginationWrapper(statsData.referralSetStats.edges));
|
||||
@ -87,10 +100,13 @@ export const Statistics = ({
|
||||
const runningVolumeValue = statsAvailable
|
||||
? Number(statsAvailable.referralSetRunningNotionalTakerVolume)
|
||||
: 0;
|
||||
const referrerVolumeValue = statsAvailable
|
||||
? Number(statsAvailable.referrerTakerVolume)
|
||||
: 0;
|
||||
const multiplier = statsAvailable
|
||||
? Number(statsAvailable.rewardsMultiplier)
|
||||
: 1;
|
||||
const finalCommissionValue = !isNaN(multiplier)
|
||||
const finalCommissionValue = isNaN(multiplier)
|
||||
? baseCommissionValue
|
||||
: multiplier * baseCommissionValue;
|
||||
|
||||
@ -118,7 +134,13 @@ export const Statistics = ({
|
||||
: 0;
|
||||
|
||||
const baseCommissionTile = (
|
||||
<StatTile title={t('Base commission rate')}>
|
||||
<StatTile
|
||||
title={t('Base commission rate')}
|
||||
description={t('(Combined set volume %s over last %s epochs)', [
|
||||
compactNumFormat.format(runningVolumeValue),
|
||||
(details?.windowLength || DEFAULT_AGGREGATION_DAYS).toString(),
|
||||
])}
|
||||
>
|
||||
{baseCommissionValue * 100}%
|
||||
</StatTile>
|
||||
);
|
||||
@ -134,7 +156,16 @@ export const Statistics = ({
|
||||
</StatTile>
|
||||
);
|
||||
const finalCommissionTile = (
|
||||
<StatTile title={t('Final commission rate')}>
|
||||
<StatTile
|
||||
title={t('Final commission rate')}
|
||||
description={
|
||||
!isNaN(multiplier)
|
||||
? `(${baseCommissionValue * 100}% ⨉ ${multiplier} = ${
|
||||
finalCommissionValue * 100
|
||||
}%)`
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
{finalCommissionValue * 100}%
|
||||
</StatTile>
|
||||
);
|
||||
@ -143,12 +174,21 @@ export const Statistics = ({
|
||||
<StatTile title={t('Number of traders')}>{numberOfTradersValue}</StatTile>
|
||||
);
|
||||
|
||||
const codeTile = <CodeTile code={data?.code} />;
|
||||
const createdAtTile = (
|
||||
<StatTile title={t('Created at')}>
|
||||
<span className="text-3xl">
|
||||
{getDateFormat().format(new Date(data.createdAt))}
|
||||
</span>
|
||||
const codeTile = (
|
||||
<CodeTile
|
||||
code={data?.code}
|
||||
createdAt={getDateFormat().format(new Date(data.createdAt))}
|
||||
/>
|
||||
);
|
||||
|
||||
const referrerVolumeTile = (
|
||||
<StatTile
|
||||
title={t(
|
||||
'My volume (last %s epochs)',
|
||||
(details?.windowLength || DEFAULT_AGGREGATION_DAYS).toString()
|
||||
)}
|
||||
>
|
||||
{compactNumFormat.format(referrerVolumeValue)}
|
||||
</StatTile>
|
||||
);
|
||||
|
||||
@ -157,7 +197,10 @@ export const Statistics = ({
|
||||
.reduce((all, r) => all.plus(r), new BigNumber(0));
|
||||
const totalCommissionTile = (
|
||||
<StatTile
|
||||
title={t('Total commission (last 30 days)')}
|
||||
title={t(
|
||||
'Total commission (last %s epochs)',
|
||||
(details?.windowLength || DEFAULT_AGGREGATION_DAYS).toString()
|
||||
)}
|
||||
description={t('(qUSD)')}
|
||||
>
|
||||
{getNumberFormat(0).format(Number(totalCommissionValue))}
|
||||
@ -174,20 +217,13 @@ export const Statistics = ({
|
||||
|
||||
<div className="grid grid-rows-1 gap-5 grid-cols-1 sm:grid-cols-2 xl:grid-cols-4">
|
||||
{codeTile}
|
||||
{createdAtTile}
|
||||
{referrerVolumeTile}
|
||||
{numberOfTradersTile}
|
||||
{totalCommissionTile}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
const compactNumFormat = new Intl.NumberFormat(getUserLocale(), {
|
||||
minimumFractionDigits: 0,
|
||||
maximumFractionDigits: 2,
|
||||
notation: 'compact',
|
||||
compactDisplay: 'short',
|
||||
});
|
||||
|
||||
const currentBenefitTierTile = (
|
||||
<StatTile title={t('Current tier')}>
|
||||
{currentBenefitTierValue?.tier || 'None'}
|
||||
@ -266,7 +302,7 @@ export const Statistics = ({
|
||||
{/* Referees (only for referrer view) */}
|
||||
{as === 'referrer' && data.referees.length > 0 && (
|
||||
<div className="mt-20 mb-20">
|
||||
<h2 className="text-2xl mb-5">{t('Referees')}</h2>
|
||||
<h2 className="mb-5 text-2xl">{t('Referees')}</h2>
|
||||
<div
|
||||
className={classNames(
|
||||
collapsed && [
|
||||
@ -292,10 +328,23 @@ export const Statistics = ({
|
||||
columns={[
|
||||
{ name: 'party', displayName: t('Trader') },
|
||||
{ name: 'joined', displayName: t('Date Joined') },
|
||||
{ name: 'volume', displayName: t('Volume (last 30 days)') },
|
||||
{
|
||||
name: 'volume',
|
||||
displayName: t(
|
||||
'Volume (last %s epochs)',
|
||||
(
|
||||
details?.windowLength || DEFAULT_AGGREGATION_DAYS
|
||||
).toString()
|
||||
),
|
||||
},
|
||||
{
|
||||
name: 'commission',
|
||||
displayName: t('Commission earned (last 30 days)'),
|
||||
displayName: t(
|
||||
'Commission earned (last %s epochs)',
|
||||
(
|
||||
details?.windowLength || DEFAULT_AGGREGATION_DAYS
|
||||
).toString()
|
||||
),
|
||||
},
|
||||
]}
|
||||
data={sortBy(
|
||||
|
@ -109,6 +109,7 @@ export const TiersContainer = () => {
|
||||
<Loading variant="large" />
|
||||
) : (
|
||||
<TiersTable
|
||||
windowLength={details?.windowLength}
|
||||
data={benefitTiers.map((bt) => ({
|
||||
...bt,
|
||||
tierElement: (
|
||||
@ -162,6 +163,7 @@ const StakingTiers = ({
|
||||
|
||||
const TiersTable = ({
|
||||
data,
|
||||
windowLength,
|
||||
}: {
|
||||
data: Array<{
|
||||
tier: number;
|
||||
@ -170,6 +172,7 @@ const TiersTable = ({
|
||||
discount: string;
|
||||
volume: string;
|
||||
}>;
|
||||
windowLength?: number;
|
||||
}) => {
|
||||
return (
|
||||
<Table
|
||||
@ -181,7 +184,15 @@ const TiersTable = ({
|
||||
tooltip: t('A percentage of commission earned by the referrer'),
|
||||
},
|
||||
{ name: 'discount', displayName: t('Referrer trading discount') },
|
||||
{ name: 'volume', displayName: t('Min. trading volume') },
|
||||
{
|
||||
name: 'volume',
|
||||
displayName: t(
|
||||
'Min. trading volume %s',
|
||||
windowLength
|
||||
? t('(last %s epochs)', windowLength.toString())
|
||||
: undefined
|
||||
),
|
||||
},
|
||||
{ name: 'epochs', displayName: t('Min. epochs') },
|
||||
]}
|
||||
data={data.map((d) => ({
|
||||
|
@ -7,6 +7,7 @@ import {
|
||||
import classNames from 'classnames';
|
||||
import type { HTMLAttributes, ReactNode } from 'react';
|
||||
import { Button } from './buttons';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
|
||||
export const Tile = ({
|
||||
className,
|
||||
@ -54,13 +55,18 @@ const FADE_OUT_STYLE = classNames(
|
||||
|
||||
export const CodeTile = ({
|
||||
code,
|
||||
createdAt,
|
||||
className,
|
||||
}: {
|
||||
code: string;
|
||||
createdAt?: string;
|
||||
className?: string;
|
||||
}) => {
|
||||
return (
|
||||
<StatTile title="Your referral code">
|
||||
<StatTile
|
||||
title={t('Your referral code')}
|
||||
description={createdAt ? t('(Created at: %s)', createdAt) : undefined}
|
||||
>
|
||||
<div className="flex gap-2 items-center justify-between">
|
||||
<Tooltip
|
||||
description={
|
||||
@ -82,7 +88,7 @@ export const CodeTile = ({
|
||||
</Tooltip>
|
||||
<CopyWithTooltip text={code}>
|
||||
<Button className="text-sm no-underline !py-0 !px-0 h-fit !bg-transparent">
|
||||
<span className="sr-only">Copy</span>
|
||||
<span className="sr-only">{t('Copy')}</span>
|
||||
<VegaIcon size={24} name={VegaIconNames.COPY} />
|
||||
</Button>
|
||||
</CopyWithTooltip>
|
||||
|
8
libs/types/src/__generated__/types.ts
generated
8
libs/types/src/__generated__/types.ts
generated
@ -813,7 +813,7 @@ export type DispatchStrategy = {
|
||||
/** Minimum notional time-weighted averaged position required for a party to be considered eligible */
|
||||
notionalTimeWeightedAveragePositionRequirement: Scalars['String'];
|
||||
/** Ascending order list of start rank and corresponding share ratio */
|
||||
rankTable?: Maybe<RankTable>;
|
||||
rankTable?: Maybe<Array<Maybe<RankTable>>>;
|
||||
/** Minimum number of governance tokens, e.g. VEGA, staked for a party to be considered eligible */
|
||||
stakingRequirement: Scalars['String'];
|
||||
/** The teams in scope for the reward, if the entity is teams */
|
||||
@ -3624,6 +3624,8 @@ export type PartyVestingStats = {
|
||||
__typename?: 'PartyVestingStats';
|
||||
/** Epoch for which the statistics are valid */
|
||||
epochSeq: Scalars['Int'];
|
||||
/** The balance of the party, in quantum. */
|
||||
quantumBalance: Scalars['String'];
|
||||
/** The reward bonus multiplier */
|
||||
rewardBonusMultiplier: Scalars['String'];
|
||||
};
|
||||
@ -4831,7 +4833,7 @@ export type QueryprotocolUpgradeProposalsArgs = {
|
||||
|
||||
/** Queries allow a caller to read data and filter data via GraphQL. */
|
||||
export type QueryreferralSetRefereesArgs = {
|
||||
aggregationDays?: InputMaybe<Scalars['Int']>;
|
||||
aggregationEpochs?: InputMaybe<Scalars['Int']>;
|
||||
id?: InputMaybe<Scalars['ID']>;
|
||||
pagination?: InputMaybe<Pagination>;
|
||||
referee?: InputMaybe<Scalars['ID']>;
|
||||
@ -5088,6 +5090,8 @@ export type ReferralSetStats = {
|
||||
partyId: Scalars['ID'];
|
||||
/** Running volume for the set based on the window length of the current referral program. */
|
||||
referralSetRunningNotionalTakerVolume: Scalars['String'];
|
||||
/** The referrer's taker volume */
|
||||
referrerTakerVolume: Scalars['String'];
|
||||
/** Reward factor applied to the party. */
|
||||
rewardFactor: Scalars['String'];
|
||||
/** The proportion of the referees taker fees to be rewarded to the referrer. */
|
||||
|
Loading…
Reference in New Issue
Block a user