Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e322078e10 | ||
|
|
42b1d28f02 | ||
|
|
87a103ce56 | ||
|
|
a8c4b32f39 | ||
|
|
345a332afc | ||
|
|
4a2faa9fce | ||
|
|
445e1cd445 | ||
|
|
22a0ce4979 | ||
|
|
ab99bde47f | ||
|
|
c72b9f22f3 | ||
|
|
da9d477980 |
@@ -26,6 +26,7 @@ NX_ICEBERG_ORDERS=true
|
||||
NX_METAMASK_SNAPS=true
|
||||
NX_REFERRALS=true
|
||||
# NX_DISABLE_CLOSE_POSITION=false
|
||||
NX_TEAM_COMPETITION=true
|
||||
|
||||
NX_TENDERMINT_URL=https://be.vega.community
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
|
||||
|
||||
@@ -28,3 +28,8 @@ NX_REFERRALS=true
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.be.devnet1.vega.xyz/
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.devnet1.vega.xyz/websocket
|
||||
|
||||
NX_TEAM_COMPETITION=true
|
||||
|
||||
NX_CHARTING_LIBRARY_PATH=https://assets.vega.community/trading-view-bundle/v0.0.1/
|
||||
NX_CHARTING_LIBRARY_HASH=PDjWaqPFndDp+LCvqbKvntWriaqNzNpZ5i9R/BULzCg=
|
||||
|
||||
@@ -26,6 +26,7 @@ NX_ICEBERG_ORDERS=true
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
NX_METAMASK_SNAPS=true
|
||||
NX_REFERRALS=true
|
||||
NX_TEAM_COMPETITION=true
|
||||
|
||||
NX_CHARTING_LIBRARY_PATH=https://assets.vega.community/trading-view-bundle/v0.0.1/
|
||||
NX_CHARTING_LIBRARY_HASH=PDjWaqPFndDp+LCvqbKvntWriaqNzNpZ5i9R/BULzCg=
|
||||
|
||||
@@ -26,6 +26,7 @@ NX_ISOLATED_MARGIN=true
|
||||
NX_ICEBERG_ORDERS=true
|
||||
NX_METAMASK_SNAPS=true
|
||||
NX_REFERRALS=true
|
||||
NX_TEAM_COMPETITION=true
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
|
||||
|
||||
@@ -1,28 +1,9 @@
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import {
|
||||
Intent,
|
||||
TextArea,
|
||||
TradingAnchorButton,
|
||||
TradingButton,
|
||||
TradingCheckbox,
|
||||
TradingFormGroup,
|
||||
TradingInput,
|
||||
TradingInputError,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
useVegaWallet,
|
||||
type CreateReferralSet,
|
||||
type Status,
|
||||
useVegaWalletDialogStore,
|
||||
} from '@vegaprotocol/wallet';
|
||||
import {
|
||||
addDecimalsFormatNumber,
|
||||
isValidVegaPublicKey,
|
||||
URL_REGEX,
|
||||
} from '@vegaprotocol/utils';
|
||||
import { Intent, TradingAnchorButton } from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
|
||||
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
|
||||
import { useT } from '../../lib/use-t';
|
||||
import { useCreateReferralSet } from '../../lib/hooks/use-create-referral-set';
|
||||
import { useReferralSetTransaction } from '../../lib/hooks/use-referral-set-transaction';
|
||||
import { DApp, TokenStaticLinks, useLinks } from '@vegaprotocol/environment';
|
||||
import { RainbowButton } from '../../components/rainbow-button';
|
||||
import { usePageTitle } from '../../lib/hooks/use-page-title';
|
||||
@@ -30,14 +11,7 @@ import { ErrorBoundary } from '../../components/error-boundary';
|
||||
import { Box } from '../../components/competitions/box';
|
||||
import { LayoutWithGradient } from '../../components/layouts-inner';
|
||||
import { Links } from '../../lib/links';
|
||||
|
||||
interface FormFields {
|
||||
name: string;
|
||||
url: string;
|
||||
avatarUrl: string;
|
||||
private: boolean;
|
||||
allowList: string;
|
||||
}
|
||||
import { TeamForm, TransactionType } from './team-form';
|
||||
|
||||
export const CompetitionsCreateTeam = () => {
|
||||
const [searchParams] = useSearchParams();
|
||||
@@ -85,7 +59,7 @@ const CreateTeamFormContainer = ({ isSolo }: { isSolo: boolean }) => {
|
||||
const createLink = useLinks(DApp.Governance);
|
||||
|
||||
const { err, status, code, isEligible, requiredStake, onSubmit } =
|
||||
useCreateReferralSet({
|
||||
useReferralSetTransaction({
|
||||
onSuccess: (code) => {
|
||||
// For some reason team creation takes a long time, too long even to make
|
||||
// polling viable, so its not feasible to navigate to the team page
|
||||
@@ -146,7 +120,8 @@ const CreateTeamFormContainer = ({ isSolo }: { isSolo: boolean }) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<CreateTeamForm
|
||||
<TeamForm
|
||||
type={TransactionType.CreateReferralSet}
|
||||
onSubmit={onSubmit}
|
||||
status={status}
|
||||
err={err}
|
||||
@@ -154,180 +129,3 @@ const CreateTeamFormContainer = ({ isSolo }: { isSolo: boolean }) => {
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const CreateTeamForm = ({
|
||||
status,
|
||||
err,
|
||||
isSolo,
|
||||
onSubmit,
|
||||
}: {
|
||||
status: ReturnType<typeof useCreateReferralSet>['status'];
|
||||
err: ReturnType<typeof useCreateReferralSet>['err'];
|
||||
isSolo: boolean;
|
||||
onSubmit: (tx: CreateReferralSet) => void;
|
||||
}) => {
|
||||
const t = useT();
|
||||
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
control,
|
||||
watch,
|
||||
formState: { errors },
|
||||
} = useForm<FormFields>({
|
||||
defaultValues: {
|
||||
private: isSolo,
|
||||
},
|
||||
});
|
||||
|
||||
const isPrivate = watch('private');
|
||||
|
||||
const createTeam = (fields: FormFields) => {
|
||||
onSubmit({
|
||||
createReferralSet: {
|
||||
isTeam: true,
|
||||
team: {
|
||||
name: fields.name,
|
||||
teamUrl: fields.url,
|
||||
avatarUrl: fields.avatarUrl,
|
||||
closed: fields.private,
|
||||
allowList: fields.private ? parseAllowListText(fields.allowList) : [],
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(createTeam)}>
|
||||
<TradingFormGroup label={t('Team name')} labelFor="name">
|
||||
<TradingInput {...register('name', { required: t('Required') })} />
|
||||
{errors.name?.message && (
|
||||
<TradingInputError forInput="name">
|
||||
{errors.name.message}
|
||||
</TradingInputError>
|
||||
)}
|
||||
</TradingFormGroup>
|
||||
<TradingFormGroup
|
||||
label={t('URL')}
|
||||
labelFor="url"
|
||||
labelDescription={t(
|
||||
'Provide a link so users can learn more about your team'
|
||||
)}
|
||||
>
|
||||
<TradingInput
|
||||
{...register('url', {
|
||||
pattern: { value: URL_REGEX, message: t('Invalid URL') },
|
||||
})}
|
||||
/>
|
||||
{errors.url?.message && (
|
||||
<TradingInputError forInput="url">
|
||||
{errors.url.message}
|
||||
</TradingInputError>
|
||||
)}
|
||||
</TradingFormGroup>
|
||||
<TradingFormGroup
|
||||
label={t('Avatar URL')}
|
||||
labelFor="avatarUrl"
|
||||
labelDescription={t('Provide a URL to a hosted image')}
|
||||
>
|
||||
<TradingInput
|
||||
{...register('avatarUrl', {
|
||||
pattern: {
|
||||
value: URL_REGEX,
|
||||
message: t('Invalid image URL'),
|
||||
},
|
||||
})}
|
||||
/>
|
||||
{errors.avatarUrl?.message && (
|
||||
<TradingInputError forInput="avatarUrl">
|
||||
{errors.avatarUrl.message}
|
||||
</TradingInputError>
|
||||
)}
|
||||
</TradingFormGroup>
|
||||
<TradingFormGroup
|
||||
label={t('Make team private')}
|
||||
labelFor="private"
|
||||
hideLabel={true}
|
||||
>
|
||||
<Controller
|
||||
name="private"
|
||||
control={control}
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<TradingCheckbox
|
||||
label={t('Make team private')}
|
||||
checked={field.value}
|
||||
onCheckedChange={(value) => {
|
||||
field.onChange(value);
|
||||
}}
|
||||
disabled={isSolo}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</TradingFormGroup>
|
||||
{isPrivate && (
|
||||
<TradingFormGroup
|
||||
label={t('Public key allow list')}
|
||||
labelFor="allowList"
|
||||
labelDescription={t(
|
||||
'Use a comma separated list to allow only specific public keys to join the team'
|
||||
)}
|
||||
>
|
||||
<TextArea
|
||||
{...register('allowList', {
|
||||
required: t('Required'),
|
||||
disabled: isSolo,
|
||||
validate: {
|
||||
allowList: (value) => {
|
||||
const publicKeys = parseAllowListText(value);
|
||||
if (publicKeys.every((pk) => isValidVegaPublicKey(pk))) {
|
||||
return true;
|
||||
}
|
||||
return t('Invalid public key found in allow list');
|
||||
},
|
||||
},
|
||||
})}
|
||||
/>
|
||||
{errors.allowList?.message && (
|
||||
<TradingInputError forInput="avatarUrl">
|
||||
{errors.allowList.message}
|
||||
</TradingInputError>
|
||||
)}
|
||||
</TradingFormGroup>
|
||||
)}
|
||||
{err && (
|
||||
<p className="text-danger text-xs mb-4 first-letter:capitalize">
|
||||
{err}
|
||||
</p>
|
||||
)}
|
||||
<SubmitButton status={status} />
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
const SubmitButton = ({ status }: { status: Status }) => {
|
||||
const t = useT();
|
||||
const disabled = status === 'pending' || status === 'requested';
|
||||
|
||||
let text = t('Create');
|
||||
|
||||
if (status === 'requested') {
|
||||
text = t('Confirm in wallet...');
|
||||
} else if (status === 'pending') {
|
||||
text = t('Confirming transaction...');
|
||||
}
|
||||
|
||||
return (
|
||||
<TradingButton type="submit" intent={Intent.Info} disabled={disabled}>
|
||||
{text}
|
||||
</TradingButton>
|
||||
);
|
||||
};
|
||||
|
||||
const parseAllowListText = (str: string) => {
|
||||
return str
|
||||
.split(',')
|
||||
.map((v) => v.trim())
|
||||
.filter(Boolean);
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@ import { ErrorBoundary } from '@sentry/react';
|
||||
import { CompetitionsHeader } from '../../components/competitions/competitions-header';
|
||||
import { Intent, Loader, TradingButton } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
import { useGames } from './hooks/use-games';
|
||||
import { useGames } from '../../lib/hooks/use-games';
|
||||
import { useCurrentEpochInfoQuery } from '../referrals/hooks/__generated__/Epoch';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import { Links } from '../../lib/links';
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
} from '../../components/competitions/competitions-cta';
|
||||
import { GamesContainer } from '../../components/competitions/games-container';
|
||||
import { CompetitionsLeaderboard } from '../../components/competitions/competitions-leaderboard';
|
||||
import { useTeams } from './hooks/use-teams';
|
||||
import { useTeams } from '../../lib/hooks/use-teams';
|
||||
import take from 'lodash/take';
|
||||
import { usePageTitle } from '../../lib/hooks/use-page-title';
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import { ErrorBoundary } from '../../components/error-boundary';
|
||||
import { LayoutWithGradient } from '../../components/layouts-inner';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { JoinTeam } from './join-team';
|
||||
import { UpdateTeamButton } from './update-team-button';
|
||||
|
||||
export const CompetitionsTeam = () => {
|
||||
const t = useT();
|
||||
@@ -93,17 +94,31 @@ const TeamPage = ({
|
||||
<header className="flex gap-3 lg:gap-4 pt-5 lg:pt-10">
|
||||
<TeamAvatar teamId={team.teamId} imgUrl={team.avatarUrl} />
|
||||
<div className="flex flex-col items-start gap-1 lg:gap-3">
|
||||
<h1 className="calt text-2xl lg:text-3xl xl:text-5xl">{team.name}</h1>
|
||||
<h1
|
||||
className="calt text-2xl lg:text-3xl xl:text-5xl"
|
||||
data-testid="team-name"
|
||||
>
|
||||
{team.name}
|
||||
</h1>
|
||||
<JoinTeam team={team} partyTeam={partyTeam} refetch={refetch} />
|
||||
<UpdateTeamButton team={team} />
|
||||
</div>
|
||||
</header>
|
||||
<TeamStats stats={stats} members={members} games={games} />
|
||||
<section>
|
||||
<div className="flex gap-4 lg:gap-8 mb-4 border-b border-default">
|
||||
<ToggleButton active={showGames} onClick={() => setShowGames(true)}>
|
||||
<ToggleButton
|
||||
active={showGames}
|
||||
onClick={() => setShowGames(true)}
|
||||
data-testid="games-toggle"
|
||||
>
|
||||
{t('Games ({{count}})', { count: games ? games.length : 0 })}
|
||||
</ToggleButton>
|
||||
<ToggleButton active={!showGames} onClick={() => setShowGames(false)}>
|
||||
<ToggleButton
|
||||
active={!showGames}
|
||||
onClick={() => setShowGames(false)}
|
||||
data-testid="members-toggle"
|
||||
>
|
||||
{t('Members ({{count}})', {
|
||||
count: members ? members.length : 0,
|
||||
})}
|
||||
@@ -129,16 +144,22 @@ const Games = ({ games }: { games?: TeamGame[] }) => {
|
||||
{
|
||||
name: 'epoch',
|
||||
displayName: t('Epoch'),
|
||||
headerClassName: 'hidden md:block',
|
||||
className: 'hidden md:block',
|
||||
headerClassName: 'hidden md:table-cell',
|
||||
className: 'hidden md:table-cell',
|
||||
},
|
||||
{ name: 'type', displayName: t('Type') },
|
||||
{ name: 'amount', displayName: t('Amount earned') },
|
||||
{
|
||||
name: 'teams',
|
||||
name: 'participatingTeams',
|
||||
displayName: t('No. of participating teams'),
|
||||
headerClassName: 'hidden md:block',
|
||||
className: 'hidden md:block',
|
||||
headerClassName: 'hidden md:table-cell',
|
||||
className: 'hidden md:table-cell',
|
||||
},
|
||||
{
|
||||
name: 'participatingMembers',
|
||||
displayName: t('No. of participating members'),
|
||||
headerClassName: 'hidden md:table-cell',
|
||||
className: 'hidden md:table-cell',
|
||||
},
|
||||
]}
|
||||
data={games.map((game) => ({
|
||||
@@ -146,7 +167,8 @@ const Games = ({ games }: { games?: TeamGame[] }) => {
|
||||
epoch: game.epoch,
|
||||
type: DispatchMetricLabels[game.team.rewardMetric as DispatchMetric],
|
||||
amount: formatNumber(game.team.totalRewardsEarned),
|
||||
teams: game.numberOfParticipants,
|
||||
participatingTeams: game.entities.length,
|
||||
participatingMembers: game.numberOfParticipants,
|
||||
}))}
|
||||
noCollapse={true}
|
||||
/>
|
||||
|
||||
@@ -2,7 +2,7 @@ import { ErrorBoundary } from '@sentry/react';
|
||||
import { CompetitionsHeader } from '../../components/competitions/competitions-header';
|
||||
import { useRef, useState } from 'react';
|
||||
import { useT } from '../../lib/use-t';
|
||||
import { useTeams } from './hooks/use-teams';
|
||||
import { useTeams } from '../../lib/hooks/use-teams';
|
||||
import { CompetitionsLeaderboard } from '../../components/competitions/competitions-leaderboard';
|
||||
import {
|
||||
Input,
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
import { ErrorBoundary } from '../../components/error-boundary';
|
||||
import { usePageTitle } from '../../lib/hooks/use-page-title';
|
||||
import { Box } from '../../components/competitions/box';
|
||||
import { useT } from '../../lib/use-t';
|
||||
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
|
||||
import { Loader, Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { RainbowButton } from '../../components/rainbow-button';
|
||||
import { Link, Navigate, useParams } from 'react-router-dom';
|
||||
import { Links } from '../../lib/links';
|
||||
import { useReferralSetTransaction } from '../../lib/hooks/use-referral-set-transaction';
|
||||
import { type FormFields, TeamForm, TransactionType } from './team-form';
|
||||
import { useTeam } from '../../lib/hooks/use-team';
|
||||
import { LayoutWithGradient } from '../../components/layouts-inner';
|
||||
|
||||
export const CompetitionsUpdateTeam = () => {
|
||||
const t = useT();
|
||||
usePageTitle([t('Competitions'), t('Update a team')]);
|
||||
const { pubKey, isReadOnly } = useVegaWallet();
|
||||
const openWalletDialog = useVegaWalletDialogStore(
|
||||
(store) => store.openVegaWalletDialog
|
||||
);
|
||||
const { teamId } = useParams<{ teamId: string }>();
|
||||
if (!teamId) {
|
||||
return <Navigate to={Links.COMPETITIONS()} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<ErrorBoundary feature="update-team">
|
||||
<LayoutWithGradient>
|
||||
<div className="mx-auto md:w-2/3 max-w-xl">
|
||||
<Box className="flex flex-col gap-4">
|
||||
<h1 className="calt text-2xl lg:text-3xl xl:text-5xl">
|
||||
{t('Update a team')}
|
||||
</h1>
|
||||
{pubKey && !isReadOnly ? (
|
||||
<UpdateTeamFormContainer teamId={teamId} pubKey={pubKey} />
|
||||
) : (
|
||||
<>
|
||||
<p>{t('Connect to update the details of your team.')}</p>
|
||||
<RainbowButton variant="border" onClick={openWalletDialog}>
|
||||
{t('Connect wallet')}
|
||||
</RainbowButton>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
</div>
|
||||
</LayoutWithGradient>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
};
|
||||
|
||||
const UpdateTeamFormContainer = ({
|
||||
teamId,
|
||||
pubKey,
|
||||
}: {
|
||||
teamId: string;
|
||||
pubKey: string;
|
||||
}) => {
|
||||
const t = useT();
|
||||
const { team, loading, error } = useTeam(teamId, pubKey);
|
||||
|
||||
const { err, status, onSubmit } = useReferralSetTransaction({
|
||||
onSuccess: () => {
|
||||
// NOOP
|
||||
},
|
||||
});
|
||||
|
||||
if (loading) {
|
||||
return <Loader size="small" />;
|
||||
}
|
||||
if (error) {
|
||||
return (
|
||||
<Splash className="gap-1">
|
||||
<span>{t('Something went wrong.')}</span>
|
||||
<Link to={Links.COMPETITIONS_TEAM(teamId)} className="underline">
|
||||
{t("Go back to the team's profile")}
|
||||
</Link>
|
||||
</Splash>
|
||||
);
|
||||
}
|
||||
|
||||
const isMyTeam = team?.referrer === pubKey;
|
||||
if (!isMyTeam) {
|
||||
return <Navigate to={Links.COMPETITIONS_TEAM(teamId)} />;
|
||||
}
|
||||
|
||||
const defaultValues: FormFields = {
|
||||
id: team.teamId,
|
||||
name: team.name,
|
||||
url: team.teamUrl,
|
||||
avatarUrl: team.avatarUrl,
|
||||
private: team.closed,
|
||||
allowList: team.allowList.join(','),
|
||||
};
|
||||
|
||||
return (
|
||||
<TeamForm
|
||||
type={TransactionType.UpdateReferralSet}
|
||||
status={status}
|
||||
err={err}
|
||||
isSolo={team.closed}
|
||||
onSubmit={onSubmit}
|
||||
defaultValues={defaultValues}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,254 @@
|
||||
import {
|
||||
TradingFormGroup,
|
||||
TradingInput,
|
||||
TradingInputError,
|
||||
TradingCheckbox,
|
||||
TextArea,
|
||||
TradingButton,
|
||||
Intent,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { URL_REGEX, isValidVegaPublicKey } from '@vegaprotocol/utils';
|
||||
|
||||
import { type useReferralSetTransaction } from '../../lib/hooks/use-referral-set-transaction';
|
||||
import { useT } from '../../lib/use-t';
|
||||
import { useForm, Controller } from 'react-hook-form';
|
||||
import type {
|
||||
CreateReferralSet,
|
||||
UpdateReferralSet,
|
||||
Status,
|
||||
} from '@vegaprotocol/wallet';
|
||||
|
||||
export type FormFields = {
|
||||
id: string;
|
||||
name: string;
|
||||
url: string;
|
||||
avatarUrl: string;
|
||||
private: boolean;
|
||||
allowList: string;
|
||||
};
|
||||
|
||||
export enum TransactionType {
|
||||
CreateReferralSet,
|
||||
UpdateReferralSet,
|
||||
}
|
||||
|
||||
const prepareTransaction = (
|
||||
type: TransactionType,
|
||||
fields: FormFields
|
||||
): CreateReferralSet | UpdateReferralSet => {
|
||||
switch (type) {
|
||||
case TransactionType.CreateReferralSet:
|
||||
return {
|
||||
createReferralSet: {
|
||||
isTeam: true,
|
||||
team: {
|
||||
name: fields.name,
|
||||
teamUrl: fields.url,
|
||||
avatarUrl: fields.avatarUrl,
|
||||
closed: fields.private,
|
||||
allowList: fields.private
|
||||
? parseAllowListText(fields.allowList)
|
||||
: [],
|
||||
},
|
||||
},
|
||||
};
|
||||
case TransactionType.UpdateReferralSet:
|
||||
return {
|
||||
updateReferralSet: {
|
||||
id: fields.id,
|
||||
isTeam: true,
|
||||
team: {
|
||||
name: fields.name,
|
||||
teamUrl: fields.url,
|
||||
avatarUrl: fields.avatarUrl,
|
||||
closed: fields.private,
|
||||
allowList: fields.private
|
||||
? parseAllowListText(fields.allowList)
|
||||
: [],
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export const TeamForm = ({
|
||||
type,
|
||||
status,
|
||||
err,
|
||||
isSolo,
|
||||
onSubmit,
|
||||
defaultValues,
|
||||
}: {
|
||||
type: TransactionType;
|
||||
status: ReturnType<typeof useReferralSetTransaction>['status'];
|
||||
err: ReturnType<typeof useReferralSetTransaction>['err'];
|
||||
isSolo: boolean;
|
||||
onSubmit: ReturnType<typeof useReferralSetTransaction>['onSubmit'];
|
||||
defaultValues?: FormFields;
|
||||
}) => {
|
||||
const t = useT();
|
||||
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
control,
|
||||
watch,
|
||||
formState: { errors },
|
||||
} = useForm<FormFields>({
|
||||
defaultValues: {
|
||||
private: isSolo,
|
||||
...defaultValues,
|
||||
},
|
||||
});
|
||||
|
||||
const isPrivate = watch('private');
|
||||
|
||||
const sendTransaction = (fields: FormFields) => {
|
||||
onSubmit(prepareTransaction(type, fields));
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(sendTransaction)}>
|
||||
<input
|
||||
type="hidden"
|
||||
{...register('id', {
|
||||
disabled: true,
|
||||
})}
|
||||
/>
|
||||
<TradingFormGroup label={t('Team name')} labelFor="name">
|
||||
<TradingInput {...register('name', { required: t('Required') })} />
|
||||
{errors.name?.message && (
|
||||
<TradingInputError forInput="name">
|
||||
{errors.name.message}
|
||||
</TradingInputError>
|
||||
)}
|
||||
</TradingFormGroup>
|
||||
<TradingFormGroup
|
||||
label={t('URL')}
|
||||
labelFor="url"
|
||||
labelDescription={t(
|
||||
'Provide a link so users can learn more about your team'
|
||||
)}
|
||||
>
|
||||
<TradingInput
|
||||
{...register('url', {
|
||||
pattern: { value: URL_REGEX, message: t('Invalid URL') },
|
||||
})}
|
||||
/>
|
||||
{errors.url?.message && (
|
||||
<TradingInputError forInput="url">
|
||||
{errors.url.message}
|
||||
</TradingInputError>
|
||||
)}
|
||||
</TradingFormGroup>
|
||||
<TradingFormGroup
|
||||
label={t('Avatar URL')}
|
||||
labelFor="avatarUrl"
|
||||
labelDescription={t('Provide a URL to a hosted image')}
|
||||
>
|
||||
<TradingInput
|
||||
{...register('avatarUrl', {
|
||||
pattern: {
|
||||
value: URL_REGEX,
|
||||
message: t('Invalid image URL'),
|
||||
},
|
||||
})}
|
||||
/>
|
||||
{errors.avatarUrl?.message && (
|
||||
<TradingInputError forInput="avatarUrl">
|
||||
{errors.avatarUrl.message}
|
||||
</TradingInputError>
|
||||
)}
|
||||
</TradingFormGroup>
|
||||
<TradingFormGroup
|
||||
label={t('Make team private')}
|
||||
labelFor="private"
|
||||
hideLabel={true}
|
||||
>
|
||||
<Controller
|
||||
name="private"
|
||||
control={control}
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<TradingCheckbox
|
||||
label={t('Make team private')}
|
||||
checked={field.value}
|
||||
onCheckedChange={(value) => {
|
||||
field.onChange(value);
|
||||
}}
|
||||
disabled={isSolo}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</TradingFormGroup>
|
||||
{isPrivate && (
|
||||
<TradingFormGroup
|
||||
label={t('Public key allow list')}
|
||||
labelFor="allowList"
|
||||
labelDescription={t(
|
||||
'Use a comma separated list to allow only specific public keys to join the team'
|
||||
)}
|
||||
>
|
||||
<TextArea
|
||||
{...register('allowList', {
|
||||
required: t('Required'),
|
||||
disabled: isSolo,
|
||||
validate: {
|
||||
allowList: (value) => {
|
||||
const publicKeys = parseAllowListText(value);
|
||||
if (publicKeys.every((pk) => isValidVegaPublicKey(pk))) {
|
||||
return true;
|
||||
}
|
||||
return t('Invalid public key found in allow list');
|
||||
},
|
||||
},
|
||||
})}
|
||||
/>
|
||||
{errors.allowList?.message && (
|
||||
<TradingInputError forInput="avatarUrl">
|
||||
{errors.allowList.message}
|
||||
</TradingInputError>
|
||||
)}
|
||||
</TradingFormGroup>
|
||||
)}
|
||||
{err && <p className="text-danger text-xs mb-4 capitalize">{err}</p>}
|
||||
<SubmitButton type={type} status={status} />
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
const SubmitButton = ({
|
||||
type,
|
||||
status,
|
||||
}: {
|
||||
type?: TransactionType;
|
||||
status: Status;
|
||||
}) => {
|
||||
const t = useT();
|
||||
const disabled = status === 'pending' || status === 'requested';
|
||||
|
||||
let text = t('Create');
|
||||
if (type === TransactionType.UpdateReferralSet) {
|
||||
text = t('Update');
|
||||
}
|
||||
|
||||
if (status === 'requested') {
|
||||
text = t('Confirm in wallet...');
|
||||
} else if (status === 'pending') {
|
||||
text = t('Confirming transaction...');
|
||||
}
|
||||
|
||||
return (
|
||||
<TradingButton type="submit" intent={Intent.Info} disabled={disabled}>
|
||||
{text}
|
||||
</TradingButton>
|
||||
);
|
||||
};
|
||||
|
||||
const parseAllowListText = (str: string) => {
|
||||
return str
|
||||
.split(',')
|
||||
.map((v) => v.trim())
|
||||
.filter(Boolean);
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { type Team } from '../../lib/hooks/use-team';
|
||||
import { Intent, TradingAnchorButton } from '@vegaprotocol/ui-toolkit';
|
||||
import { Links } from '../../lib/links';
|
||||
|
||||
export const UpdateTeamButton = ({ team }: { team: Team }) => {
|
||||
const { pubKey, isReadOnly } = useVegaWallet();
|
||||
|
||||
if (pubKey && !isReadOnly && pubKey === team.referrer) {
|
||||
return (
|
||||
<TradingAnchorButton
|
||||
data-testid="update-team-button"
|
||||
href={Links.COMPETITIONS_UPDATE_TEAM(team.teamId)}
|
||||
intent={Intent.Info}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
@@ -21,7 +21,7 @@ import { useT } from '../../lib/use-t';
|
||||
import { Link, Navigate, useNavigate } from 'react-router-dom';
|
||||
import { Links, Routes } from '../../lib/links';
|
||||
import { useReferralProgram } from './hooks/use-referral-program';
|
||||
import { useCreateReferralSet } from '../../lib/hooks/use-create-referral-set';
|
||||
import { useReferralSetTransaction } from '../../lib/hooks/use-referral-set-transaction';
|
||||
import { Trans } from 'react-i18next';
|
||||
|
||||
export const CreateCodeContainer = () => {
|
||||
@@ -155,7 +155,7 @@ const CreateCodeDialog = ({
|
||||
stakeAvailable: currentStakeAvailable,
|
||||
requiredStake,
|
||||
onSubmit,
|
||||
} = useCreateReferralSet();
|
||||
} = useReferralSetTransaction();
|
||||
|
||||
const { details: programDetails } = useReferralProgram();
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { getNumberFormat } from '@vegaprotocol/utils';
|
||||
import { type useTeams } from '../../client-pages/competitions/hooks/use-teams';
|
||||
import { type useTeams } from '../../lib/hooks/use-teams';
|
||||
import { useT } from '../../lib/use-t';
|
||||
import { Table } from '../table';
|
||||
import { Rank } from './graphics/rank';
|
||||
|
||||
@@ -30,35 +30,42 @@ export const TeamStats = ({
|
||||
<>
|
||||
<StatSection>
|
||||
<StatList>
|
||||
<Stat value={members ? members.length : 0} label={t('Members')} />
|
||||
<Stat
|
||||
value={members ? members.length : 0}
|
||||
label={t('Members')}
|
||||
valueTestId="members-count-stat"
|
||||
/>
|
||||
<Stat
|
||||
value={stats ? stats.totalGamesPlayed : 0}
|
||||
label={t('Total games')}
|
||||
tooltip={t('Total number of games this team has participated in')}
|
||||
valueTestId="total-games-stat"
|
||||
/>
|
||||
<StatSectionSeparator />
|
||||
<Stat
|
||||
value={
|
||||
stats
|
||||
? formatNumberRounded(
|
||||
new BigNumber(stats.totalQuantumVolume),
|
||||
new BigNumber(stats.totalQuantumVolume || 0),
|
||||
'1e3'
|
||||
)
|
||||
: 0
|
||||
}
|
||||
label={t('Total volume')}
|
||||
valueTestId="total-volume-stat"
|
||||
/>
|
||||
<Stat
|
||||
value={
|
||||
stats
|
||||
? formatNumberRounded(
|
||||
new BigNumber(stats.totalQuantumRewards),
|
||||
new BigNumber(stats.totalQuantumRewards || 0),
|
||||
'1e3'
|
||||
)
|
||||
: 0
|
||||
}
|
||||
label={t('Rewards paid')}
|
||||
label={t('Rewards paid out')}
|
||||
tooltip={'Total amount of rewards paid out to this team in qUSD'}
|
||||
valueTestId="rewards-paid-stat"
|
||||
/>
|
||||
</StatList>
|
||||
</StatSection>
|
||||
@@ -159,14 +166,18 @@ const Stat = ({
|
||||
value,
|
||||
label,
|
||||
tooltip,
|
||||
valueTestId,
|
||||
}: {
|
||||
value: ReactNode;
|
||||
label: ReactNode;
|
||||
tooltip?: string;
|
||||
valueTestId?: string;
|
||||
}) => {
|
||||
return (
|
||||
<div>
|
||||
<dd className="text-3xl lg:text-4xl">{value}</dd>
|
||||
<dd className="text-3xl lg:text-4xl" data-testid={valueTestId}>
|
||||
{value}
|
||||
</dd>
|
||||
<dt className="text-sm text-muted">
|
||||
{tooltip ? (
|
||||
<Tooltip description={tooltip} underline={false}>
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
VegaIconNames,
|
||||
TradingInput,
|
||||
TinyScroll,
|
||||
truncateMiddle,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { IconNames } from '@blueprintjs/icons';
|
||||
import {
|
||||
@@ -30,6 +31,9 @@ import {
|
||||
DispatchMetricLabels,
|
||||
EntityScopeLabelMapping,
|
||||
MarketState,
|
||||
type DispatchStrategy,
|
||||
IndividualScopeMapping,
|
||||
IndividualScopeDescriptionMapping,
|
||||
} from '@vegaprotocol/types';
|
||||
import { Card } from '../card/card';
|
||||
import { useMemo, useState } from 'react';
|
||||
@@ -308,6 +312,9 @@ export const ActiveRewardCard = ({
|
||||
MarketState.STATE_CLOSED,
|
||||
].includes(m.state)
|
||||
);
|
||||
if (marketSettled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const assetInSettledMarket =
|
||||
allMarkets &&
|
||||
@@ -326,10 +333,6 @@ export const ActiveRewardCard = ({
|
||||
return false;
|
||||
});
|
||||
|
||||
if (marketSettled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Gray out the cards that are related to suspended markets
|
||||
const suspended = transferNode.markets?.some(
|
||||
(m) =>
|
||||
@@ -359,6 +362,7 @@ export const ActiveRewardCard = ({
|
||||
: getGradientClasses(dispatchStrategy.dispatchMetric);
|
||||
|
||||
const entityScope = dispatchStrategy.entityScope;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
@@ -474,83 +478,127 @@ export const ActiveRewardCard = ({
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
|
||||
{dispatchStrategy?.dispatchMetric && (
|
||||
<span className="text-muted text-sm h-[2rem]">
|
||||
{t(DispatchMetricDescription[dispatchStrategy?.dispatchMetric])}
|
||||
</span>
|
||||
)}
|
||||
|
||||
<span className="border-[0.5px] border-gray-700" />
|
||||
|
||||
<div className="flex justify-between flex-wrap items-center gap-3 text-xs">
|
||||
<span className="flex flex-col gap-1">
|
||||
<span className="flex items-center gap-1 text-muted">
|
||||
{t('Entity scope')}{' '}
|
||||
</span>
|
||||
|
||||
<span className="flex items-center gap-1">
|
||||
{kind.dispatchStrategy?.teamScope && (
|
||||
<Tooltip
|
||||
description={
|
||||
<span>{kind.dispatchStrategy?.teamScope}</span>
|
||||
}
|
||||
>
|
||||
<span className="flex items-center p-1 rounded-full border border-gray-600">
|
||||
{<VegaIcon name={VegaIconNames.TEAM} size={16} />}
|
||||
</span>
|
||||
</Tooltip>
|
||||
)}
|
||||
{kind.dispatchStrategy?.individualScope && (
|
||||
<Tooltip
|
||||
description={
|
||||
<span>{kind.dispatchStrategy?.individualScope}</span>
|
||||
}
|
||||
>
|
||||
<span className="flex items-center p-1 rounded-full border border-gray-600">
|
||||
{<VegaIcon name={VegaIconNames.MAN} size={16} />}
|
||||
</span>
|
||||
</Tooltip>
|
||||
)}
|
||||
{/* Shows transfer status */}
|
||||
{/* <StatusIndicator
|
||||
status={transfer.status}
|
||||
reason={transfer.reason}
|
||||
/> */}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<span className="flex flex-col gap-1">
|
||||
<span className="flex items-center gap-1 text-muted">
|
||||
{t('Staked VEGA')}{' '}
|
||||
</span>
|
||||
<span className="flex items-center gap-1">
|
||||
{addDecimalsFormatNumber(
|
||||
kind.dispatchStrategy?.stakingRequirement || 0,
|
||||
transfer.asset?.decimals || 0
|
||||
)}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<span className="flex flex-col gap-1">
|
||||
<span className="flex items-center gap-1 text-muted">
|
||||
{t('Average position')}{' '}
|
||||
</span>
|
||||
<span className="flex items-center gap-1">
|
||||
{addDecimalsFormatNumber(
|
||||
kind.dispatchStrategy
|
||||
?.notionalTimeWeightedAveragePositionRequirement || 0,
|
||||
transfer.asset?.decimals || 0
|
||||
)}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
{kind.dispatchStrategy && (
|
||||
<RewardRequirements
|
||||
dispatchStrategy={kind.dispatchStrategy}
|
||||
assetDecimalPlaces={transfer.asset?.decimals}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const RewardRequirements = ({
|
||||
dispatchStrategy,
|
||||
assetDecimalPlaces = 0,
|
||||
}: {
|
||||
dispatchStrategy: DispatchStrategy;
|
||||
assetDecimalPlaces: number | undefined;
|
||||
}) => {
|
||||
const t = useT();
|
||||
|
||||
return (
|
||||
<dl className="flex justify-between flex-wrap items-center gap-3 text-xs">
|
||||
<div className="flex flex-col gap-1">
|
||||
<dt className="flex items-center gap-1 text-muted">
|
||||
{t('{{entity}} scope', {
|
||||
entity: EntityScopeLabelMapping[dispatchStrategy.entityScope],
|
||||
})}
|
||||
</dt>
|
||||
<dd className="flex items-center gap-1">
|
||||
<RewardEntityScope dispatchStrategy={dispatchStrategy} />
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-1">
|
||||
<dt className="flex items-center gap-1 text-muted">
|
||||
{t('Staked VEGA')}
|
||||
</dt>
|
||||
<dd className="flex items-center gap-1">
|
||||
{addDecimalsFormatNumber(
|
||||
dispatchStrategy?.stakingRequirement || 0,
|
||||
assetDecimalPlaces
|
||||
)}
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-1">
|
||||
<dt className="flex items-center gap-1 text-muted">
|
||||
{t('Average position')}
|
||||
</dt>
|
||||
<dd className="flex items-center gap-1">
|
||||
{addDecimalsFormatNumber(
|
||||
dispatchStrategy?.notionalTimeWeightedAveragePositionRequirement ||
|
||||
0,
|
||||
assetDecimalPlaces
|
||||
)}
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
);
|
||||
};
|
||||
|
||||
const RewardEntityScope = ({
|
||||
dispatchStrategy,
|
||||
}: {
|
||||
dispatchStrategy: DispatchStrategy;
|
||||
}) => {
|
||||
const t = useT();
|
||||
|
||||
if (dispatchStrategy.entityScope === EntityScope.ENTITY_SCOPE_TEAMS) {
|
||||
return (
|
||||
<Tooltip
|
||||
description={
|
||||
dispatchStrategy.teamScope?.length ? (
|
||||
<div className="text-xs">
|
||||
<p className="mb-1">{t('Eligible teams')}</p>
|
||||
<ul>
|
||||
{dispatchStrategy.teamScope.map((teamId) => {
|
||||
if (!teamId) return null;
|
||||
return <li key={teamId}>{truncateMiddle(teamId)}</li>;
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
) : (
|
||||
t('All teams are eligible')
|
||||
)
|
||||
}
|
||||
>
|
||||
<span>
|
||||
{dispatchStrategy.teamScope?.length
|
||||
? t('Some teams')
|
||||
: t('All teams')}
|
||||
</span>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
dispatchStrategy.entityScope === EntityScope.ENTITY_SCOPE_INDIVIDUALS &&
|
||||
dispatchStrategy.individualScope
|
||||
) {
|
||||
return (
|
||||
<Tooltip
|
||||
description={
|
||||
IndividualScopeDescriptionMapping[dispatchStrategy.individualScope]
|
||||
}
|
||||
>
|
||||
<span>{IndividualScopeMapping[dispatchStrategy.individualScope]}</span>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const getGradientClasses = (d: DispatchMetric | undefined) => {
|
||||
switch (d) {
|
||||
case DispatchMetric.DISPATCH_METRIC_AVERAGE_POSITION:
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
CONSOLE_IMAGE_NAME=vegaprotocol/trading:latest
|
||||
VEGA_VERSION=v0.74.0-preview.2
|
||||
LOCAL_SERVER=false
|
||||
VEGA_VERSION=v0.74.0-preview.7
|
||||
LOCAL_SERVER=true
|
||||
|
||||
@@ -5,6 +5,7 @@ from vega_sim.null_service import VegaServiceNull
|
||||
from conftest import init_vega
|
||||
from actions.utils import next_epoch
|
||||
from fixtures.market import setup_continuous_market
|
||||
from conftest import auth_setup, init_page, init_vega, risk_accepted_setup
|
||||
from wallet_config import PARTY_A, PARTY_B, PARTY_C, PARTY_D, MM_WALLET
|
||||
|
||||
|
||||
@@ -13,7 +14,16 @@ def vega(request):
|
||||
with init_vega(request) as vega:
|
||||
yield vega
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def team_page(vega, browser, request, setup_teams_and_games):
|
||||
with init_page(vega, browser, request) as page:
|
||||
risk_accepted_setup(page)
|
||||
auth_setup(vega, page)
|
||||
team_id = setup_teams_and_games["team_id"]
|
||||
page.goto(f"/#/competitions/teams/{team_id}")
|
||||
yield page
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def setup_teams_and_games(vega: VegaServiceNull):
|
||||
tDAI_market = setup_continuous_market(vega)
|
||||
tDAI_asset_id = vega.find_asset_id(symbol="tDAI")
|
||||
@@ -50,6 +60,16 @@ def setup_teams_and_games(vega: VegaServiceNull):
|
||||
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
|
||||
current_epoch = vega.statistics().epoch_seq
|
||||
game_start = current_epoch + 1
|
||||
game_end = current_epoch + 11
|
||||
|
||||
current_epoch = vega.statistics().epoch_seq
|
||||
print(f"[EPOCH: {current_epoch}] creating recurring transfer")
|
||||
print(f"Game start: {game_start}")
|
||||
print(f"Game game end: {game_end}")
|
||||
|
||||
vega.recurring_transfer(
|
||||
from_key_name=PARTY_A.name,
|
||||
from_account_type=vega_protos.vega.ACCOUNT_TYPE_GENERAL,
|
||||
@@ -62,39 +82,50 @@ def setup_teams_and_games(vega: VegaServiceNull):
|
||||
n_top_performers=1,
|
||||
amount=100,
|
||||
factor=1.0,
|
||||
start_epoch=game_start,
|
||||
end_epoch=game_end,
|
||||
window_length=10
|
||||
)
|
||||
vega.submit_order(
|
||||
trading_key=PARTY_B.name,
|
||||
market_id=tDAI_market,
|
||||
order_type="TYPE_MARKET",
|
||||
time_in_force="TIME_IN_FORCE_IOC",
|
||||
side="SIDE_BUY",
|
||||
volume=1,
|
||||
)
|
||||
vega.submit_order(
|
||||
trading_key=PARTY_A.name,
|
||||
market_id=tDAI_market,
|
||||
order_type="TYPE_MARKET",
|
||||
time_in_force="TIME_IN_FORCE_IOC",
|
||||
side="SIDE_BUY",
|
||||
volume=1,
|
||||
)
|
||||
next_epoch(vega=vega)
|
||||
return tDAI_market, tDAI_asset_id, team_id, team_name
|
||||
|
||||
next_epoch(vega)
|
||||
print(f"[EPOCH: {vega.statistics().epoch_seq}] starting order activity")
|
||||
|
||||
@pytest.mark.usefixtures("auth", "risk_accepted")
|
||||
def test_teams(vega: VegaServiceNull, page: Page):
|
||||
market_id, asset_id, team_id, team_name = setup_teams_and_games(vega)
|
||||
page.goto(f"/#/competitions/teams/{team_id}")
|
||||
# page.pause()
|
||||
expect(page.get_by_role("heading", level=1)).to_have_text(team_name)
|
||||
expect(page.get_by_text("Members (3)")).to_be_visible()
|
||||
# Team statistics will only return data when team has been active
|
||||
# for DEFAULT_AGGREGATION_EPOCHS epochs
|
||||
#
|
||||
# https://vegaprotocol.slack.com/archives/C02KVKMAE82/p1706635625851769?thread_ts=1706631542.576449&cid=C02KVKMAE82
|
||||
|
||||
# Create trading activity for 10 epochs (which is the default)
|
||||
for i in range(10):
|
||||
vega.submit_order(
|
||||
trading_key=PARTY_B.name,
|
||||
market_id=tDAI_market,
|
||||
order_type="TYPE_MARKET",
|
||||
time_in_force="TIME_IN_FORCE_IOC",
|
||||
side="SIDE_BUY",
|
||||
volume=1,
|
||||
)
|
||||
vega.submit_order(
|
||||
trading_key=PARTY_A.name,
|
||||
market_id=tDAI_market,
|
||||
order_type="TYPE_MARKET",
|
||||
time_in_force="TIME_IN_FORCE_IOC",
|
||||
side="SIDE_BUY",
|
||||
volume=1,
|
||||
)
|
||||
next_epoch(vega)
|
||||
print(f"[EPOCH: {vega.statistics().epoch_seq}] {i} epoch passed")
|
||||
|
||||
return {
|
||||
"market_id": tDAI_market,
|
||||
"asset_id": tDAI_asset_id,
|
||||
"team_id": team_id,
|
||||
"team_name": team_name,
|
||||
}
|
||||
|
||||
|
||||
def create_team(vega: VegaServiceNull):
|
||||
team_name = "Foobar"
|
||||
|
||||
vega.create_referral_set(
|
||||
key_name=PARTY_A.name,
|
||||
name=team_name,
|
||||
@@ -104,3 +135,66 @@ def create_team(vega: VegaServiceNull):
|
||||
)
|
||||
|
||||
return team_name
|
||||
|
||||
def test_team_page_games_table(team_page: Page):
|
||||
team_page.get_by_test_id("games-toggle").click()
|
||||
expect(team_page.get_by_test_id("games-toggle")).to_have_text("Games (1)")
|
||||
expect(team_page.get_by_test_id("rank-0")).to_have_text("1")
|
||||
expect(team_page.get_by_test_id("epoch-0")).to_have_text("18")
|
||||
expect(team_page.get_by_test_id("type-0")).to_have_text("Price maker fees paid")
|
||||
expect(team_page.get_by_test_id("amount-0")).to_have_text("100,000,000")
|
||||
expect(team_page.get_by_test_id("participatingTeams-0")).to_have_text(
|
||||
"1"
|
||||
)
|
||||
expect(team_page.get_by_test_id("participatingMembers-0")).to_have_text(
|
||||
"2"
|
||||
)
|
||||
|
||||
def test_team_page_members_table(team_page: Page):
|
||||
team_page.get_by_test_id("members-toggle").click()
|
||||
expect(team_page.get_by_test_id("members-toggle")).to_have_text("Members (3)")
|
||||
expect(team_page.get_by_test_id("referee-0")).to_be_visible()
|
||||
expect(team_page.get_by_test_id("joinedAt-0")).to_be_visible()
|
||||
expect(team_page.get_by_test_id("joinedAtEpoch-0")).to_have_text("8")
|
||||
|
||||
def test_team_page_headline(team_page: Page, setup_teams_and_games
|
||||
):
|
||||
team_name = setup_teams_and_games["team_name"]
|
||||
expect(team_page.get_by_test_id("team-name")).to_have_text(team_name)
|
||||
expect(team_page.get_by_test_id("members-count-stat")).to_have_text("3")
|
||||
|
||||
expect(team_page.get_by_test_id("total-games-stat")).to_have_text(
|
||||
"1"
|
||||
)
|
||||
|
||||
# TODO this still seems wrong as its always 0
|
||||
expect(team_page.get_by_test_id("total-volume-stat")).to_have_text(
|
||||
"0"
|
||||
)
|
||||
|
||||
expect(team_page.get_by_test_id("rewards-paid-stat")).to_have_text(
|
||||
"100m"
|
||||
)
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def competitions_page(vega, browser, request):
|
||||
with init_page(vega, browser, request) as page:
|
||||
risk_accepted_setup(page)
|
||||
auth_setup(vega, page)
|
||||
yield page
|
||||
|
||||
def test_leaderboard(competitions_page: Page, setup_teams_and_games):
|
||||
team_name = setup_teams_and_games["team_name"]
|
||||
competitions_page.goto(f"/#/competitions/")
|
||||
expect(competitions_page.get_by_test_id("rank-0").locator(".text-yellow-300")).to_have_count(1)
|
||||
expect(competitions_page.get_by_test_id("team-0")).to_have_text(team_name)
|
||||
expect(competitions_page.get_by_test_id("status-0")).to_have_text("Open")
|
||||
|
||||
expect(competitions_page.get_by_test_id("earned-0")).to_have_text("100,000,000")
|
||||
expect(competitions_page.get_by_test_id("games-0")).to_have_text("1")
|
||||
|
||||
# TODO still odd that this is 0
|
||||
expect(competitions_page.get_by_test_id("volume-0")).to_have_text("-")
|
||||
|
||||
#TODO def test_games(competitions_page: Page):
|
||||
#TODO currently no games appear which i think is a bug
|
||||
|
||||
@@ -7,6 +7,7 @@ fragment TeamFields on Team {
|
||||
createdAt
|
||||
createdAtEpoch
|
||||
closed
|
||||
allowList
|
||||
}
|
||||
|
||||
fragment TeamStatsFields on TeamStatistics {
|
||||
@@ -50,7 +51,7 @@ fragment TeamGameFields on Game {
|
||||
}
|
||||
}
|
||||
|
||||
query Team($teamId: ID!, $partyId: ID) {
|
||||
query Team($teamId: ID!, $partyId: ID, $aggregationEpochs: Int) {
|
||||
teams(teamId: $teamId) {
|
||||
edges {
|
||||
node {
|
||||
@@ -65,7 +66,7 @@ query Team($teamId: ID!, $partyId: ID) {
|
||||
}
|
||||
}
|
||||
}
|
||||
teamsStatistics(teamId: $teamId) {
|
||||
teamsStatistics(teamId: $teamId, aggregationEpochs: $aggregationEpochs) {
|
||||
edges {
|
||||
node {
|
||||
...TeamStatsFields
|
||||
|
||||
+9
-6
@@ -3,23 +3,24 @@ import * as Types from '@vegaprotocol/types';
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type TeamFieldsFragment = { __typename?: 'Team', teamId: string, referrer: string, name: string, teamUrl: string, avatarUrl: string, createdAt: any, createdAtEpoch: number, closed: boolean };
|
||||
export type TeamFieldsFragment = { __typename?: 'Team', teamId: string, referrer: string, name: string, teamUrl: string, avatarUrl: string, createdAt: any, createdAtEpoch: number, closed: boolean, allowList: Array<string> };
|
||||
|
||||
export type TeamStatsFieldsFragment = { __typename?: 'TeamStatistics', teamId: string, totalQuantumVolume: string, totalQuantumRewards: string, totalGamesPlayed: number, gamesPlayed: Array<string>, quantumRewards: Array<{ __typename?: 'QuantumRewardsPerEpoch', epoch: number, total_quantum_rewards: string }> };
|
||||
|
||||
export type TeamRefereeFieldsFragment = { __typename?: 'TeamReferee', teamId: string, referee: string, joinedAt: any, joinedAtEpoch: number };
|
||||
|
||||
export type TeamEntityFragment = { __typename?: 'TeamGameEntity', rank: number, volume: string, rewardMetric: string, rewardEarned: string, totalRewardsEarned: string, team: { __typename?: 'TeamParticipation', teamId: string } };
|
||||
export type TeamEntityFragment = { __typename?: 'TeamGameEntity', rank: number, volume: string, rewardMetric: Types.DispatchMetric, rewardEarned: string, totalRewardsEarned: string, team: { __typename?: 'TeamParticipation', teamId: string } };
|
||||
|
||||
export type TeamGameFieldsFragment = { __typename?: 'Game', id: string, epoch: number, numberOfParticipants: number, entities: Array<{ __typename?: 'IndividualGameEntity' } | { __typename?: 'TeamGameEntity', rank: number, volume: string, rewardMetric: string, rewardEarned: string, totalRewardsEarned: string, team: { __typename?: 'TeamParticipation', teamId: string } }> };
|
||||
export type TeamGameFieldsFragment = { __typename?: 'Game', id: string, epoch: number, numberOfParticipants: number, entities: Array<{ __typename?: 'IndividualGameEntity' } | { __typename?: 'TeamGameEntity', rank: number, volume: string, rewardMetric: Types.DispatchMetric, rewardEarned: string, totalRewardsEarned: string, team: { __typename?: 'TeamParticipation', teamId: string } }> };
|
||||
|
||||
export type TeamQueryVariables = Types.Exact<{
|
||||
teamId: Types.Scalars['ID'];
|
||||
partyId?: Types.InputMaybe<Types.Scalars['ID']>;
|
||||
aggregationEpochs?: Types.InputMaybe<Types.Scalars['Int']>;
|
||||
}>;
|
||||
|
||||
|
||||
export type TeamQuery = { __typename?: 'Query', teams?: { __typename?: 'TeamConnection', edges: Array<{ __typename?: 'TeamEdge', node: { __typename?: 'Team', teamId: string, referrer: string, name: string, teamUrl: string, avatarUrl: string, createdAt: any, createdAtEpoch: number, closed: boolean } }> } | null, partyTeams?: { __typename?: 'TeamConnection', edges: Array<{ __typename?: 'TeamEdge', node: { __typename?: 'Team', teamId: string, referrer: string, name: string, teamUrl: string, avatarUrl: string, createdAt: any, createdAtEpoch: number, closed: boolean } }> } | null, teamsStatistics?: { __typename?: 'TeamsStatisticsConnection', edges: Array<{ __typename?: 'TeamStatisticsEdge', node: { __typename?: 'TeamStatistics', teamId: string, totalQuantumVolume: string, totalQuantumRewards: string, totalGamesPlayed: number, gamesPlayed: Array<string>, quantumRewards: Array<{ __typename?: 'QuantumRewardsPerEpoch', epoch: number, total_quantum_rewards: string }> } }> } | null, teamReferees?: { __typename?: 'TeamRefereeConnection', edges: Array<{ __typename?: 'TeamRefereeEdge', node: { __typename?: 'TeamReferee', teamId: string, referee: string, joinedAt: any, joinedAtEpoch: number } }> } | null, games: { __typename?: 'GamesConnection', edges?: Array<{ __typename?: 'GameEdge', node: { __typename?: 'Game', id: string, epoch: number, numberOfParticipants: number, entities: Array<{ __typename?: 'IndividualGameEntity' } | { __typename?: 'TeamGameEntity', rank: number, volume: string, rewardMetric: string, rewardEarned: string, totalRewardsEarned: string, team: { __typename?: 'TeamParticipation', teamId: string } }> } } | null> | null } };
|
||||
export type TeamQuery = { __typename?: 'Query', teams?: { __typename?: 'TeamConnection', edges: Array<{ __typename?: 'TeamEdge', node: { __typename?: 'Team', teamId: string, referrer: string, name: string, teamUrl: string, avatarUrl: string, createdAt: any, createdAtEpoch: number, closed: boolean, allowList: Array<string> } }> } | null, partyTeams?: { __typename?: 'TeamConnection', edges: Array<{ __typename?: 'TeamEdge', node: { __typename?: 'Team', teamId: string, referrer: string, name: string, teamUrl: string, avatarUrl: string, createdAt: any, createdAtEpoch: number, closed: boolean, allowList: Array<string> } }> } | null, teamsStatistics?: { __typename?: 'TeamsStatisticsConnection', edges: Array<{ __typename?: 'TeamStatisticsEdge', node: { __typename?: 'TeamStatistics', teamId: string, totalQuantumVolume: string, totalQuantumRewards: string, totalGamesPlayed: number, gamesPlayed: Array<string>, quantumRewards: Array<{ __typename?: 'QuantumRewardsPerEpoch', epoch: number, total_quantum_rewards: string }> } }> } | null, teamReferees?: { __typename?: 'TeamRefereeConnection', edges: Array<{ __typename?: 'TeamRefereeEdge', node: { __typename?: 'TeamReferee', teamId: string, referee: string, joinedAt: any, joinedAtEpoch: number } }> } | null, games: { __typename?: 'GamesConnection', edges?: Array<{ __typename?: 'GameEdge', node: { __typename?: 'Game', id: string, epoch: number, numberOfParticipants: number, entities: Array<{ __typename?: 'IndividualGameEntity' } | { __typename?: 'TeamGameEntity', rank: number, volume: string, rewardMetric: Types.DispatchMetric, rewardEarned: string, totalRewardsEarned: string, team: { __typename?: 'TeamParticipation', teamId: string } }> } } | null> | null } };
|
||||
|
||||
export const TeamFieldsFragmentDoc = gql`
|
||||
fragment TeamFields on Team {
|
||||
@@ -31,6 +32,7 @@ export const TeamFieldsFragmentDoc = gql`
|
||||
createdAt
|
||||
createdAtEpoch
|
||||
closed
|
||||
allowList
|
||||
}
|
||||
`;
|
||||
export const TeamStatsFieldsFragmentDoc = gql`
|
||||
@@ -79,7 +81,7 @@ export const TeamGameFieldsFragmentDoc = gql`
|
||||
}
|
||||
${TeamEntityFragmentDoc}`;
|
||||
export const TeamDocument = gql`
|
||||
query Team($teamId: ID!, $partyId: ID) {
|
||||
query Team($teamId: ID!, $partyId: ID, $aggregationEpochs: Int) {
|
||||
teams(teamId: $teamId) {
|
||||
edges {
|
||||
node {
|
||||
@@ -94,7 +96,7 @@ export const TeamDocument = gql`
|
||||
}
|
||||
}
|
||||
}
|
||||
teamsStatistics(teamId: $teamId) {
|
||||
teamsStatistics(teamId: $teamId, aggregationEpochs: $aggregationEpochs) {
|
||||
edges {
|
||||
node {
|
||||
...TeamStatsFields
|
||||
@@ -135,6 +137,7 @@ ${TeamGameFieldsFragmentDoc}`;
|
||||
* variables: {
|
||||
* teamId: // value for 'teamId'
|
||||
* partyId: // value for 'partyId'
|
||||
* aggregationEpochs: // value for 'aggregationEpochs'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
|
||||
+3
-4
@@ -1,6 +1,6 @@
|
||||
import compact from 'lodash/compact';
|
||||
import { useActiveRewardsQuery } from '../../../components/rewards-container/__generated__/Rewards';
|
||||
import { isActiveReward } from '../../../components/rewards-container/active-rewards';
|
||||
import { useActiveRewardsQuery } from '../../components/rewards-container/__generated__/Rewards';
|
||||
import { isActiveReward } from '../../components/rewards-container/active-rewards';
|
||||
import { EntityScope, type TransferNode } from '@vegaprotocol/types';
|
||||
|
||||
const isScopedToTeams = (node: TransferNode) =>
|
||||
@@ -25,9 +25,8 @@ export const useGames = ({
|
||||
const games = compact(data?.transfersConnection?.edges?.map((n) => n?.node))
|
||||
.map((n) => n as TransferNode)
|
||||
.filter((node) => {
|
||||
const recurring = node.transfer.kind.__typename !== 'RecurringTransfer';
|
||||
const active = onlyActive ? isActiveReward(node, currentEpoch) : true;
|
||||
return active && recurring && isScopedToTeams(node);
|
||||
return active && isScopedToTeams(node);
|
||||
});
|
||||
|
||||
return {
|
||||
+4
-3
@@ -1,14 +1,15 @@
|
||||
import {
|
||||
useSimpleTransaction,
|
||||
type CreateReferralSet,
|
||||
type Options,
|
||||
type CreateReferralSet,
|
||||
type UpdateReferralSet,
|
||||
} from '@vegaprotocol/wallet';
|
||||
import { useStakeAvailable } from './use-stake-available';
|
||||
|
||||
/**
|
||||
* Manages state for creating a referral set or team
|
||||
*/
|
||||
export const useCreateReferralSet = (opts?: Options) => {
|
||||
export const useReferralSetTransaction = (opts?: Options) => {
|
||||
const { stakeAvailable, requiredStake, isEligible } = useStakeAvailable();
|
||||
|
||||
const { status, result, error, send } = useSimpleTransaction({
|
||||
@@ -16,7 +17,7 @@ export const useCreateReferralSet = (opts?: Options) => {
|
||||
onError: opts?.onError,
|
||||
});
|
||||
|
||||
const onSubmit = (tx: CreateReferralSet) => {
|
||||
const onSubmit = (tx: CreateReferralSet | UpdateReferralSet) => {
|
||||
send(tx);
|
||||
};
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
type TeamRefereeFieldsFragment,
|
||||
type TeamEntityFragment,
|
||||
} from './__generated__/Team';
|
||||
import { DEFAULT_AGGREGATION_EPOCHS } from './use-teams';
|
||||
|
||||
export type Team = TeamFieldsFragment;
|
||||
export type TeamStats = TeamStatsFieldsFragment;
|
||||
@@ -16,7 +17,11 @@ export type TeamGame = ReturnType<typeof useTeam>['games'][number];
|
||||
|
||||
export const useTeam = (teamId?: string, partyId?: string) => {
|
||||
const { data, loading, error, refetch } = useTeamQuery({
|
||||
variables: { teamId: teamId || '', partyId },
|
||||
variables: {
|
||||
teamId: teamId || '',
|
||||
partyId,
|
||||
aggregationEpochs: DEFAULT_AGGREGATION_EPOCHS,
|
||||
},
|
||||
skip: !teamId,
|
||||
fetchPolicy: 'cache-and-network',
|
||||
});
|
||||
@@ -47,6 +52,7 @@ export const useTeam = (teamId?: string, partyId?: string) => {
|
||||
id: edge.node.id,
|
||||
epoch: edge.node.epoch,
|
||||
numberOfParticipants: edge.node.numberOfParticipants,
|
||||
entities: edge.node.entities,
|
||||
team: team as TeamEntity, // TS can't infer that all the game entities are teams
|
||||
};
|
||||
});
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ type UseTeamsArgs = {
|
||||
order?: 'asc' | 'desc';
|
||||
};
|
||||
|
||||
const DEFAULT_AGGREGATION_EPOCHS = 10;
|
||||
export const DEFAULT_AGGREGATION_EPOCHS = 10;
|
||||
|
||||
export const useTeams = ({
|
||||
aggregationEpochs = DEFAULT_AGGREGATION_EPOCHS,
|
||||
@@ -18,9 +18,10 @@ export const Routes = {
|
||||
REFERRALS_CREATE_CODE: '/referrals/create-code',
|
||||
COMPETITIONS: '/competitions',
|
||||
COMPETITIONS_TEAMS: '/competitions/teams',
|
||||
COMPETITIONS_TEAM: '/competitions/teams/:teamId',
|
||||
COMPETITIONS_CREATE_TEAM: '/competitions/teams/create',
|
||||
COMPETITIONS_CREATE_TEAM_SOLO: '/competitions/teams/create?solo=true',
|
||||
COMPETITIONS_TEAM: '/competitions/teams/:teamId',
|
||||
COMPETITIONS_UPDATE_TEAM: '/competitions/teams/:teamId/update',
|
||||
FEES: '/fees',
|
||||
REWARDS: '/rewards',
|
||||
} as const;
|
||||
@@ -47,10 +48,12 @@ export const Links: ConsoleLinks = {
|
||||
REFERRALS_CREATE_CODE: () => Routes.REFERRALS_CREATE_CODE,
|
||||
COMPETITIONS: () => Routes.COMPETITIONS,
|
||||
COMPETITIONS_TEAMS: () => Routes.COMPETITIONS_TEAMS,
|
||||
COMPETITIONS_CREATE_TEAM: () => Routes.COMPETITIONS_CREATE_TEAM,
|
||||
COMPETITIONS_CREATE_TEAM_SOLO: () => Routes.COMPETITIONS_CREATE_TEAM_SOLO,
|
||||
COMPETITIONS_TEAM: (teamId: string) =>
|
||||
Routes.COMPETITIONS_TEAM.replace(':teamId', teamId),
|
||||
COMPETITIONS_CREATE_TEAM: () => Routes.COMPETITIONS_CREATE_TEAM,
|
||||
COMPETITIONS_CREATE_TEAM_SOLO: () => Routes.COMPETITIONS_CREATE_TEAM_SOLO,
|
||||
COMPETITIONS_UPDATE_TEAM: (teamId: string) =>
|
||||
Routes.COMPETITIONS_UPDATE_TEAM.replace(':teamId', teamId),
|
||||
FEES: () => Routes.FEES,
|
||||
REWARDS: () => Routes.REWARDS,
|
||||
};
|
||||
|
||||
@@ -32,6 +32,7 @@ import { CompetitionsHome } from '../client-pages/competitions/competitions-home
|
||||
import { CompetitionsTeams } from '../client-pages/competitions/competitions-teams';
|
||||
import { CompetitionsTeam } from '../client-pages/competitions/competitions-team';
|
||||
import { CompetitionsCreateTeam } from '../client-pages/competitions/competitions-create-team';
|
||||
import { CompetitionsUpdateTeam } from '../client-pages/competitions/competitions-update-team';
|
||||
|
||||
// These must remain dynamically imported as pennant cannot be compiled by nextjs due to ESM
|
||||
// Using dynamic imports is a workaround for this until pennant is published as ESM
|
||||
@@ -112,13 +113,17 @@ export const useRouterConfig = (): RouteObject[] => {
|
||||
],
|
||||
},
|
||||
// pages with blurred background
|
||||
{
|
||||
path: AppRoutes.COMPETITIONS_TEAM,
|
||||
element: <CompetitionsTeam />,
|
||||
},
|
||||
{
|
||||
path: AppRoutes.COMPETITIONS_CREATE_TEAM,
|
||||
element: <CompetitionsCreateTeam />,
|
||||
},
|
||||
{
|
||||
path: AppRoutes.COMPETITIONS_TEAM,
|
||||
element: <CompetitionsTeam />,
|
||||
path: AppRoutes.COMPETITIONS_UPDATE_TEAM,
|
||||
element: <CompetitionsUpdateTeam />,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
"Active": "Active",
|
||||
"Activity Streak": "Activity Streak",
|
||||
"All": "All",
|
||||
"All teams": "All teams",
|
||||
"All teams are eligible": "All teams are eligible",
|
||||
"Amount earned": "Amount earned",
|
||||
"An unknown error occurred.": "An unknown error occurred.",
|
||||
"Anonymous": "Anonymous",
|
||||
@@ -85,9 +87,11 @@
|
||||
"Docs": "Docs",
|
||||
"Earn commission & stake rewards": "Earn commission & stake rewards",
|
||||
"Earned by me": "Earned by me",
|
||||
"Eligible teams": "Eligible teams",
|
||||
"Enactment date reached and usual auction exit checks pass": "Enactment date reached and usual auction exit checks pass",
|
||||
"Ends in": "Ends in",
|
||||
"Entity scope": "Entity scope",
|
||||
"{{entity}} scope": "{{entity}} scope",
|
||||
"Environment not configured": "Environment not configured",
|
||||
"Epoch": "Epoch",
|
||||
"epochs in referral set": "epochs in referral set",
|
||||
@@ -209,6 +213,7 @@
|
||||
"No third party has access to your funds.": "No third party has access to your funds.",
|
||||
"No volume discount program active": "No volume discount program active",
|
||||
"No withdrawals": "No withdrawals",
|
||||
"No. of participating members": "No. of participating members",
|
||||
"No. of participating teams": "No. of participating teams",
|
||||
"Node: {{VEGA_URL}} is unsuitable": "Node: {{VEGA_URL}} is unsuitable",
|
||||
"Non-custodial and pseudonymous": "Non-custodial and pseudonymous",
|
||||
@@ -408,7 +413,7 @@
|
||||
"myVolume_other": "My volume (last {{count}} epochs)",
|
||||
"numberEpochs": "{{count}} epochs",
|
||||
"numberEpochs_one": "{{count}} epoch",
|
||||
"Rewards paid": "Rewards paid",
|
||||
"Rewards paid out": "Rewards paid out",
|
||||
"{{reward}}x": "{{reward}}x",
|
||||
"userActive": "{{active}} trader: {{count}} epochs so far",
|
||||
"volumeLastEpochs": "Volume (last {{count}} epochs)",
|
||||
|
||||
Generated
+12
-12
@@ -1879,12 +1879,8 @@ export type LiquidityFeeSettings = {
|
||||
/** Configuration of a market liquidity monitoring parameters */
|
||||
export type LiquidityMonitoringParameters = {
|
||||
__typename?: 'LiquidityMonitoringParameters';
|
||||
/** Specifies by how many seconds an auction should be extended if leaving the auction were to trigger a liquidity auction */
|
||||
auctionExtensionSecs: Scalars['Int'];
|
||||
/** Specifies parameters related to target stake calculation */
|
||||
targetStakeParameters: TargetStakeParameters;
|
||||
/** Specifies the triggering ratio for entering liquidity auction */
|
||||
triggeringRatio: Scalars['String'];
|
||||
};
|
||||
|
||||
/** A special order type for liquidity providers */
|
||||
@@ -4002,10 +3998,10 @@ export type Perpetual = {
|
||||
fundingRateScalingFactor: Scalars['String'];
|
||||
/** Upper bound for the funding-rate such that the funding-rate will never be higher than this value */
|
||||
fundingRateUpperBound: Scalars['String'];
|
||||
/** Optional configuration driving the index price calculation for perpetual product */
|
||||
indexPriceConfig?: Maybe<CompositePriceConfiguration>;
|
||||
/** Continuously compounded interest rate used in funding rate calculation, in the range [-1, 1] */
|
||||
interestRate: Scalars['String'];
|
||||
/** Optional configuration driving the internal composite price calculation for perpetual product */
|
||||
internalCompositePriceConfig?: Maybe<CompositePriceConfiguration>;
|
||||
/** Controls how much the upcoming funding payment liability contributes to party's margin, in the range [0, 1] */
|
||||
marginFundingFactor: Scalars['String'];
|
||||
/** Quote name of the instrument */
|
||||
@@ -4023,14 +4019,14 @@ export type PerpetualData = {
|
||||
fundingPayment?: Maybe<Scalars['String']>;
|
||||
/** Percentage difference between the time-weighted average price of the external and internal data point. */
|
||||
fundingRate?: Maybe<Scalars['String']>;
|
||||
/** The index price used for external VWAP calculation */
|
||||
indexPrice: Scalars['String'];
|
||||
/** The methodology used to calculated index price for perps */
|
||||
indexPriceType: CompositePriceType;
|
||||
/** Internal composite price used as input to the internal VWAP */
|
||||
internalCompositePrice: Scalars['String'];
|
||||
/** The methodology used to calculated internal composite price for perpetual markets */
|
||||
internalCompositePriceType: CompositePriceType;
|
||||
/** Time-weighted average price calculated from data points for this period from the internal data source. */
|
||||
internalTwap?: Maybe<Scalars['String']>;
|
||||
/** RFC3339Nano time indicating the next time index price will be calculated for perps where applicable */
|
||||
nextIndexPriceCalc: Scalars['String'];
|
||||
/** RFC3339Nano time indicating the next time internal composite price will be calculated for perpetual markets, where applicable */
|
||||
nextInternalCompositePriceCalc: Scalars['String'];
|
||||
/** Funding period sequence number */
|
||||
seqNum: Scalars['Int'];
|
||||
/** Time at which the funding period started */
|
||||
@@ -5507,6 +5503,8 @@ export type ReferralSet = {
|
||||
id: Scalars['ID'];
|
||||
/** Party that created the set. */
|
||||
referrer: Scalars['ID'];
|
||||
/** Current number of members in the referral set. */
|
||||
totalMembers: Scalars['Int'];
|
||||
/** Timestamp as RFC3339Nano when the referral set was updated. */
|
||||
updatedAt: Scalars['Timestamp'];
|
||||
};
|
||||
@@ -6333,6 +6331,8 @@ export type Team = {
|
||||
teamId: Scalars['ID'];
|
||||
/** Link to the team's homepage. */
|
||||
teamUrl: Scalars['String'];
|
||||
/** Current number of members in the team. */
|
||||
totalMembers: Scalars['Int'];
|
||||
};
|
||||
|
||||
/** Connection type for retrieving cursor-based paginated team data */
|
||||
|
||||
@@ -3,6 +3,7 @@ import type {
|
||||
EntityScope,
|
||||
GovernanceTransferKind,
|
||||
GovernanceTransferType,
|
||||
IndividualScope,
|
||||
PeggedReference,
|
||||
ProposalChange,
|
||||
TransferStatus,
|
||||
@@ -700,6 +701,20 @@ export const EntityScopeLabelMapping: { [e in EntityScope]: string } = {
|
||||
ENTITY_SCOPE_TEAMS: 'Team',
|
||||
};
|
||||
|
||||
export const IndividualScopeMapping: { [e in IndividualScope]: string } = {
|
||||
INDIVIDUAL_SCOPE_ALL: 'All',
|
||||
INDIVIDUAL_SCOPE_IN_TEAM: 'In team',
|
||||
INDIVIDUAL_SCOPE_NOT_IN_TEAM: 'Not in team',
|
||||
};
|
||||
|
||||
export const IndividualScopeDescriptionMapping: {
|
||||
[e in IndividualScope]: string;
|
||||
} = {
|
||||
INDIVIDUAL_SCOPE_ALL: 'All parties are eligble',
|
||||
INDIVIDUAL_SCOPE_IN_TEAM: 'Parties in teams are eligible',
|
||||
INDIVIDUAL_SCOPE_NOT_IN_TEAM: 'Only parties not in teams are eligible',
|
||||
};
|
||||
|
||||
export enum DistributionStrategyMapping {
|
||||
/** Rewards funded using the pro-rata strategy should be distributed pro-rata by each entity's reward metric scaled by any active multipliers that party has */
|
||||
DISTRIBUTION_STRATEGY_PRO_RATA = 'Pro rata',
|
||||
|
||||
@@ -3,12 +3,14 @@ import type { ReactNode } from 'react';
|
||||
|
||||
export interface SplashProps {
|
||||
children: ReactNode;
|
||||
className?: classNames.Argument;
|
||||
}
|
||||
|
||||
export const Splash = ({ children }: SplashProps) => {
|
||||
export const Splash = ({ children, className }: SplashProps) => {
|
||||
const splashClasses = classNames(
|
||||
'w-full h-full text-xs text-center text-gray-800 dark:text-gray-200',
|
||||
'flex items-center justify-center'
|
||||
'flex items-center justify-center',
|
||||
className
|
||||
);
|
||||
return <div className={splashClasses}>{children}</div>;
|
||||
};
|
||||
|
||||
@@ -458,6 +458,20 @@ export type CreateReferralSet = {
|
||||
};
|
||||
};
|
||||
|
||||
export type UpdateReferralSet = {
|
||||
updateReferralSet: {
|
||||
id: string;
|
||||
isTeam: boolean;
|
||||
team?: {
|
||||
name: string;
|
||||
teamUrl?: string;
|
||||
avatarUrl?: string;
|
||||
closed: boolean;
|
||||
allowList: string[];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
export enum MarginMode {
|
||||
MARGIN_MODE_CROSS_MARGIN = 1,
|
||||
MARGIN_MODE_ISOLATED_MARGIN,
|
||||
@@ -489,7 +503,8 @@ export type Transaction =
|
||||
| LiquidityProvisionSubmission
|
||||
| ApplyReferralCode
|
||||
| JoinTeam
|
||||
| CreateReferralSet;
|
||||
| CreateReferralSet
|
||||
| UpdateReferralSet;
|
||||
|
||||
export const isMarginModeUpdateTransaction = (
|
||||
transaction: Transaction
|
||||
|
||||
Reference in New Issue
Block a user