feat: preview, ux changes

This commit is contained in:
asiaznik
2023-10-23 13:56:45 +02:00
parent 6047178547
commit 2b4736c585
22 changed files with 497 additions and 274 deletions
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'
* },
* });
*/
@@ -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>;
@@ -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 />
+17 -1
View File
@@ -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&apos;re sorry but we don&apos;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 */}
+65 -9
View File
@@ -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']>;