feat: preview, ux changes
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -7,7 +7,7 @@ export type NewMarketProductFieldsFragment = { __typename?: 'Proposal', terms: {
|
||||
|
||||
export type UpdateMarketStatesFragment = { __typename?: 'Proposal', terms: { __typename?: 'ProposalTerms', change: { __typename?: 'CancelTransfer' } | { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket' } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateMarketState', updateType: Types.MarketUpdateType, price?: string | null, market: { __typename?: 'Market', decimalPlaces: number, id: string, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, code: string, product: { __typename: 'Future', quoteName: string } | { __typename: 'Perpetual', quoteName: string } | { __typename: 'Spot' } } } } } | { __typename?: 'UpdateNetworkParameter' } | { __typename?: 'UpdateReferralProgram' } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram' } } };
|
||||
|
||||
export type UpdateReferralProgramsFragment = { __typename?: 'Proposal', terms: { __typename?: 'ProposalTerms', change: { __typename?: 'CancelTransfer' } | { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket' } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateMarketState' } | { __typename?: 'UpdateNetworkParameter' } | { __typename?: 'UpdateReferralProgram', windowLength: number, endOfProgram: string, benefitTiers: Array<{ __typename?: 'BenefitTier', minimumEpochs: number, minimumRunningNotionalTakerVolume: string, referralDiscountFactor: string, referralRewardFactor: string }>, stakingTiers: Array<{ __typename?: 'StakingTier', minimumStakedTokens: string, referralRewardMultiplier: string }> } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram' } } };
|
||||
export type UpdateReferralProgramsFragment = { __typename?: 'Proposal', terms: { __typename?: 'ProposalTerms', change: { __typename?: 'CancelTransfer' } | { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket' } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateMarketState' } | { __typename?: 'UpdateNetworkParameter' } | { __typename?: 'UpdateReferralProgram', windowLength: number, endOfProgram: any, benefitTiers: Array<{ __typename?: 'BenefitTier', minimumEpochs: number, minimumRunningNotionalTakerVolume: string, referralDiscountFactor: string, referralRewardFactor: string }>, stakingTiers: Array<{ __typename?: 'StakingTier', minimumStakedTokens: string, referralRewardMultiplier: string }> } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram' } } };
|
||||
|
||||
export type UpdateVolumeDiscountProgramsFragment = { __typename?: 'Proposal', terms: { __typename?: 'ProposalTerms', change: { __typename?: 'CancelTransfer' } | { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket' } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateMarketState' } | { __typename?: 'UpdateNetworkParameter' } | { __typename?: 'UpdateReferralProgram' } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram', endOfProgramTimestamp: any, windowLength: number, benefitTiers: Array<{ __typename?: 'VolumeBenefitTier', minimumRunningNotionalTakerVolume: string, volumeDiscountFactor: string }> } } };
|
||||
|
||||
@@ -21,7 +21,7 @@ export type ProposalsQueryVariables = Types.Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type ProposalsQuery = { __typename?: 'Query', proposalsConnection?: { __typename?: 'ProposalsConnection', edges?: Array<{ __typename?: 'ProposalEdge', node: { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: string }, terms: { __typename?: 'ProposalTerms', closingDatetime: any, enactmentDatetime?: any | null, change: { __typename?: 'CancelTransfer' } | { __typename: 'NewAsset', name: string, symbol: string, decimals: number, quantum: string, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string, withdrawThreshold: string, lifetimeLimit: string } } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', settlementAsset: { __typename?: 'Asset', symbol: string } } | null, product?: { __typename: 'FutureProduct' } | { __typename: 'PerpetualProduct' } | { __typename: 'SpotProduct' } | null } } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset', quantum: string, assetId: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename?: 'UpdateMarket', marketId: string } | { __typename?: 'UpdateMarketState', updateType: Types.MarketUpdateType, price?: string | null, market: { __typename?: 'Market', decimalPlaces: number, id: string, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, code: string, product: { __typename: 'Future', quoteName: string } | { __typename: 'Perpetual', quoteName: string } | { __typename: 'Spot' } } } } } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } | { __typename?: 'UpdateReferralProgram', windowLength: number, endOfProgram: string, benefitTiers: Array<{ __typename?: 'BenefitTier', minimumEpochs: number, minimumRunningNotionalTakerVolume: string, referralDiscountFactor: string, referralRewardFactor: string }>, stakingTiers: Array<{ __typename?: 'StakingTier', minimumStakedTokens: string, referralRewardMultiplier: string }> } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram', endOfProgramTimestamp: any, windowLength: number, benefitTiers: Array<{ __typename?: 'VolumeBenefitTier', minimumRunningNotionalTakerVolume: string, volumeDiscountFactor: string }> } }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string } } } } | null> | null } | null };
|
||||
export type ProposalsQuery = { __typename?: 'Query', proposalsConnection?: { __typename?: 'ProposalsConnection', edges?: Array<{ __typename?: 'ProposalEdge', node: { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: string }, terms: { __typename?: 'ProposalTerms', closingDatetime: any, enactmentDatetime?: any | null, change: { __typename?: 'CancelTransfer' } | { __typename: 'NewAsset', name: string, symbol: string, decimals: number, quantum: string, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string, withdrawThreshold: string, lifetimeLimit: string } } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', settlementAsset: { __typename?: 'Asset', symbol: string } } | null, product?: { __typename: 'FutureProduct' } | { __typename: 'PerpetualProduct' } | { __typename: 'SpotProduct' } | null } } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset', quantum: string, assetId: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename?: 'UpdateMarket', marketId: string } | { __typename?: 'UpdateMarketState', updateType: Types.MarketUpdateType, price?: string | null, market: { __typename?: 'Market', decimalPlaces: number, id: string, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, code: string, product: { __typename: 'Future', quoteName: string } | { __typename: 'Perpetual', quoteName: string } | { __typename: 'Spot' } } } } } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } | { __typename?: 'UpdateReferralProgram', windowLength: number, endOfProgram: any, benefitTiers: Array<{ __typename?: 'BenefitTier', minimumEpochs: number, minimumRunningNotionalTakerVolume: string, referralDiscountFactor: string, referralRewardFactor: string }>, stakingTiers: Array<{ __typename?: 'StakingTier', minimumStakedTokens: string, referralRewardMultiplier: string }> } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram', endOfProgramTimestamp: any, windowLength: number, benefitTiers: Array<{ __typename?: 'VolumeBenefitTier', minimumRunningNotionalTakerVolume: string, volumeDiscountFactor: string }> } }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string } } } } | null> | null } | null };
|
||||
|
||||
export const NewMarketProductFieldsFragmentDoc = gql`
|
||||
fragment NewMarketProductFields on Proposal {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
Input,
|
||||
InputError,
|
||||
Loader,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
@@ -16,9 +17,21 @@ import { useReferral } from './hooks/use-referral';
|
||||
import { Routes } from '../../lib/links';
|
||||
import { useTransactionEventSubscription } from '@vegaprotocol/web3';
|
||||
import { clearTimeout } from 'timers';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { Statistics } from './referral-statistics';
|
||||
|
||||
const RELOAD_DELAY = 3000;
|
||||
|
||||
const validateCode = (value: string) => {
|
||||
const number = +`0x${value}`;
|
||||
if (!value || value.length !== 64) {
|
||||
return t('Code must be 64 characters in length');
|
||||
} else if (Number.isNaN(number)) {
|
||||
return t('Code must be be valid hex');
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
export const ApplyCodeForm = () => {
|
||||
const navigate = useNavigate();
|
||||
const openWalletDialog = useVegaWalletDialogStore(
|
||||
@@ -36,11 +49,17 @@ export const ApplyCodeForm = () => {
|
||||
formState: { errors },
|
||||
setValue,
|
||||
setError,
|
||||
watch,
|
||||
} = useForm();
|
||||
const [params] = useSearchParams();
|
||||
|
||||
const { data: referee } = useReferral(pubKey, 'referee');
|
||||
const { data: referrer } = useReferral(pubKey, 'referrer');
|
||||
const { data: referee } = useReferral({ pubKey, role: 'referee' });
|
||||
const { data: referrer } = useReferral({ pubKey, role: 'referrer' });
|
||||
|
||||
const codeField = watch('code');
|
||||
const { data: previewData, loading: previewLoading } = useReferral({
|
||||
code: validateCode(codeField) ? codeField : undefined,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const code = params.get('code');
|
||||
@@ -121,14 +140,16 @@ export const ApplyCodeForm = () => {
|
||||
}, RELOAD_DELAY);
|
||||
}
|
||||
return () => {
|
||||
clearTimeout(to);
|
||||
if (to) clearTimeout(to);
|
||||
};
|
||||
}, [navigate, status]);
|
||||
|
||||
// go to main page if the current pubkey is already a referrer or referee
|
||||
if (referee || referrer) {
|
||||
return <Navigate to={Routes.REFERRALS} />;
|
||||
}
|
||||
|
||||
// show "code applied" message when successfully applied
|
||||
if (status === 'successful') {
|
||||
return (
|
||||
<div className="w-1/2 mx-auto">
|
||||
@@ -179,35 +200,51 @@ export const ApplyCodeForm = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-2/3 max-w-md mx-auto bg-vega-clight-800 dark:bg-vega-cdark-800 p-8 rounded-lg">
|
||||
<h3 className="mb-4 text-2xl text-center calt">Apply a referral code</h3>
|
||||
<p className="mb-4 text-center text-base">
|
||||
Enter a referral code to get trading discounts.
|
||||
</p>
|
||||
<form
|
||||
className={classNames('w-full flex flex-col gap-4', {
|
||||
'animate-shake': Boolean(errors.code),
|
||||
})}
|
||||
onSubmit={handleSubmit(onSubmit)}
|
||||
>
|
||||
<label>
|
||||
<span className="sr-only">Your referral code</span>
|
||||
<Input
|
||||
hasError={Boolean(errors.code)}
|
||||
{...register('code', {
|
||||
required: 'You have to provide a code to apply it.',
|
||||
})}
|
||||
placeholder="Enter a code"
|
||||
className="mb-2 bg-vega-clight-900 dark:bg-vega-cdark-700"
|
||||
/>
|
||||
</label>
|
||||
<RainbowButton variant="border" {...getButtonProps()} />
|
||||
</form>
|
||||
{errors.code && (
|
||||
<InputError className="break-words overflow-auto">
|
||||
{errors.code.message?.toString()}
|
||||
</InputError>
|
||||
)}
|
||||
</div>
|
||||
<>
|
||||
<div className="w-2/3 max-w-md mx-auto bg-vega-clight-800 dark:bg-vega-cdark-800 p-8 rounded-lg">
|
||||
<h3 className="mb-4 text-2xl text-center calt">
|
||||
Apply a referral code
|
||||
</h3>
|
||||
<p className="mb-4 text-center text-base">
|
||||
Enter a referral code to get trading discounts.
|
||||
</p>
|
||||
<form
|
||||
className={classNames('w-full flex flex-col gap-4', {
|
||||
'animate-shake': Boolean(errors.code),
|
||||
})}
|
||||
onSubmit={handleSubmit(onSubmit)}
|
||||
>
|
||||
<label>
|
||||
<span className="sr-only">Your referral code</span>
|
||||
<Input
|
||||
hasError={Boolean(errors.code)}
|
||||
{...register('code', {
|
||||
required: 'You have to provide a code to apply it.',
|
||||
validate: validateCode,
|
||||
})}
|
||||
placeholder="Enter a code"
|
||||
className="mb-2 bg-vega-clight-900 dark:bg-vega-cdark-700"
|
||||
/>
|
||||
</label>
|
||||
<RainbowButton variant="border" {...getButtonProps()} />
|
||||
</form>
|
||||
{errors.code && (
|
||||
<InputError className="break-words overflow-auto">
|
||||
{errors.code.message?.toString()}
|
||||
</InputError>
|
||||
)}
|
||||
</div>
|
||||
{previewLoading && !previewData ? (
|
||||
<div className="mt-10">
|
||||
<Loader />
|
||||
</div>
|
||||
) : null}
|
||||
{previewData ? (
|
||||
<div className="mt-10">
|
||||
<h2 className="text-2xl mb-5">You are joining</h2>
|
||||
<Statistics data={previewData} as="referee" />
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
ABOUT_REFERRAL_DOCS_LINK,
|
||||
DISCLAIMER_REFERRAL_DOCS_LINK,
|
||||
} from './constants';
|
||||
import { useReferral } from './hooks/use-referral';
|
||||
|
||||
export const CreateCodeContainer = () => {
|
||||
return <CreateCodeForm />;
|
||||
@@ -33,7 +34,7 @@ export const CreateCodeForm = () => {
|
||||
const openWalletDialog = useVegaWalletDialogStore(
|
||||
(store) => store.openVegaWalletDialog
|
||||
);
|
||||
const { pubKey } = useVegaWallet();
|
||||
const { pubKey, isReadOnly } = useVegaWallet();
|
||||
|
||||
return (
|
||||
<div className="w-2/3 max-w-md mx-auto bg-vega-clight-800 dark:bg-vega-cdark-800 p-8 rounded-lg">
|
||||
@@ -46,6 +47,7 @@ export const CreateCodeForm = () => {
|
||||
<div className="w-full flex flex-col">
|
||||
<RainbowButton
|
||||
variant="border"
|
||||
disabled={isReadOnly}
|
||||
onClick={() => {
|
||||
if (pubKey) {
|
||||
setDialogOpen(true);
|
||||
@@ -77,6 +79,7 @@ const CreateCodeDialog = ({
|
||||
}) => {
|
||||
const createLink = useLinks(DApp.Governance);
|
||||
const { isReadOnly, pubKey, sendTx } = useVegaWallet();
|
||||
const { refetch } = useReferral({ pubKey, role: 'referrer' });
|
||||
const [err, setErr] = useState<string | null>(null);
|
||||
const [code, setCode] = useState<string | null>(null);
|
||||
const [status, setStatus] = useState<
|
||||
@@ -137,7 +140,10 @@ const CreateCodeDialog = ({
|
||||
return {
|
||||
children: 'Close',
|
||||
intent: Intent.Success,
|
||||
onClick: () => setDialogOpen(false),
|
||||
onClick: () => {
|
||||
refetch();
|
||||
setDialogOpen(false);
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
query Referee($partyId: ID!) {
|
||||
referralSets(referee: $partyId) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
referrer
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
query Referees($code: ID!) {
|
||||
referralSetReferees(id: $code) {
|
||||
query Referees($code: ID!, $aggregationDays: Int) {
|
||||
referralSetReferees(id: $code, aggregationDays: $aggregationDays) {
|
||||
edges {
|
||||
node {
|
||||
referralSetId
|
||||
refereeId
|
||||
joinedAt
|
||||
atEpoch
|
||||
totalRefereeNotionalTakerVolume
|
||||
totalRefereeGeneratedRewards
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
query ReferralFeesStats(
|
||||
$marketId: ID
|
||||
$assetId: ID
|
||||
$partyId: ID
|
||||
$epoch: Int
|
||||
) {
|
||||
feesStats(
|
||||
marketId: $marketId
|
||||
assetId: $assetId
|
||||
partyId: $partyId
|
||||
epoch: $epoch
|
||||
) {
|
||||
marketId
|
||||
assetId
|
||||
epoch
|
||||
totalRewardsPaid {
|
||||
partyId
|
||||
amount
|
||||
}
|
||||
referrerRewardsGenerated {
|
||||
referrerId
|
||||
generatedReward {
|
||||
partyId
|
||||
amount
|
||||
}
|
||||
}
|
||||
refereesDiscountApplied {
|
||||
partyId
|
||||
amount
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,8 @@ query ReferralSetStats($code: ID!, $epoch: Int) {
|
||||
rewardFactor
|
||||
epochNotionalTakerVolume
|
||||
referralSetRunningNotionalTakerVolume
|
||||
rewardsMultiplier
|
||||
rewardsFactorMultiplier
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
query ReferralSets($id: ID, $referrer: ID, $referee: ID) {
|
||||
referralSets(id: $id, referrer: $referrer, referee: $referee) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
referrer
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
query Referrer($partyId: ID!) {
|
||||
referralSets(referrer: $partyId) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
referrer
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
import * as Types from '@vegaprotocol/types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type RefereeQueryVariables = Types.Exact<{
|
||||
partyId: Types.Scalars['ID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type RefereeQuery = { __typename?: 'Query', referralSets: { __typename?: 'ReferralSetConnection', edges: Array<{ __typename?: 'ReferralSetEdge', node: { __typename?: 'ReferralSet', id: string, referrer: string, createdAt: any, updatedAt: any } } | null> } };
|
||||
|
||||
|
||||
export const RefereeDocument = gql`
|
||||
query Referee($partyId: ID!) {
|
||||
referralSets(referee: $partyId) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
referrer
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useRefereeQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useRefereeQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useRefereeQuery` 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 } = useRefereeQuery({
|
||||
* variables: {
|
||||
* partyId: // value for 'partyId'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useRefereeQuery(baseOptions: Apollo.QueryHookOptions<RefereeQuery, RefereeQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<RefereeQuery, RefereeQueryVariables>(RefereeDocument, options);
|
||||
}
|
||||
export function useRefereeLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<RefereeQuery, RefereeQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<RefereeQuery, RefereeQueryVariables>(RefereeDocument, options);
|
||||
}
|
||||
export type RefereeQueryHookResult = ReturnType<typeof useRefereeQuery>;
|
||||
export type RefereeLazyQueryHookResult = ReturnType<typeof useRefereeLazyQuery>;
|
||||
export type RefereeQueryResult = Apollo.QueryResult<RefereeQuery, RefereeQueryVariables>;
|
||||
@@ -5,21 +5,24 @@ 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']>;
|
||||
}>;
|
||||
|
||||
|
||||
export type RefereesQuery = { __typename?: 'Query', referralSetReferees: { __typename?: 'ReferralSetRefereeConnection', edges: Array<{ __typename?: 'ReferralSetRefereeEdge', node: { __typename?: 'ReferralSetReferee', referralSetId: string, refereeId: string, joinedAt: any, atEpoch: number } } | null> } };
|
||||
export type RefereesQuery = { __typename?: 'Query', referralSetReferees: { __typename?: 'ReferralSetRefereeConnection', edges: Array<{ __typename?: 'ReferralSetRefereeEdge', node: { __typename?: 'ReferralSetReferee', referralSetId: string, refereeId: string, joinedAt: any, atEpoch: number, totalRefereeNotionalTakerVolume: string, totalRefereeGeneratedRewards: string } } | null> } };
|
||||
|
||||
|
||||
export const RefereesDocument = gql`
|
||||
query Referees($code: ID!) {
|
||||
referralSetReferees(id: $code) {
|
||||
query Referees($code: ID!, $aggregationDays: Int) {
|
||||
referralSetReferees(id: $code, aggregationDays: $aggregationDays) {
|
||||
edges {
|
||||
node {
|
||||
referralSetId
|
||||
refereeId
|
||||
joinedAt
|
||||
atEpoch
|
||||
totalRefereeNotionalTakerVolume
|
||||
totalRefereeGeneratedRewards
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,6 +42,7 @@ export const RefereesDocument = gql`
|
||||
* const { data, loading, error } = useRefereesQuery({
|
||||
* variables: {
|
||||
* code: // value for 'code'
|
||||
* aggregationDays: // value for 'aggregationDays'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
import * as Types from '@vegaprotocol/types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type ReferralFeesStatsQueryVariables = Types.Exact<{
|
||||
marketId?: Types.InputMaybe<Types.Scalars['ID']>;
|
||||
assetId?: Types.InputMaybe<Types.Scalars['ID']>;
|
||||
partyId?: Types.InputMaybe<Types.Scalars['ID']>;
|
||||
epoch?: Types.InputMaybe<Types.Scalars['Int']>;
|
||||
}>;
|
||||
|
||||
|
||||
export type ReferralFeesStatsQuery = { __typename?: 'Query', feesStats?: { __typename?: 'FeesStats', marketId: string, assetId: string, epoch: number, totalRewardsPaid: Array<{ __typename?: 'PartyAmount', partyId: string, amount: string }>, referrerRewardsGenerated: Array<{ __typename?: 'ReferrerRewardsGenerated', referrerId: string, generatedReward: Array<{ __typename?: 'PartyAmount', partyId: string, amount: string }> }>, refereesDiscountApplied: Array<{ __typename?: 'PartyAmount', partyId: string, amount: string }> } | null };
|
||||
|
||||
|
||||
export const ReferralFeesStatsDocument = gql`
|
||||
query ReferralFeesStats($marketId: ID, $assetId: ID, $partyId: ID, $epoch: Int) {
|
||||
feesStats(
|
||||
marketId: $marketId
|
||||
assetId: $assetId
|
||||
partyId: $partyId
|
||||
epoch: $epoch
|
||||
) {
|
||||
marketId
|
||||
assetId
|
||||
epoch
|
||||
totalRewardsPaid {
|
||||
partyId
|
||||
amount
|
||||
}
|
||||
referrerRewardsGenerated {
|
||||
referrerId
|
||||
generatedReward {
|
||||
partyId
|
||||
amount
|
||||
}
|
||||
}
|
||||
refereesDiscountApplied {
|
||||
partyId
|
||||
amount
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useReferralFeesStatsQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useReferralFeesStatsQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useReferralFeesStatsQuery` 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 } = useReferralFeesStatsQuery({
|
||||
* variables: {
|
||||
* marketId: // value for 'marketId'
|
||||
* assetId: // value for 'assetId'
|
||||
* partyId: // value for 'partyId'
|
||||
* epoch: // value for 'epoch'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useReferralFeesStatsQuery(baseOptions?: Apollo.QueryHookOptions<ReferralFeesStatsQuery, ReferralFeesStatsQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<ReferralFeesStatsQuery, ReferralFeesStatsQueryVariables>(ReferralFeesStatsDocument, options);
|
||||
}
|
||||
export function useReferralFeesStatsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ReferralFeesStatsQuery, ReferralFeesStatsQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<ReferralFeesStatsQuery, ReferralFeesStatsQueryVariables>(ReferralFeesStatsDocument, options);
|
||||
}
|
||||
export type ReferralFeesStatsQueryHookResult = ReturnType<typeof useReferralFeesStatsQuery>;
|
||||
export type ReferralFeesStatsLazyQueryHookResult = ReturnType<typeof useReferralFeesStatsLazyQuery>;
|
||||
export type ReferralFeesStatsQueryResult = Apollo.QueryResult<ReferralFeesStatsQuery, ReferralFeesStatsQueryVariables>;
|
||||
+3
-1
@@ -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 } } | 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 } } | null> } };
|
||||
|
||||
|
||||
export const ReferralSetStatsDocument = gql`
|
||||
@@ -23,6 +23,8 @@ export const ReferralSetStatsDocument = gql`
|
||||
rewardFactor
|
||||
epochNotionalTakerVolume
|
||||
referralSetRunningNotionalTakerVolume
|
||||
rewardsMultiplier
|
||||
rewardsFactorMultiplier
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import * as Types from '@vegaprotocol/types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type ReferralSetsQueryVariables = Types.Exact<{
|
||||
id?: Types.InputMaybe<Types.Scalars['ID']>;
|
||||
referrer?: Types.InputMaybe<Types.Scalars['ID']>;
|
||||
referee?: Types.InputMaybe<Types.Scalars['ID']>;
|
||||
}>;
|
||||
|
||||
|
||||
export type ReferralSetsQuery = { __typename?: 'Query', referralSets: { __typename?: 'ReferralSetConnection', edges: Array<{ __typename?: 'ReferralSetEdge', node: { __typename?: 'ReferralSet', id: string, referrer: string, createdAt: any, updatedAt: any } } | null> } };
|
||||
|
||||
|
||||
export const ReferralSetsDocument = gql`
|
||||
query ReferralSets($id: ID, $referrer: ID, $referee: ID) {
|
||||
referralSets(id: $id, referrer: $referrer, referee: $referee) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
referrer
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useReferralSetsQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useReferralSetsQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useReferralSetsQuery` 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 } = useReferralSetsQuery({
|
||||
* variables: {
|
||||
* id: // value for 'id'
|
||||
* referrer: // value for 'referrer'
|
||||
* referee: // value for 'referee'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useReferralSetsQuery(baseOptions?: Apollo.QueryHookOptions<ReferralSetsQuery, ReferralSetsQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<ReferralSetsQuery, ReferralSetsQueryVariables>(ReferralSetsDocument, options);
|
||||
}
|
||||
export function useReferralSetsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ReferralSetsQuery, ReferralSetsQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<ReferralSetsQuery, ReferralSetsQueryVariables>(ReferralSetsDocument, options);
|
||||
}
|
||||
export type ReferralSetsQueryHookResult = ReturnType<typeof useReferralSetsQuery>;
|
||||
export type ReferralSetsLazyQueryHookResult = ReturnType<typeof useReferralSetsLazyQuery>;
|
||||
export type ReferralSetsQueryResult = Apollo.QueryResult<ReferralSetsQuery, ReferralSetsQueryVariables>;
|
||||
@@ -1,55 +0,0 @@
|
||||
import * as Types from '@vegaprotocol/types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type ReferrerQueryVariables = Types.Exact<{
|
||||
partyId: Types.Scalars['ID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type ReferrerQuery = { __typename?: 'Query', referralSets: { __typename?: 'ReferralSetConnection', edges: Array<{ __typename?: 'ReferralSetEdge', node: { __typename?: 'ReferralSet', id: string, referrer: string, createdAt: any, updatedAt: any } } | null> } };
|
||||
|
||||
|
||||
export const ReferrerDocument = gql`
|
||||
query Referrer($partyId: ID!) {
|
||||
referralSets(referrer: $partyId) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
referrer
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useReferrerQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useReferrerQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useReferrerQuery` 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 } = useReferrerQuery({
|
||||
* variables: {
|
||||
* partyId: // value for 'partyId'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useReferrerQuery(baseOptions: Apollo.QueryHookOptions<ReferrerQuery, ReferrerQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<ReferrerQuery, ReferrerQueryVariables>(ReferrerDocument, options);
|
||||
}
|
||||
export function useReferrerLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ReferrerQuery, ReferrerQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<ReferrerQuery, ReferrerQueryVariables>(ReferrerDocument, options);
|
||||
}
|
||||
export type ReferrerQueryHookResult = ReturnType<typeof useReferrerQuery>;
|
||||
export type ReferrerLazyQueryHookResult = ReturnType<typeof useReferrerLazyQuery>;
|
||||
export type ReferrerQueryResult = Apollo.QueryResult<ReferrerQuery, ReferrerQueryVariables>;
|
||||
@@ -1,12 +1,8 @@
|
||||
import { useQuery } from '@apollo/client';
|
||||
import { getNumberFormat } from '@vegaprotocol/utils';
|
||||
import { addDays } from 'date-fns';
|
||||
import sortBy from 'lodash/sortBy';
|
||||
import omit from 'lodash/omit';
|
||||
import {
|
||||
ReferralProgramDocument,
|
||||
useReferralProgramQuery,
|
||||
} from './__generated__/CurrentReferralProgram';
|
||||
import { useReferralProgramQuery } from './__generated__/CurrentReferralProgram';
|
||||
|
||||
const STAKING_TIERS_MAPPING: Record<number, string> = {
|
||||
1: 'Tradestarter',
|
||||
|
||||
@@ -1,47 +1,66 @@
|
||||
import { useQuery } from '@apollo/client';
|
||||
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
||||
import { useCallback } from 'react';
|
||||
import { ReferrerDocument } from './__generated__/Referrer';
|
||||
import { RefereeDocument } from './__generated__/Referee';
|
||||
import { useRefereesQuery } from './__generated__/Referees';
|
||||
import compact from 'lodash/compact';
|
||||
import type { ReferralSetsQueryVariables } from './__generated__/ReferralSets';
|
||||
import { useReferralSetsQuery } from './__generated__/ReferralSets';
|
||||
|
||||
const DEFAULT_AGGREGATION_DAYS = 30;
|
||||
|
||||
export type Role = 'referrer' | 'referee';
|
||||
export type ReferralData = {
|
||||
pubKey: string;
|
||||
role: Role;
|
||||
code: string;
|
||||
createdAt: string;
|
||||
referees: Array<{
|
||||
refereeId: string;
|
||||
joinedAt: string;
|
||||
atEpoch: number;
|
||||
}>;
|
||||
type UseReferralArgs = (
|
||||
| { code: string }
|
||||
| { pubKey: string | null; role: Role }
|
||||
) & {
|
||||
aggregationDays?: number;
|
||||
};
|
||||
|
||||
export const useReferral = (pubKey: string | null, role: Role) => {
|
||||
const query = {
|
||||
referrer: ReferrerDocument,
|
||||
referee: RefereeDocument,
|
||||
};
|
||||
const prepareVariables = (
|
||||
args: UseReferralArgs
|
||||
): [ReferralSetsQueryVariables, boolean] => {
|
||||
const byCode = 'code' in args;
|
||||
const byRole = 'pubKey' in args && 'role' in args;
|
||||
let variables = {};
|
||||
let skip = true;
|
||||
if (byCode) {
|
||||
variables = {
|
||||
id: args.code,
|
||||
};
|
||||
skip = !args.code;
|
||||
}
|
||||
if (byRole) {
|
||||
if (args.role === 'referee') {
|
||||
variables = { referee: args.pubKey };
|
||||
}
|
||||
if (args.role === 'referrer') {
|
||||
variables = { referrer: args.pubKey };
|
||||
}
|
||||
skip = !args.pubKey;
|
||||
}
|
||||
|
||||
return [variables, skip];
|
||||
};
|
||||
|
||||
export const useReferral = (args: UseReferralArgs) => {
|
||||
const [variables, skip] = prepareVariables(args);
|
||||
|
||||
const {
|
||||
data: referralData,
|
||||
loading: referralLoading,
|
||||
error: referralError,
|
||||
refetch: referralRefetch,
|
||||
} = useQuery(query[role], {
|
||||
variables: {
|
||||
partyId: pubKey,
|
||||
},
|
||||
skip: !pubKey,
|
||||
} = useReferralSetsQuery({
|
||||
variables,
|
||||
skip,
|
||||
fetchPolicy: 'cache-and-network',
|
||||
});
|
||||
|
||||
// A user can only have 1 active referral program at a time
|
||||
const referral = referralData?.referralSets.edges.length
|
||||
? referralData.referralSets.edges[0].node
|
||||
: undefined;
|
||||
const referralSet =
|
||||
referralData?.referralSets.edges &&
|
||||
referralData.referralSets.edges.length > 0
|
||||
? referralData.referralSets.edges[0]?.node
|
||||
: undefined;
|
||||
|
||||
const {
|
||||
data: refereesData,
|
||||
@@ -50,9 +69,13 @@ export const useReferral = (pubKey: string | null, role: Role) => {
|
||||
refetch: refereesRefetch,
|
||||
} = useRefereesQuery({
|
||||
variables: {
|
||||
code: referral?.id,
|
||||
code: referralSet?.id as string,
|
||||
aggregationDays:
|
||||
args.aggregationDays != null
|
||||
? args.aggregationDays
|
||||
: DEFAULT_AGGREGATION_DAYS,
|
||||
},
|
||||
skip: !referral?.id,
|
||||
skip: !referralSet?.id,
|
||||
fetchPolicy: 'cache-and-network',
|
||||
});
|
||||
|
||||
@@ -65,19 +88,26 @@ export const useReferral = (pubKey: string | null, role: Role) => {
|
||||
refereesRefetch();
|
||||
}, [refereesRefetch, referralRefetch]);
|
||||
|
||||
const byReferee =
|
||||
'role' in args && 'pubKey' in args && args.role === 'referee';
|
||||
const referee = byReferee
|
||||
? referees.find((r) => r.refereeId === args.pubKey) || null
|
||||
: null;
|
||||
|
||||
const data =
|
||||
referral && refereesData
|
||||
referralSet && refereesData
|
||||
? {
|
||||
pubKey,
|
||||
role,
|
||||
code: referral.id,
|
||||
createdAt: referral.createdAt,
|
||||
code: referralSet.id,
|
||||
role: 'role' in args ? args.role : null,
|
||||
referee: referee,
|
||||
referrerId: referralSet.referrer,
|
||||
createdAt: referralSet.createdAt,
|
||||
referees,
|
||||
}
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
data: data as ReferralData | undefined,
|
||||
data,
|
||||
loading: referralLoading || refereesLoading,
|
||||
error: referralError || refereesError,
|
||||
refetch,
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import type { ReferralData } from './hooks/use-referral';
|
||||
import { useReferral } from './hooks/use-referral';
|
||||
import { CreateCodeContainer } from './create-code-form';
|
||||
import classNames from 'classnames';
|
||||
@@ -23,16 +22,22 @@ import { useReferralSetStatsQuery } from './hooks/__generated__/ReferralSetStats
|
||||
import compact from 'lodash/compact';
|
||||
import { useReferralProgram } from './hooks/use-referral-program';
|
||||
import { useStakeAvailable } from './hooks/use-stake-available';
|
||||
import minBy from 'lodash/minBy';
|
||||
import sortBy from 'lodash/sortBy';
|
||||
import { useLayoutEffect, useRef, useState } from 'react';
|
||||
import { useCurrentEpochInfoQuery } from './hooks/__generated__/Epoch';
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
export const ReferralStatistics = () => {
|
||||
const { pubKey } = useVegaWallet();
|
||||
|
||||
const { data: referee } = useReferral(pubKey, 'referee');
|
||||
const { data: referrer } = useReferral(pubKey, 'referrer');
|
||||
const { data: referee } = useReferral({
|
||||
pubKey,
|
||||
role: 'referee',
|
||||
});
|
||||
const { data: referrer } = useReferral({
|
||||
pubKey,
|
||||
role: 'referrer',
|
||||
});
|
||||
|
||||
if (referee?.code) {
|
||||
return <Statistics data={referee} as="referee" />;
|
||||
@@ -45,16 +50,16 @@ export const ReferralStatistics = () => {
|
||||
return <CreateCodeContainer />;
|
||||
};
|
||||
|
||||
const Statistics = ({
|
||||
export const Statistics = ({
|
||||
data,
|
||||
as,
|
||||
}: {
|
||||
data: ReferralData;
|
||||
data: NonNullable<ReturnType<typeof useReferral>['data']>;
|
||||
as: 'referrer' | 'referee';
|
||||
}) => {
|
||||
const { data: epochData } = useCurrentEpochInfoQuery();
|
||||
const { stakeAvailable } = useStakeAvailable();
|
||||
const { stakingTiers, benefitTiers } = useReferralProgram();
|
||||
const { benefitTiers } = useReferralProgram();
|
||||
const { data: statsData } = useReferralSetStatsQuery({
|
||||
variables: {
|
||||
code: data.code,
|
||||
@@ -68,31 +73,24 @@ const Statistics = ({
|
||||
const stats =
|
||||
statsData?.referralSetStats.edges &&
|
||||
compact(removePaginationWrapper(statsData.referralSetStats.edges));
|
||||
const refereeInfo = data.referees.find((r) => r.refereeId === data.pubKey);
|
||||
const refereeInfo = data.referee;
|
||||
const refereeStats = stats?.find(
|
||||
(r) => r.partyId === data.referee?.refereeId
|
||||
);
|
||||
|
||||
const refereeStats = stats?.find((r) => r.partyId === data.pubKey);
|
||||
|
||||
// console.table(stats);
|
||||
|
||||
const baseCommissionValue =
|
||||
stats && stats.length > 0 ? Number(stats[0].rewardFactor) : 0;
|
||||
const runningVolumeValue =
|
||||
stats && stats.length > 0
|
||||
? Number(stats[0].referralSetRunningNotionalTakerVolume)
|
||||
: 0;
|
||||
const stakingTier =
|
||||
stakingTiers &&
|
||||
stakeAvailable &&
|
||||
minBy(
|
||||
stakingTiers.filter(
|
||||
(st) => BigInt(st.minimumStakedTokens) <= stakeAvailable
|
||||
),
|
||||
(st) => BigInt(st.minimumStakedTokens)
|
||||
)?.tier;
|
||||
const finalCommissionValue =
|
||||
!stakingTier || isNaN(stakingTier)
|
||||
? baseCommissionValue
|
||||
: stakingTier * baseCommissionValue;
|
||||
const statsAvailable = stats && stats.length > 0 && stats[0];
|
||||
const baseCommissionValue = statsAvailable
|
||||
? Number(statsAvailable.rewardFactor)
|
||||
: 0;
|
||||
const runningVolumeValue = statsAvailable
|
||||
? Number(statsAvailable.referralSetRunningNotionalTakerVolume)
|
||||
: 0;
|
||||
const multiplier = statsAvailable
|
||||
? Number(statsAvailable.rewardsMultiplier)
|
||||
: 1;
|
||||
const finalCommissionValue = !isNaN(multiplier)
|
||||
? baseCommissionValue
|
||||
: multiplier * baseCommissionValue;
|
||||
|
||||
const discountFactorValue = refereeStats?.discountFactor
|
||||
? Number(refereeStats.discountFactor)
|
||||
@@ -130,7 +128,7 @@ const Statistics = ({
|
||||
18
|
||||
)} $VEGA staked)`}
|
||||
>
|
||||
{stakingTier || 'None'}
|
||||
{multiplier || 'None'}
|
||||
</StatTile>
|
||||
);
|
||||
const finalCommissionTile = (
|
||||
@@ -152,9 +150,12 @@ const Statistics = ({
|
||||
</StatTile>
|
||||
);
|
||||
|
||||
const totalCommissionValue = data.referees
|
||||
.map((r) => new BigNumber(r.totalRefereeGeneratedRewards))
|
||||
.reduce((all, r) => all.plus(r), new BigNumber(0));
|
||||
const totalCommissionTile = (
|
||||
<StatTile title="Running total commission" description="(Quantum)">
|
||||
...
|
||||
<StatTile title="Total commission (last 30 days)" description="(Quantum)">
|
||||
{getNumberFormat(0).format(Number(totalCommissionValue))}
|
||||
</StatTile>
|
||||
);
|
||||
|
||||
@@ -274,23 +275,29 @@ const Statistics = ({
|
||||
columns={[
|
||||
{ name: 'party', displayName: 'Trader' },
|
||||
{ name: 'joined', displayName: 'Date Joined' },
|
||||
{ name: 'volume', displayName: 'Volume' },
|
||||
{ name: 'volume', displayName: 'Volume (last 30 days)' },
|
||||
{
|
||||
name: 'commission',
|
||||
displayName: 'Commission earned (last 30 days)',
|
||||
},
|
||||
]}
|
||||
data={sortBy(
|
||||
data.referees.map((r) => ({
|
||||
party: <span>{truncateMiddle(r.refereeId)}</span>,
|
||||
party: (
|
||||
<span title={r.refereeId}>
|
||||
{truncateMiddle(r.refereeId)}
|
||||
</span>
|
||||
),
|
||||
joined: getDateTimeFormat().format(new Date(r.joinedAt)),
|
||||
volume:
|
||||
Number(
|
||||
stats?.find((s) => s.partyId === r.refereeId)
|
||||
?.epochNotionalTakerVolume
|
||||
) || 0,
|
||||
volume: Number(r.totalRefereeNotionalTakerVolume),
|
||||
commission: Number(r.totalRefereeGeneratedRewards),
|
||||
})),
|
||||
(r) => r.volume
|
||||
)
|
||||
.map((r) => ({
|
||||
...r,
|
||||
volume: getNumberFormat(0).format(r.volume),
|
||||
commission: getNumberFormat(0).format(r.commission),
|
||||
}))
|
||||
.reverse()}
|
||||
/>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
Loader,
|
||||
TradingAnchorButton,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
@@ -25,18 +26,37 @@ const Nav = () => (
|
||||
|
||||
export const Referrals = () => {
|
||||
const { pubKey } = useVegaWallet();
|
||||
const { data: referee } = useReferral(pubKey, 'referee');
|
||||
const { data: referrer } = useReferral(pubKey, 'referrer');
|
||||
|
||||
const showNav = !referrer && !referee;
|
||||
const { data: referee, loading: refereeLoading } = useReferral({
|
||||
pubKey,
|
||||
role: 'referee',
|
||||
});
|
||||
const { data: referrer, loading: referrerLoading } = useReferral({
|
||||
pubKey,
|
||||
role: 'referrer',
|
||||
});
|
||||
|
||||
const loading = refereeLoading || referrerLoading;
|
||||
const showNav = !loading && !referrer && !referee;
|
||||
|
||||
return (
|
||||
<>
|
||||
<LandingBanner />
|
||||
|
||||
{showNav && <Nav />}
|
||||
<div className={classNames({ 'py-16': showNav })}>
|
||||
<Outlet />
|
||||
<div
|
||||
className={classNames({
|
||||
'py-16': showNav,
|
||||
'h-[300px] relative': loading,
|
||||
})}
|
||||
>
|
||||
{loading ? (
|
||||
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2">
|
||||
<Loader />
|
||||
</div>
|
||||
) : (
|
||||
<Outlet />
|
||||
)}
|
||||
</div>
|
||||
|
||||
<TiersContainer />
|
||||
|
||||
@@ -5,6 +5,8 @@ import classNames from 'classnames';
|
||||
import { BORDER_COLOR, GRADIENT } from './constants';
|
||||
import { Tag } from './tag';
|
||||
import type { ComponentProps, ReactNode } from 'react';
|
||||
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
import { DApp, TOKEN_PROPOSALS, useLinks } from '@vegaprotocol/environment';
|
||||
|
||||
const Loading = ({ variant }: { variant: 'large' | 'inline' }) => (
|
||||
<div
|
||||
@@ -67,12 +69,26 @@ const StakingTier = ({
|
||||
};
|
||||
|
||||
export const TiersContainer = () => {
|
||||
const { benefitTiers, stakingTiers, details, loading } = useReferralProgram();
|
||||
const { benefitTiers, stakingTiers, details, loading, error } =
|
||||
useReferralProgram();
|
||||
|
||||
const ends = details?.endOfProgramTimestamp
|
||||
? getDateTimeFormat().format(new Date(details.endOfProgramTimestamp))
|
||||
: undefined;
|
||||
|
||||
const governanceLink = useLinks(DApp.Governance);
|
||||
|
||||
if ((!loading && !details) || error) {
|
||||
return (
|
||||
<div className="text-base px-5 py-10 text-center">
|
||||
We're sorry but we don't have an active referral programme
|
||||
currently running. You can propose a new programme{' '}
|
||||
<ExternalLink href={governanceLink(TOKEN_PROPOSALS)}>here</ExternalLink>
|
||||
.
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Benefit tiers */}
|
||||
|
||||
Generated
+65
-9
@@ -510,7 +510,7 @@ export type CurrentReferralProgram = {
|
||||
__typename?: 'CurrentReferralProgram';
|
||||
/** Defined tiers in increasing order. First element will give Tier 1, second element will give Tier 2, etc. */
|
||||
benefitTiers: Array<BenefitTier>;
|
||||
/** Timestamp as RFC3339Nano, after which when the current epoch ends, the program will end and benefits will be disabled. */
|
||||
/** Timestamp as Unix time in nanoseconds, after which when the current epoch ends, the program will end and benefits will be disabled. */
|
||||
endOfProgramTimestamp: Scalars['Timestamp'];
|
||||
/** Timestamp as RFC3339Nano when the program ended. If present, the current program has ended and no program is currently running. */
|
||||
endedAt?: Maybe<Scalars['Timestamp']>;
|
||||
@@ -1279,12 +1279,16 @@ export type FeesStats = {
|
||||
assetId: Scalars['String'];
|
||||
/** The epoch for which these stats were valid. */
|
||||
epoch: Scalars['Int'];
|
||||
/** The total maker fees generated by all parties. */
|
||||
makerFeesGenerated: Array<MakerFeesGenerated>;
|
||||
/** The market the fees were paid in */
|
||||
marketId: Scalars['String'];
|
||||
/** The total referral discounts applied to all referee taker fees */
|
||||
refereesDiscountApplied: Array<PartyAmount>;
|
||||
/** The total referral rewards generated by all referee taker fees. */
|
||||
referrerRewardsGenerated: Array<ReferrerRewardsGenerated>;
|
||||
/** The total maker fees received by the maker side. */
|
||||
totalMakeFeesReceived: Array<PartyAmount>;
|
||||
/** The total referral rewards paid to the referrer of the referral set. */
|
||||
totalRewardsPaid: Array<PartyAmount>;
|
||||
/** The total volume discounts applied to all referee taker fees */
|
||||
@@ -1880,6 +1884,15 @@ export type LossSocialization = {
|
||||
partyId: Scalars['ID'];
|
||||
};
|
||||
|
||||
/** Maker fees generated by the trade aggressor */
|
||||
export type MakerFeesGenerated = {
|
||||
__typename?: 'MakerFeesGenerated';
|
||||
/** Amount of maker fees paid by the taker to the maker */
|
||||
makerFeesPaid: Array<PartyAmount>;
|
||||
/** Party that paid the fees */
|
||||
taker: Scalars['String'];
|
||||
};
|
||||
|
||||
export type MarginCalculator = {
|
||||
__typename?: 'MarginCalculator';
|
||||
/** The scaling factors that will be used for margin calculation */
|
||||
@@ -3238,6 +3251,39 @@ export type Pagination = {
|
||||
last?: InputMaybe<Scalars['Int']>;
|
||||
};
|
||||
|
||||
/** Liquidity fees that have been paid to a party in a specific market/asset up to the given epoch. */
|
||||
export type PaidLiquidityFees = {
|
||||
__typename?: 'PaidLiquidityFees';
|
||||
/** The settlement asset of the market. */
|
||||
assetId: Scalars['String'];
|
||||
/** The epoch for which these stats were valid. */
|
||||
epoch: Scalars['Int'];
|
||||
/** Fees paid per party */
|
||||
feesPaidPerParty: Array<PartyAmount>;
|
||||
/** The market the fees were paid in */
|
||||
marketId: Scalars['String'];
|
||||
/** Total fees paid across all parties */
|
||||
totalFeesPaid: Scalars['String'];
|
||||
};
|
||||
|
||||
/** Connection type for retrieving cursor-based paginated paid liquidity fees statistics */
|
||||
export type PaidLiquidityFeesConnection = {
|
||||
__typename?: 'PaidLiquidityFeesConnection';
|
||||
/** The volume discount statistics in this connection */
|
||||
edges: Array<Maybe<PaidLiquidityFeesEdge>>;
|
||||
/** The pagination information */
|
||||
pageInfo: PageInfo;
|
||||
};
|
||||
|
||||
/** Edge type containing the volume discount statistics and cursor information returned by a PaidLiquidityFeesConnection */
|
||||
export type PaidLiquidityFeesEdge = {
|
||||
__typename?: 'PaidLiquidityFeesEdge';
|
||||
/** The cursor for this volume discount statistics */
|
||||
cursor: Scalars['String'];
|
||||
/** The volume discount statistics */
|
||||
node: PaidLiquidityFees;
|
||||
};
|
||||
|
||||
/** Represents a party on Vega, could be an ethereum wallet address in the future */
|
||||
export type Party = {
|
||||
__typename?: 'Party';
|
||||
@@ -4237,6 +4283,8 @@ export type Query = {
|
||||
orderByReference: Order;
|
||||
/** Order versions (created via amendments if any) found by orderID */
|
||||
orderVersionsConnection?: Maybe<OrderConnection>;
|
||||
/** List paid liquidity fees statistics */
|
||||
paidLiquidityFees?: Maybe<PaidLiquidityFeesConnection>;
|
||||
/** One or more entities that are trading on the Vega network */
|
||||
partiesConnection?: Maybe<PartyConnection>;
|
||||
/** An entity that is trading on the Vega network */
|
||||
@@ -4434,8 +4482,7 @@ export type QueryfeesStatsArgs = {
|
||||
assetId?: InputMaybe<Scalars['ID']>;
|
||||
epoch?: InputMaybe<Scalars['Int']>;
|
||||
marketId?: InputMaybe<Scalars['ID']>;
|
||||
referee?: InputMaybe<Scalars['ID']>;
|
||||
referrer?: InputMaybe<Scalars['ID']>;
|
||||
partyId?: InputMaybe<Scalars['ID']>;
|
||||
};
|
||||
|
||||
|
||||
@@ -4586,6 +4633,15 @@ export type QueryorderVersionsConnectionArgs = {
|
||||
};
|
||||
|
||||
|
||||
/** Queries allow a caller to read data and filter data via GraphQL. */
|
||||
export type QuerypaidLiquidityFeesArgs = {
|
||||
assetId?: InputMaybe<Scalars['ID']>;
|
||||
epoch?: InputMaybe<Scalars['Int']>;
|
||||
marketId?: InputMaybe<Scalars['ID']>;
|
||||
partyIDs?: InputMaybe<Array<Scalars['String']>>;
|
||||
};
|
||||
|
||||
|
||||
/** Queries allow a caller to read data and filter data via GraphQL. */
|
||||
export type QuerypartiesConnectionArgs = {
|
||||
id?: InputMaybe<Scalars['ID']>;
|
||||
@@ -4793,8 +4849,8 @@ export type ReferralProgram = {
|
||||
__typename?: 'ReferralProgram';
|
||||
/** Defined tiers in increasing order. First element will give Tier 1, second element will give Tier 2, etc. */
|
||||
benefitTiers: Array<BenefitTier>;
|
||||
/** Timestamp as RFC3339, after which when the current epoch ends, the programs will end and benefits will be disabled. */
|
||||
endOfProgramTimestamp: Scalars['String'];
|
||||
/** Timestamp as Unix time in nanoseconds, after which when the current epoch ends, the program will end and benefits will be disabled. */
|
||||
endOfProgramTimestamp: Scalars['Timestamp'];
|
||||
/** Unique ID generated from the proposal that created this program. */
|
||||
id: Scalars['ID'];
|
||||
/**
|
||||
@@ -6156,8 +6212,8 @@ export type UpdateReferralProgram = {
|
||||
__typename?: 'UpdateReferralProgram';
|
||||
/** Defined tiers in increasing order. First element will give Tier 1, second element will give Tier 2, etc. */
|
||||
benefitTiers: Array<BenefitTier>;
|
||||
/** Timestamp as RFC3339, after which when the current epoch ends, the programs will end and benefits will be disabled. */
|
||||
endOfProgramTimestamp: Scalars['String'];
|
||||
/** Timestamp as Unix time in nanoseconds, after which when the current epoch ends, the program will end and benefits will be disabled. */
|
||||
endOfProgramTimestamp: Scalars['Timestamp'];
|
||||
/**
|
||||
* Defined staking tiers in increasing order. First element will give Tier 1,
|
||||
* second element will give Tier 2, and so on. Determines the level of
|
||||
@@ -6195,7 +6251,7 @@ export type UpdateVolumeDiscountProgram = {
|
||||
__typename?: 'UpdateVolumeDiscountProgram';
|
||||
/** The benefit tiers for the program */
|
||||
benefitTiers: Array<VolumeBenefitTier>;
|
||||
/** The end time of the program */
|
||||
/** Timestamp as Unix time in nanoseconds, after which program ends. */
|
||||
endOfProgramTimestamp: Scalars['Timestamp'];
|
||||
/** The window length to consider for the volume discount program */
|
||||
windowLength: Scalars['Int'];
|
||||
@@ -6224,7 +6280,7 @@ export type VolumeDiscountProgram = {
|
||||
__typename?: 'VolumeDiscountProgram';
|
||||
/** Defined tiers in increasing order. First element will give Tier 1, second element will give Tier 2, etc. */
|
||||
benefitTiers: Array<VolumeBenefitTier>;
|
||||
/** Timestamp as Unix time in nanoseconds, after which when the current epoch ends, the programs will end and benefits will be disabled. */
|
||||
/** Timestamp as Unix time in nanoseconds, after which when the current epoch ends, the program will end and benefits will be disabled. */
|
||||
endOfProgramTimestamp: Scalars['Timestamp'];
|
||||
/** Timestamp as RFC3339Nano when the program ended. If present, the current program has ended and no program is currently running. */
|
||||
endedAt?: Maybe<Scalars['Timestamp']>;
|
||||
|
||||
Reference in New Issue
Block a user