Compare commits

...

6 Commits

Author SHA1 Message Date
asiaznik
7176ff63f1
fix: align headers 2024-02-15 15:48:13 +00:00
bwallacee
fb7860ec4f
chore(trading): fix test 2024-02-15 15:48:13 +00:00
asiaznik
034afe7e54
chore: add reward asset id to query 2024-02-15 15:48:13 +00:00
asiaznik
725b5f1b23
fix: remove debug id column 2024-02-15 15:48:13 +00:00
asiaznik
f50e813ed2
fix: transfer picking 2024-02-15 15:48:13 +00:00
asiaznik
80620abaff
feat(trading): game results table 2024-02-15 15:47:57 +00:00
19 changed files with 479 additions and 119 deletions

View File

@ -4,7 +4,7 @@ import { CompetitionsHeader } from '../../components/competitions/competitions-h
import { Intent, Loader, TradingButton } from '@vegaprotocol/ui-toolkit'; import { Intent, Loader, TradingButton } from '@vegaprotocol/ui-toolkit';
import { useGameCards } from '../../lib/hooks/use-game-cards'; import { useGameCards } from '../../lib/hooks/use-game-cards';
import { useCurrentEpochInfoQuery } from '../../lib/hooks/__generated__/Epoch'; import { useEpochInfoQuery } from '../../lib/hooks/__generated__/Epoch';
import { Link, useNavigate } from 'react-router-dom'; import { Link, useNavigate } from 'react-router-dom';
import { Links } from '../../lib/links'; import { Links } from '../../lib/links';
import { import {
@ -25,7 +25,7 @@ export const CompetitionsHome = () => {
usePageTitle(t('Competitions')); usePageTitle(t('Competitions'));
const { data: epochData } = useCurrentEpochInfoQuery(); const { data: epochData } = useEpochInfoQuery();
const currentEpoch = Number(epochData?.epoch.id); const currentEpoch = Number(epochData?.epoch.id);
const { data: gamesData, loading: gamesLoading } = useGameCards({ const { data: gamesData, loading: gamesLoading } = useGameCards({

View File

@ -1,12 +1,28 @@
import { useState, type ButtonHTMLAttributes } from 'react'; import { useState, type ButtonHTMLAttributes, useRef } from 'react';
import { Link, useParams } from 'react-router-dom'; import { Link, useParams } from 'react-router-dom';
import orderBy from 'lodash/orderBy'; import orderBy from 'lodash/orderBy';
import { Splash, truncateMiddle, Loader } from '@vegaprotocol/ui-toolkit'; import {
import { DispatchMetricLabels, type DispatchMetric } from '@vegaprotocol/types'; Splash,
truncateMiddle,
Loader,
Dialog,
Button,
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import {
TransferStatus,
type Asset,
type RecurringTransfer,
} from '@vegaprotocol/types';
import classNames from 'classnames'; import classNames from 'classnames';
import { useT } from '../../lib/use-t'; import { useT } from '../../lib/use-t';
import { Table } from '../../components/table'; import { Table } from '../../components/table';
import { formatNumber, getDateTimeFormat } from '@vegaprotocol/utils'; import {
addDecimalsFormatNumberQuantum,
formatNumber,
getDateTimeFormat,
} from '@vegaprotocol/utils';
import { import {
useTeam, useTeam,
type TeamStats as ITeamStats, type TeamStats as ITeamStats,
@ -27,6 +43,19 @@ import {
useGames, useGames,
areTeamGames, areTeamGames,
} from '../../lib/hooks/use-games'; } from '../../lib/hooks/use-games';
import { useEpochInfoQuery } from '../../lib/hooks/__generated__/Epoch';
import {
type EnrichedTransfer,
isScopedToTeams,
useGameCards,
} from '../../lib/hooks/use-game-cards';
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
import {
ActiveRewardCard,
DispatchMetricInfo,
} from '../../components/rewards-container/active-rewards';
import { type MarketMap, useMarketsMapProvider } from '@vegaprotocol/markets';
import format from 'date-fns/format';
export const CompetitionsTeam = () => { export const CompetitionsTeam = () => {
const t = useT(); const t = useT();
@ -49,6 +78,14 @@ const TeamPageContainer = ({ teamId }: { teamId: string | undefined }) => {
const { data: games, loading: gamesLoading } = useGames(teamId); const { data: games, loading: gamesLoading } = useGames(teamId);
const { data: epochData, loading: epochLoading } = useEpochInfoQuery();
const { data: transfersData, loading: transfersLoading } = useGameCards({
currentEpoch: Number(epochData?.epoch.id),
onlyActive: false,
});
const { data: markets } = useMarketsMapProvider();
// only show spinner on first load so when users join teams its smoother // only show spinner on first load so when users join teams its smoother
if (!data && loading) { if (!data && loading) {
return ( return (
@ -74,6 +111,9 @@ const TeamPageContainer = ({ teamId }: { teamId: string | undefined }) => {
members={members} members={members}
games={areTeamGames(games) ? games : undefined} games={areTeamGames(games) ? games : undefined}
gamesLoading={gamesLoading} gamesLoading={gamesLoading}
transfers={transfersData}
transfersLoading={epochLoading || transfersLoading}
allMarkets={markets || undefined}
refetch={refetch} refetch={refetch}
/> />
); );
@ -86,6 +126,9 @@ const TeamPage = ({
members, members,
games, games,
gamesLoading, gamesLoading,
transfers,
transfersLoading,
allMarkets,
refetch, refetch,
}: { }: {
team: TeamType; team: TeamType;
@ -94,6 +137,9 @@ const TeamPage = ({
members?: Member[]; members?: Member[];
games?: TeamGame[]; games?: TeamGame[];
gamesLoading?: boolean; gamesLoading?: boolean;
transfers?: EnrichedTransfer[];
transfersLoading?: boolean;
allMarkets?: MarketMap;
refetch: () => void; refetch: () => void;
}) => { }) => {
const t = useT(); const t = useT();
@ -141,7 +187,13 @@ const TeamPage = ({
</ToggleButton> </ToggleButton>
</div> </div>
{showGames ? ( {showGames ? (
<Games games={games} gamesLoading={gamesLoading} /> <Games
games={games}
gamesLoading={gamesLoading}
transfers={transfers}
transfersLoading={transfersLoading}
allMarkets={allMarkets}
/>
) : ( ) : (
<Members members={members} /> <Members members={members} />
)} )}
@ -153,9 +205,15 @@ const TeamPage = ({
const Games = ({ const Games = ({
games, games,
gamesLoading, gamesLoading,
transfers,
transfersLoading,
allMarkets,
}: { }: {
games?: TeamGame[]; games?: TeamGame[];
gamesLoading?: boolean; gamesLoading?: boolean;
transfers?: EnrichedTransfer[];
transfersLoading?: boolean;
allMarkets?: MarketMap;
}) => { }) => {
const t = useT(); const t = useT();
@ -171,40 +229,106 @@ const Games = ({
return <p>{t('No game results available')}</p>; return <p>{t('No game results available')}</p>;
} }
const dependable = (value: string | JSX.Element) => {
if (transfersLoading) return <Loader size="small" />;
return value;
};
return ( return (
<Table <Table
columns={[ columns={[
{ name: 'rank', displayName: t('Rank') },
{ {
name: 'epoch', name: 'epoch',
displayName: t('Epoch'), displayName: t('Epoch'),
headerClassName: 'hidden md:table-cell', },
className: 'hidden md:table-cell', {
name: 'endtime',
displayName: t('End time'),
}, },
{ name: 'type', displayName: t('Type') }, { name: 'type', displayName: t('Type') },
{ name: 'amount', displayName: t('Amount earned') }, {
name: 'asset',
displayName: t('Reward asset'),
},
{ name: 'daily', displayName: t('Daily reward amount') },
{ name: 'rank', displayName: t('Rank') },
{ name: 'amount', displayName: t('Amount earned this epoch') },
{ name: 'total', displayName: t('Cumulative amount earned') },
{ {
name: 'participatingTeams', name: 'participatingTeams',
displayName: t('No. of participating teams'), displayName: t('No. of participating teams'),
headerClassName: 'hidden md:table-cell',
className: 'hidden md:table-cell',
}, },
{ {
name: 'participatingMembers', name: 'participatingMembers',
displayName: t('No. of participating members'), displayName: t('No. of participating members'),
headerClassName: 'hidden md:table-cell',
className: 'hidden md:table-cell',
}, },
]} ].map((c) => ({ ...c, headerClassName: 'text-left' }))}
data={games.map((game) => ({ data={games.map((game) => {
rank: game.team.rank, let transfer = transfers?.find((t) => {
if (!isScopedToTeams(t)) return false;
const idMatch = t.transfer.gameId === game.id;
const metricMatch =
t.transfer.kind.dispatchStrategy?.dispatchMetric ===
game.team.rewardMetric;
const start = t.transfer.kind.startEpoch <= game.epoch;
const end = t.transfer.kind.endEpoch
? t.transfer.kind.endEpoch >= game.epoch
: true;
const rejected = t.transfer.status === TransferStatus.STATUS_REJECTED;
return idMatch && metricMatch && start && end && !rejected;
});
if (!transfer || !isScopedToTeams(transfer)) transfer = undefined;
const asset = transfer?.transfer.asset;
const dailyAmount =
asset && transfer
? addDecimalsFormatNumberQuantum(
transfer.transfer.amount,
asset.decimals,
asset.quantum
)
: '-';
const earnedAmount = asset
? addDecimalsFormatNumberQuantum(
game.team.rewardEarned,
asset.decimals,
asset.quantum
)
: '-';
const totalAmount = asset
? addDecimalsFormatNumberQuantum(
game.team.totalRewardsEarned,
asset.decimals,
asset.quantum
)
: '-';
const assetSymbol = asset ? <RewardAssetCell asset={asset} /> : '-';
return {
id: game.id,
amount: dependable(earnedAmount),
asset: dependable(assetSymbol),
daily: dependable(dailyAmount),
endtime: <EndTimeCell epoch={game.epoch} />,
epoch: game.epoch, epoch: game.epoch,
type: DispatchMetricLabels[game.team.rewardMetric as DispatchMetric],
amount: formatNumber(game.team.totalRewardsEarned),
participatingTeams: game.entities.length,
participatingMembers: game.numberOfParticipants, participatingMembers: game.numberOfParticipants,
}))} participatingTeams: game.entities.length,
noCollapse={true} rank: game.team.rank,
total: totalAmount,
// type: DispatchMetricLabels[game.team.rewardMetric as DispatchMetric],
type: dependable(
<GameTypeCell transfer={transfer} allMarkets={allMarkets} />
),
};
})}
noCollapse={false}
/> />
); );
}; };
@ -286,3 +410,126 @@ const ToggleButton = ({
/> />
); );
}; };
const EndTimeCell = ({ epoch }: { epoch?: number }) => {
const { data, loading } = useEpochInfoQuery({
variables: {
epochId: epoch ? epoch.toString() : undefined,
},
fetchPolicy: 'cache-and-network',
});
if (loading) return <Loader size="small" />;
if (data) {
return format(
new Date(data.epoch.timestamps.expiry),
'yyyy/MM/dd hh:mm:ss'
);
}
return null;
};
const RewardAssetCell = ({ asset }: { asset: Asset }) => {
const open = useAssetDetailsDialogStore((state) => state.open);
const ref = useRef<HTMLButtonElement>(null);
return (
<button
ref={ref}
onClick={(e) => {
e.preventDefault();
open(asset.id, ref.current);
}}
className="border-b border-dashed border-vega-clight-200 dark:border-vega-cdark-200 text-left text-nowrap whitespace-nowrap"
>
{asset.symbol}
</button>
);
};
const GameTypeCell = ({
transfer,
allMarkets,
}: {
transfer?: EnrichedTransfer;
allMarkets?: MarketMap;
}) => {
const [open, setOpen] = useState(false);
const ref = useRef<HTMLButtonElement>(null);
if (!transfer) return '-';
return (
<>
<ActiveRewardCardDialog
open={open}
onChange={(isOpen) => setOpen(isOpen)}
trigger={ref.current}
transfer={transfer}
allMarkets={allMarkets}
/>
<button
onClick={(e) => {
e.preventDefault();
setOpen(true);
}}
ref={ref}
className="border-b border-dashed border-vega-clight-200 dark:border-vega-cdark-200 text-left md:truncate md:max-w-[25vw]"
>
<DispatchMetricInfo transferNode={transfer} allMarkets={allMarkets} />
</button>
</>
);
};
const ActiveRewardCardDialog = ({
open,
onChange,
trigger,
transfer,
allMarkets,
}: {
open: boolean;
onChange: (isOpen: boolean) => void;
trigger?: HTMLElement | null;
transfer: EnrichedTransfer;
allMarkets?: MarketMap;
}) => {
const t = useT();
const { data } = useEpochInfoQuery();
return (
<Dialog
open={open}
title={t('Game details')}
onChange={(isOpen) => onChange(isOpen)}
icon={<VegaIcon name={VegaIconNames.INFO} />}
onCloseAutoFocus={(e) => {
/**
* This mimics radix's default behaviour that focuses the dialog's
* trigger after closing itself
*/
if (trigger) {
e.preventDefault();
trigger.focus();
}
}}
>
<div className="py-5 max-w-[454px]">
<ActiveRewardCard
transferNode={transfer}
currentEpoch={Number(data?.epoch.id)}
kind={transfer.transfer.kind as RecurringTransfer}
allMarkets={allMarkets}
/>
</div>
<div className="w-1/4">
<Button
data-testid="close-asset-details-dialog"
fill={true}
size="sm"
onClick={() => onChange(false)}
>
{t('Close')}
</Button>
</div>
</Dialog>
);
};

View File

@ -11,7 +11,7 @@ import { useEffect } from 'react';
import { useT } from '../../../lib/use-t'; import { useT } from '../../../lib/use-t';
import { matchPath, useLocation, useNavigate } from 'react-router-dom'; import { matchPath, useLocation, useNavigate } from 'react-router-dom';
import { Routes } from '../../../lib/links'; import { Routes } from '../../../lib/links';
import { useCurrentEpochInfoQuery } from '../../../lib/hooks/__generated__/Epoch'; import { useEpochInfoQuery } from '../../../lib/hooks/__generated__/Epoch';
const REFETCH_INTERVAL = 60 * 60 * 1000; // 1h const REFETCH_INTERVAL = 60 * 60 * 1000; // 1h
const NON_ELIGIBLE_REFERRAL_SET_TOAST_ID = 'non-eligible-referral-set'; const NON_ELIGIBLE_REFERRAL_SET_TOAST_ID = 'non-eligible-referral-set';
@ -23,7 +23,7 @@ const useNonEligibleReferralSet = () => {
data: epochData, data: epochData,
loading: epochLoading, loading: epochLoading,
refetch: epochRefetch, refetch: epochRefetch,
} = useCurrentEpochInfoQuery(); } = useEpochInfoQuery();
useEffect(() => { useEffect(() => {
const interval = setInterval(() => { const interval = setInterval(() => {

View File

@ -34,7 +34,7 @@ import {
} from './hooks/use-referral'; } from './hooks/use-referral';
import { ApplyCodeForm, ApplyCodeFormContainer } from './apply-code-form'; import { ApplyCodeForm, ApplyCodeFormContainer } from './apply-code-form';
import { useReferralProgram } from './hooks/use-referral-program'; import { useReferralProgram } from './hooks/use-referral-program';
import { useCurrentEpochInfoQuery } from '../../lib/hooks/__generated__/Epoch'; import { useEpochInfoQuery } from '../../lib/hooks/__generated__/Epoch';
import { QUSDTooltip } from './qusd-tooltip'; import { QUSDTooltip } from './qusd-tooltip';
import { CodeTile, StatTile, Tile } from './tile'; import { CodeTile, StatTile, Tile } from './tile';
import { areTeamGames, useGames } from '../../lib/hooks/use-games'; import { areTeamGames, useGames } from '../../lib/hooks/use-games';
@ -95,7 +95,7 @@ export const useStats = ({
program: ReturnType<typeof useReferralProgram>; program: ReturnType<typeof useReferralProgram>;
}) => { }) => {
const { benefitTiers } = program; const { benefitTiers } = program;
const { data: epochData } = useCurrentEpochInfoQuery({ const { data: epochData } = useEpochInfoQuery({
fetchPolicy: 'network-only', fetchPolicy: 'network-only',
}); });
const { data: statsData } = useReferralSetStatsQuery({ const { data: statsData } = useReferralSetStatsQuery({

View File

@ -1,49 +1,19 @@
import { type TransferNode } from '@vegaprotocol/types'; import { ActiveRewardCard } from '../rewards-container/active-rewards';
import {
ActiveRewardCard,
isActiveReward,
} from '../rewards-container/active-rewards';
import { useT } from '../../lib/use-t'; import { useT } from '../../lib/use-t';
import { useAssetsMapProvider } from '@vegaprotocol/assets'; import { type EnrichedTransfer } from '../../lib/hooks/use-game-cards';
import { useMarketsMapProvider } from '@vegaprotocol/markets'; import { useMarketsMapProvider } from '@vegaprotocol/markets';
export const GamesContainer = ({ export const GamesContainer = ({
data, data,
currentEpoch, currentEpoch,
}: { }: {
data: TransferNode[]; data: EnrichedTransfer[];
currentEpoch: number; currentEpoch: number;
}) => { }) => {
const t = useT(); const t = useT();
// Re-load markets and assets in the games container to ensure that the
// the cards are updated (not grayed out) when the user navigates to the games page
const { data: assets } = useAssetsMapProvider();
const { data: markets } = useMarketsMapProvider(); const { data: markets } = useMarketsMapProvider();
const enrichedTransfers = data if (!data || data.length === 0) {
.filter((node) => isActiveReward(node, currentEpoch))
.map((node) => {
if (node.transfer.kind.__typename !== 'RecurringTransfer') {
return node;
}
const asset =
assets &&
assets[
node.transfer.kind.dispatchStrategy?.dispatchMetricAssetId || ''
];
const marketsInScope =
node.transfer.kind.dispatchStrategy?.marketIdsInScope?.map(
(id) => markets && markets[id]
);
return { ...node, asset, markets: marketsInScope };
});
if (!enrichedTransfers || !enrichedTransfers.length) return null;
if (!enrichedTransfers || enrichedTransfers.length === 0) {
return ( return (
<p className="mb-6 text-muted"> <p className="mb-6 text-muted">
{t('There are currently no games available.')} {t('There are currently no games available.')}
@ -53,7 +23,7 @@ export const GamesContainer = ({
return ( return (
<div className="mb-12 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> <div className="mb-12 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{enrichedTransfers.map((game, i) => { {data.map((game, i) => {
// TODO: Remove `kind` prop from ActiveRewardCard // TODO: Remove `kind` prop from ActiveRewardCard
const { transfer } = game; const { transfer } = game;
if ( if (

View File

@ -73,6 +73,7 @@ query ActiveRewards(
reference reference
status status
timestamp timestamp
gameId
kind { kind {
... on RecurringTransfer { ... on RecurringTransfer {
startEpoch startEpoch

View File

@ -18,7 +18,7 @@ export type ActiveRewardsQueryVariables = Types.Exact<{
}>; }>;
export type ActiveRewardsQuery = { __typename?: 'Query', transfersConnection?: { __typename?: 'TransferConnection', edges?: Array<{ __typename?: 'TransferEdge', node: { __typename?: 'TransferNode', transfer: { __typename?: 'Transfer', amount: string, id: string, from: string, fromAccountType: Types.AccountType, to: string, toAccountType: Types.AccountType, reference?: string | null, status: Types.TransferStatus, timestamp: any, reason?: string | null, asset?: { __typename?: 'Asset', id: string, symbol: string, decimals: number, name: string, quantum: string, status: Types.AssetStatus } | null, kind: { __typename?: 'OneOffGovernanceTransfer' } | { __typename?: 'OneOffTransfer' } | { __typename?: 'RecurringGovernanceTransfer' } | { __typename?: 'RecurringTransfer', startEpoch: number, endEpoch?: number | null, dispatchStrategy?: { __typename?: 'DispatchStrategy', dispatchMetric: Types.DispatchMetric, dispatchMetricAssetId: string, marketIdsInScope?: Array<string> | null, entityScope: Types.EntityScope, individualScope?: Types.IndividualScope | null, teamScope?: Array<string | null> | null, nTopPerformers?: string | null, stakingRequirement: string, notionalTimeWeightedAveragePositionRequirement: string, windowLength: number, lockPeriod: number, distributionStrategy: Types.DistributionStrategy, rankTable?: Array<{ __typename?: 'RankTable', startRank: number, shareRatio: number } | null> | null } | null } }, fees?: Array<{ __typename?: 'TransferFee', transferId: string, amount: string, epoch: number } | null> | null } } | null> | null } | null }; export type ActiveRewardsQuery = { __typename?: 'Query', transfersConnection?: { __typename?: 'TransferConnection', edges?: Array<{ __typename?: 'TransferEdge', node: { __typename?: 'TransferNode', transfer: { __typename?: 'Transfer', amount: string, id: string, from: string, fromAccountType: Types.AccountType, to: string, toAccountType: Types.AccountType, reference?: string | null, status: Types.TransferStatus, timestamp: any, gameId?: string | null, reason?: string | null, asset?: { __typename?: 'Asset', id: string, symbol: string, decimals: number, name: string, quantum: string, status: Types.AssetStatus } | null, kind: { __typename?: 'OneOffGovernanceTransfer' } | { __typename?: 'OneOffTransfer' } | { __typename?: 'RecurringGovernanceTransfer' } | { __typename?: 'RecurringTransfer', startEpoch: number, endEpoch?: number | null, dispatchStrategy?: { __typename?: 'DispatchStrategy', dispatchMetric: Types.DispatchMetric, dispatchMetricAssetId: string, marketIdsInScope?: Array<string> | null, entityScope: Types.EntityScope, individualScope?: Types.IndividualScope | null, teamScope?: Array<string | null> | null, nTopPerformers?: string | null, stakingRequirement: string, notionalTimeWeightedAveragePositionRequirement: string, windowLength: number, lockPeriod: number, distributionStrategy: Types.DistributionStrategy, rankTable?: Array<{ __typename?: 'RankTable', startRank: number, shareRatio: number } | null> | null } | null } }, fees?: Array<{ __typename?: 'TransferFee', transferId: string, amount: string, epoch: number } | null> | null } } | null> | null } | null };
export type RewardsHistoryQueryVariables = Types.Exact<{ export type RewardsHistoryQueryVariables = Types.Exact<{
partyId: Types.Scalars['ID']; partyId: Types.Scalars['ID'];
@ -144,6 +144,7 @@ export const ActiveRewardsDocument = gql`
reference reference
status status
timestamp timestamp
gameId
kind { kind {
... on RecurringTransfer { ... on RecurringTransfer {
startEpoch startEpoch

View File

@ -251,12 +251,7 @@ const StatusIndicator = ({
); );
}; };
export const ActiveRewardCard = ({ type ActiveRewardCardProps = {
transferNode,
currentEpoch,
kind,
allMarkets,
}: {
transferNode: TransferNode & { transferNode: TransferNode & {
asset?: AssetFieldsFragment | null; asset?: AssetFieldsFragment | null;
markets?: (MarketFieldsFragment | null)[]; markets?: (MarketFieldsFragment | null)[];
@ -264,7 +259,13 @@ export const ActiveRewardCard = ({
currentEpoch: number; currentEpoch: number;
kind: RecurringTransfer; kind: RecurringTransfer;
allMarkets?: Record<string, MarketFieldsFragment | null>; allMarkets?: Record<string, MarketFieldsFragment | null>;
}) => { };
export const ActiveRewardCard = ({
transferNode,
currentEpoch,
kind,
allMarkets,
}: ActiveRewardCardProps) => {
const t = useT(); const t = useT();
const { transfer } = transferNode; const { transfer } = transferNode;
@ -485,6 +486,62 @@ export const ActiveRewardCard = ({
); );
}; };
export const DispatchMetricInfo = ({
transferNode,
allMarkets,
}: {
transferNode: ActiveRewardCardProps['transferNode'];
allMarkets?: ActiveRewardCardProps['allMarkets'];
}) => {
const dispatchStrategy =
transferNode.transfer.kind.__typename === 'RecurringTransfer'
? transferNode.transfer.kind.dispatchStrategy
: null;
const dispatchAsset = transferNode.transfer.asset;
const marketIdsInScope = dispatchStrategy?.marketIdsInScope;
const firstMarketData = transferNode.markets?.[0];
const specificMarkets = useMemo(() => {
if (
!firstMarketData ||
!marketIdsInScope ||
marketIdsInScope.length === 0
) {
return null;
}
if (marketIdsInScope.length > 1) {
const marketNames =
allMarkets &&
marketIdsInScope
.map((id) => allMarkets[id]?.tradableInstrument?.instrument?.name)
.join(', ');
return (
<Tooltip description={marketNames}>
<span>Specific markets</span>
</Tooltip>
);
}
const name = firstMarketData?.tradableInstrument?.instrument?.name;
if (name) {
return <span>{name}</span>;
}
return null;
}, [firstMarketData, marketIdsInScope, allMarkets]);
if (!dispatchStrategy) return null;
return (
<span data-testid="dispatch-metric-info">
{DispatchMetricLabels[dispatchStrategy.dispatchMetric]} {' '}
<span>{specificMarkets || dispatchAsset?.name}</span>
</span>
);
};
const RewardRequirements = ({ const RewardRequirements = ({
dispatchStrategy, dispatchStrategy,
assetDecimalPlaces = 0, assetDecimalPlaces = 0,

View File

@ -93,6 +93,9 @@ export const Table = forwardRef<
key={i} key={i}
className={classNames(dataEntry['className'] as string, { className={classNames(dataEntry['className'] as string, {
'max-md:flex flex-col w-full': !noCollapse, 'max-md:flex flex-col w-full': !noCollapse,
// collapsed (mobile) row divider
'first:border-t-0 max-md:border-t border-vega-clight-500 dark:border-vega-cdark-500 first:mt-0 mt-1':
!noCollapse,
})} })}
onClick={() => { onClick={() => {
if (onRowClick) { if (onRowClick) {

View File

@ -8,10 +8,13 @@ from fixtures.market import setup_continuous_market
from conftest import auth_setup, init_page, init_vega, risk_accepted_setup 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 from wallet_config import PARTY_A, PARTY_B, PARTY_C, PARTY_D, MM_WALLET
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def vega(request): def vega(request):
with init_vega(request) as vega_instance: with init_vega(request) as vega_instance:
request.addfinalizer(lambda: cleanup_container(vega_instance)) # Register the cleanup function request.addfinalizer(
lambda: cleanup_container(vega_instance)
) # Register the cleanup function
yield vega_instance yield vega_instance
@ -125,7 +128,7 @@ def setup_teams_and_games(vega: VegaServiceNull):
n_top_performers=1, n_top_performers=1,
amount=100, amount=100,
factor=1.0, factor=1.0,
window_length=15 window_length=15,
) )
vega.wait_fn(1) vega.wait_fn(1)
vega.wait_for_total_catchup() vega.wait_for_total_catchup()
@ -213,13 +216,13 @@ def create_team(vega: VegaServiceNull):
def test_team_page_games_table(team_page: Page): def test_team_page_games_table(team_page: Page):
team_page.pause()
team_page.get_by_test_id("games-toggle").click() team_page.get_by_test_id("games-toggle").click()
expect(team_page.get_by_test_id("games-toggle")).to_have_text("Results (10)") expect(team_page.get_by_test_id("games-toggle")).to_have_text("Results (10)")
expect(team_page.get_by_test_id("rank-0")).to_have_text("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("19") expect(team_page.get_by_test_id("epoch-0")).to_have_text("19")
expect(team_page.get_by_test_id("type-0") expect(team_page.get_by_test_id("type-0")).to_have_text(
).to_have_text("Price maker fees paid") "Price maker fees paid • tDAI "
)
# TODO skipped as the amount is wrong # TODO skipped as the amount is wrong
# expect(team_page.get_by_test_id("amount-0")).to_have_text("74") # 50,000,000 on 74.1 # expect(team_page.get_by_test_id("amount-0")).to_have_text("74") # 50,000,000 on 74.1
expect(team_page.get_by_test_id("participatingTeams-0")).to_have_text("2") expect(team_page.get_by_test_id("participatingTeams-0")).to_have_text("2")
@ -228,8 +231,7 @@ def test_team_page_games_table(team_page: Page):
def test_team_page_members_table(team_page: Page): def test_team_page_members_table(team_page: Page):
team_page.get_by_test_id("members-toggle").click() team_page.get_by_test_id("members-toggle").click()
expect(team_page.get_by_test_id("members-toggle") expect(team_page.get_by_test_id("members-toggle")).to_have_text("Members (4)")
).to_have_text("Members (4)")
expect(team_page.get_by_test_id("referee-0")).to_be_visible() 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("joinedAt-0")).to_be_visible()
expect(team_page.get_by_test_id("joinedAtEpoch-0")).to_have_text("9") expect(team_page.get_by_test_id("joinedAtEpoch-0")).to_have_text("9")
@ -267,8 +269,7 @@ def test_leaderboard(competitions_page: Page, setup_teams_and_games):
competitions_page.get_by_test_id("rank-0").locator(".text-yellow-300") competitions_page.get_by_test_id("rank-0").locator(".text-yellow-300")
).to_have_count(1) ).to_have_count(1)
expect( expect(
competitions_page.get_by_test_id( competitions_page.get_by_test_id("rank-1").locator(".text-vega-clight-500")
"rank-1").locator(".text-vega-clight-500")
).to_have_count(1) ).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("team-0")).to_have_text(team_name)
expect(competitions_page.get_by_test_id("status-1")).to_have_text("Open") expect(competitions_page.get_by_test_id("status-1")).to_have_text("Open")
@ -282,17 +283,16 @@ def test_leaderboard(competitions_page: Page, setup_teams_and_games):
def test_game_card(competitions_page: Page): def test_game_card(competitions_page: Page):
expect(competitions_page.get_by_test_id( expect(competitions_page.get_by_test_id("active-rewards-card")).to_have_count(2)
"active-rewards-card")).to_have_count(2)
game_1 = competitions_page.get_by_test_id("active-rewards-card").first game_1 = competitions_page.get_by_test_id("active-rewards-card").first
expect(game_1).to_be_visible() expect(game_1).to_be_visible()
expect(game_1.get_by_test_id("entity-scope")).to_have_text("Individual") expect(game_1.get_by_test_id("entity-scope")).to_have_text("Individual")
expect(game_1.get_by_test_id("locked-for")).to_have_text("1 epoch") expect(game_1.get_by_test_id("locked-for")).to_have_text("1 epoch")
expect(game_1.get_by_test_id("reward-value")).to_have_text("100.00") expect(game_1.get_by_test_id("reward-value")).to_have_text("100.00")
expect(game_1.get_by_test_id("distribution-strategy") expect(game_1.get_by_test_id("distribution-strategy")).to_have_text("Pro rata")
).to_have_text("Pro rata") expect(game_1.get_by_test_id("dispatch-metric-info")).to_have_text(
expect(game_1.get_by_test_id("dispatch-metric-info") "Price maker fees paid • tDAI"
).to_have_text("Price maker fees paid • tDAI") )
expect(game_1.get_by_test_id("assessed-over")).to_have_text("15 epochs") expect(game_1.get_by_test_id("assessed-over")).to_have_text("15 epochs")
expect(game_1.get_by_test_id("scope")).to_have_text("In team") expect(game_1.get_by_test_id("scope")).to_have_text("In team")
expect(game_1.get_by_test_id("staking-requirement")).to_have_text("0.00") expect(game_1.get_by_test_id("staking-requirement")).to_have_text("0.00")

View File

@ -1,9 +1,10 @@
query CurrentEpochInfo { query EpochInfo($epochId: ID) {
epoch { epoch(id: $epochId) {
id id
timestamps { timestamps {
start start
end end
expiry
} }
} }
} }

View File

@ -17,6 +17,7 @@ fragment GameFields on Game {
id id
epoch epoch
numberOfParticipants numberOfParticipants
rewardAssetId
entities { entities {
... on TeamGameEntity { ... on TeamGameEntity {
...TeamEntity ...TeamEntity

View File

@ -3,47 +3,51 @@ import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client'; import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client'; import * as Apollo from '@apollo/client';
const defaultOptions = {} as const; const defaultOptions = {} as const;
export type CurrentEpochInfoQueryVariables = Types.Exact<{ [key: string]: never; }>; export type EpochInfoQueryVariables = Types.Exact<{
epochId?: Types.InputMaybe<Types.Scalars['ID']>;
}>;
export type CurrentEpochInfoQuery = { __typename?: 'Query', epoch: { __typename?: 'Epoch', id: string, timestamps: { __typename?: 'EpochTimestamps', start?: any | null, end?: any | null } } }; export type EpochInfoQuery = { __typename?: 'Query', epoch: { __typename?: 'Epoch', id: string, timestamps: { __typename?: 'EpochTimestamps', start?: any | null, end?: any | null, expiry?: any | null } } };
export const CurrentEpochInfoDocument = gql` export const EpochInfoDocument = gql`
query CurrentEpochInfo { query EpochInfo($epochId: ID) {
epoch { epoch(id: $epochId) {
id id
timestamps { timestamps {
start start
end end
expiry
} }
} }
} }
`; `;
/** /**
* __useCurrentEpochInfoQuery__ * __useEpochInfoQuery__
* *
* To run a query within a React component, call `useCurrentEpochInfoQuery` and pass it any options that fit your needs. * To run a query within a React component, call `useEpochInfoQuery` and pass it any options that fit your needs.
* When your component renders, `useCurrentEpochInfoQuery` returns an object from Apollo Client that contains loading, error, and data properties * When your component renders, `useEpochInfoQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI. * 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; * @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 * @example
* const { data, loading, error } = useCurrentEpochInfoQuery({ * const { data, loading, error } = useEpochInfoQuery({
* variables: { * variables: {
* epochId: // value for 'epochId'
* }, * },
* }); * });
*/ */
export function useCurrentEpochInfoQuery(baseOptions?: Apollo.QueryHookOptions<CurrentEpochInfoQuery, CurrentEpochInfoQueryVariables>) { export function useEpochInfoQuery(baseOptions?: Apollo.QueryHookOptions<EpochInfoQuery, EpochInfoQueryVariables>) {
const options = {...defaultOptions, ...baseOptions} const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<CurrentEpochInfoQuery, CurrentEpochInfoQueryVariables>(CurrentEpochInfoDocument, options); return Apollo.useQuery<EpochInfoQuery, EpochInfoQueryVariables>(EpochInfoDocument, options);
} }
export function useCurrentEpochInfoLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<CurrentEpochInfoQuery, CurrentEpochInfoQueryVariables>) { export function useEpochInfoLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<EpochInfoQuery, EpochInfoQueryVariables>) {
const options = {...defaultOptions, ...baseOptions} const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<CurrentEpochInfoQuery, CurrentEpochInfoQueryVariables>(CurrentEpochInfoDocument, options); return Apollo.useLazyQuery<EpochInfoQuery, EpochInfoQueryVariables>(EpochInfoDocument, options);
} }
export type CurrentEpochInfoQueryHookResult = ReturnType<typeof useCurrentEpochInfoQuery>; export type EpochInfoQueryHookResult = ReturnType<typeof useEpochInfoQuery>;
export type CurrentEpochInfoLazyQueryHookResult = ReturnType<typeof useCurrentEpochInfoLazyQuery>; export type EpochInfoLazyQueryHookResult = ReturnType<typeof useEpochInfoLazyQuery>;
export type CurrentEpochInfoQueryResult = Apollo.QueryResult<CurrentEpochInfoQuery, CurrentEpochInfoQueryVariables>; export type EpochInfoQueryResult = Apollo.QueryResult<EpochInfoQuery, EpochInfoQueryVariables>;

View File

@ -5,14 +5,14 @@ import * as Apollo from '@apollo/client';
const defaultOptions = {} as const; const defaultOptions = {} as const;
export type TeamEntityFragment = { __typename?: 'TeamGameEntity', rank: number, volume: string, rewardMetric: Types.DispatchMetric, rewardEarned: string, totalRewardsEarned: string, team: { __typename?: 'TeamParticipation', teamId: string, membersParticipating: Array<{ __typename?: 'IndividualGameEntity', individual: string, rank: number }> } }; export type TeamEntityFragment = { __typename?: 'TeamGameEntity', rank: number, volume: string, rewardMetric: Types.DispatchMetric, rewardEarned: string, totalRewardsEarned: string, team: { __typename?: 'TeamParticipation', teamId: string, membersParticipating: Array<{ __typename?: 'IndividualGameEntity', individual: string, rank: number }> } };
export type GameFieldsFragment = { __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, membersParticipating: Array<{ __typename?: 'IndividualGameEntity', individual: string, rank: number }> } }> }; export type GameFieldsFragment = { __typename?: 'Game', id: string, epoch: number, numberOfParticipants: number, rewardAssetId: string, entities: Array<{ __typename?: 'IndividualGameEntity' } | { __typename?: 'TeamGameEntity', rank: number, volume: string, rewardMetric: Types.DispatchMetric, rewardEarned: string, totalRewardsEarned: string, team: { __typename?: 'TeamParticipation', teamId: string, membersParticipating: Array<{ __typename?: 'IndividualGameEntity', individual: string, rank: number }> } }> };
export type GamesQueryVariables = Types.Exact<{ export type GamesQueryVariables = Types.Exact<{
epochFrom?: Types.InputMaybe<Types.Scalars['Int']>; epochFrom?: Types.InputMaybe<Types.Scalars['Int']>;
}>; }>;
export type GamesQuery = { __typename?: 'Query', 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, membersParticipating: Array<{ __typename?: 'IndividualGameEntity', individual: string, rank: number }> } }> } } | null> | null } }; export type GamesQuery = { __typename?: 'Query', games: { __typename?: 'GamesConnection', edges?: Array<{ __typename?: 'GameEdge', node: { __typename?: 'Game', id: string, epoch: number, numberOfParticipants: number, rewardAssetId: string, entities: Array<{ __typename?: 'IndividualGameEntity' } | { __typename?: 'TeamGameEntity', rank: number, volume: string, rewardMetric: Types.DispatchMetric, rewardEarned: string, totalRewardsEarned: string, team: { __typename?: 'TeamParticipation', teamId: string, membersParticipating: Array<{ __typename?: 'IndividualGameEntity', individual: string, rank: number }> } }> } } | null> | null } };
export const TeamEntityFragmentDoc = gql` export const TeamEntityFragmentDoc = gql`
fragment TeamEntity on TeamGameEntity { fragment TeamEntity on TeamGameEntity {
@ -35,6 +35,7 @@ export const GameFieldsFragmentDoc = gql`
id id
epoch epoch
numberOfParticipants numberOfParticipants
rewardAssetId
entities { entities {
... on TeamGameEntity { ... on TeamGameEntity {
...TeamEntity ...TeamEntity

View File

@ -2,12 +2,35 @@ import compact from 'lodash/compact';
import { useActiveRewardsQuery } from '../../components/rewards-container/__generated__/Rewards'; import { useActiveRewardsQuery } from '../../components/rewards-container/__generated__/Rewards';
import { isActiveReward } from '../../components/rewards-container/active-rewards'; import { isActiveReward } from '../../components/rewards-container/active-rewards';
import { import {
type RecurringTransfer,
type TransferNode,
EntityScope, EntityScope,
IndividualScope, IndividualScope,
type TransferNode,
} from '@vegaprotocol/types'; } from '@vegaprotocol/types';
import {
type AssetFieldsFragment,
useAssetsMapProvider,
} from '@vegaprotocol/assets';
import {
type MarketFieldsFragment,
useMarketsMapProvider,
} from '@vegaprotocol/markets';
import { type ApolloError } from '@apollo/client';
const isScopedToTeams = (node: TransferNode) => export type EnrichedTransfer = TransferNode & {
asset?: AssetFieldsFragment | null;
markets?: (MarketFieldsFragment | null)[];
};
type RecurringTransferKind = EnrichedTransfer & {
transfer: {
kind: RecurringTransfer;
};
};
export const isScopedToTeams = (
node: TransferNode
): node is RecurringTransferKind =>
node.transfer.kind.__typename === 'RecurringTransfer' && node.transfer.kind.__typename === 'RecurringTransfer' &&
// scoped to teams // scoped to teams
(node.transfer.kind.dispatchStrategy?.entityScope === (node.transfer.kind.dispatchStrategy?.entityScope ===
@ -25,7 +48,7 @@ export const useGameCards = ({
}: { }: {
currentEpoch: number; currentEpoch: number;
onlyActive: boolean; onlyActive: boolean;
}) => { }): { data: EnrichedTransfer[]; loading: boolean; error?: ApolloError } => {
const { data, loading, error } = useActiveRewardsQuery({ const { data, loading, error } = useActiveRewardsQuery({
variables: { variables: {
isReward: true, isReward: true,
@ -33,16 +56,37 @@ export const useGameCards = ({
fetchPolicy: 'cache-and-network', fetchPolicy: 'cache-and-network',
}); });
const { data: assets, loading: assetsLoading } = useAssetsMapProvider();
const { data: markets, loading: marketsLoading } = useMarketsMapProvider();
const games = compact(data?.transfersConnection?.edges?.map((n) => n?.node)) const games = compact(data?.transfersConnection?.edges?.map((n) => n?.node))
.map((n) => n as TransferNode) .map((n) => n as TransferNode)
.filter((node) => { .filter((node) => {
const active = onlyActive ? isActiveReward(node, currentEpoch) : true; const active = onlyActive ? isActiveReward(node, currentEpoch) : true;
return active && isScopedToTeams(node); return active && isScopedToTeams(node);
})
.map((node) => {
if (node.transfer.kind.__typename !== 'RecurringTransfer') {
return node;
}
const asset =
assets &&
assets[
node.transfer.kind.dispatchStrategy?.dispatchMetricAssetId || ''
];
const marketsInScope =
node.transfer.kind.dispatchStrategy?.marketIdsInScope?.map(
(id) => markets && markets[id]
);
return { ...node, asset, markets: marketsInScope };
}); });
return { return {
data: games, data: games,
loading, loading: loading || assetsLoading || marketsLoading,
error, error,
}; };
}; };

View File

@ -5,7 +5,7 @@ import {
} from './__generated__/Games'; } from './__generated__/Games';
import orderBy from 'lodash/orderBy'; import orderBy from 'lodash/orderBy';
import { removePaginationWrapper } from '@vegaprotocol/utils'; import { removePaginationWrapper } from '@vegaprotocol/utils';
import { useCurrentEpochInfoQuery } from './__generated__/Epoch'; import { useEpochInfoQuery } from './__generated__/Epoch';
import { type ApolloError } from '@apollo/client'; import { type ApolloError } from '@apollo/client';
const TAKE_EPOCHS = 30; // TODO: should this be DEFAULT_AGGREGATION_EPOCHS? const TAKE_EPOCHS = 30; // TODO: should this be DEFAULT_AGGREGATION_EPOCHS?
@ -39,7 +39,7 @@ export const useGames = (teamId?: string, epochFrom?: number): GamesData => {
data: epochData, data: epochData,
loading: epochLoading, loading: epochLoading,
error: epochError, error: epochError,
} = useCurrentEpochInfoQuery({ } = useEpochInfoQuery({
skip: Boolean(epochFrom), skip: Boolean(epochFrom),
}); });

View File

@ -455,5 +455,12 @@
"Go back to the team's profile": "Go back to the team's profile", "Go back to the team's profile": "Go back to the team's profile",
"Go back to the competitions": "Go back to the competitions", "Go back to the competitions": "Go back to the competitions",
"Your team ID:": "Your team ID:", "Your team ID:": "Your team ID:",
"Changes successfully saved to your team.": "Changes successfully saved to your team." "Changes successfully saved to your team.": "Changes successfully saved to your team.",
"Results {{games}}": "Results {{games}}",
"End time": "End time",
"Reward asset": "Reward asset",
"Daily reward amount": "Daily reward amount",
"Amount earned this epoch": "Amount earned this epoch",
"Cumulative amount earned": "Cumulative amount earned",
"Game details": "Game details"
} }

View File

@ -47,8 +47,9 @@ export const marketsProvider = makeDataProvider<
errorPolicy: 'all', errorPolicy: 'all',
}); });
export type MarketMap = Record<string, Market>;
export const marketsMapProvider = makeDerivedDataProvider< export const marketsMapProvider = makeDerivedDataProvider<
Record<string, Market>, MarketMap,
never, never,
undefined undefined
>( >(
@ -59,7 +60,7 @@ export const marketsMapProvider = makeDerivedDataProvider<
markets[market.id] = market; markets[market.id] = market;
return markets; return markets;
}, },
{} as Record<string, Market> {} as MarketMap
); );
} }
); );

View File

@ -1640,6 +1640,8 @@ export type Game = {
id: Scalars['ID']; id: Scalars['ID'];
/** Number of participants that took part in the game during the epoch. */ /** Number of participants that took part in the game during the epoch. */
numberOfParticipants: Scalars['Int']; numberOfParticipants: Scalars['Int'];
/** ID of asset in which the rewards were paid. */
rewardAssetId: Scalars['ID'];
}; };
/** Edge type containing the game metrics and cursor information returned by a GameConnection */ /** Edge type containing the game metrics and cursor information returned by a GameConnection */
@ -1704,10 +1706,14 @@ export type IndividualGameEntity = {
rank: Scalars['Int']; rank: Scalars['Int'];
/** The rewards earned by the individual during the epoch */ /** The rewards earned by the individual during the epoch */
rewardEarned: Scalars['String']; rewardEarned: Scalars['String'];
/** The rewards earned by the individual during the epoch in quantum value */
rewardEarnedQuantum: Scalars['String'];
/** The reward metric applied to the game */ /** The reward metric applied to the game */
rewardMetric: DispatchMetric; rewardMetric: DispatchMetric;
/** Total rewards earned by the individual during the game */ /** Total rewards earned by the individual during the game */
totalRewardsEarned: Scalars['String']; totalRewardsEarned: Scalars['String'];
/** Total rewards earned by the individual during the game in quantum value */
totalRewardsEarnedQuantum: Scalars['String'];
/** The volume traded by the individual */ /** The volume traded by the individual */
volume: Scalars['String']; volume: Scalars['String'];
}; };
@ -4737,6 +4743,14 @@ export type QuantumRewardsPerEpoch = {
totalQuantumRewards: Scalars['String']; totalQuantumRewards: Scalars['String'];
}; };
export type QuantumVolumesPerEpoch = {
__typename?: 'QuantumVolumesPerEpoch';
/** Epoch for which this information is valid. */
epoch: Scalars['Int'];
/** Total volume across all markets, accumulated over the epoch period, expressed in quantum value. */
totalQuantumVolumes: Scalars['String'];
};
/** Queries allow a caller to read data and filter data via GraphQL. */ /** Queries allow a caller to read data and filter data via GraphQL. */
export type Query = { export type Query = {
__typename?: 'Query'; __typename?: 'Query';
@ -6412,12 +6426,16 @@ export type TeamGameEntity = {
rank: Scalars['Int']; rank: Scalars['Int'];
/** Total rewards earned by the team during the epoch */ /** Total rewards earned by the team during the epoch */
rewardEarned: Scalars['String']; rewardEarned: Scalars['String'];
/** Total rewards earned by the team during the epoch in quantum value */
rewardEarnedQuantum: Scalars['String'];
/** Reward metric applied to the game. */ /** Reward metric applied to the game. */
rewardMetric: DispatchMetric; rewardMetric: DispatchMetric;
/** Breakdown of the team members and their contributions to the total team metrics. */ /** Breakdown of the team members and their contributions to the total team metrics. */
team: TeamParticipation; team: TeamParticipation;
/** Total rewards earned by the team for the game */ /** Total rewards earned by the team for the game */
totalRewardsEarned: Scalars['String']; totalRewardsEarned: Scalars['String'];
/** Total rewards earned by the team for the game in quantum value */
totalRewardsEarnedQuantum: Scalars['String'];
/** Total volume traded by the team */ /** Total volume traded by the team */
volume: Scalars['String']; volume: Scalars['String'];
}; };
@ -6431,6 +6449,8 @@ export type TeamMemberStatistics = {
partyId: Scalars['String']; partyId: Scalars['String'];
/** List of rewards over the requested epoch period, expressed in quantum value for each epoch */ /** List of rewards over the requested epoch period, expressed in quantum value for each epoch */
quantumRewards: Array<QuantumRewardsPerEpoch>; quantumRewards: Array<QuantumRewardsPerEpoch>;
/** List of trading volume totals per epoch, for the requested epoch period, expressed in quantum value */
quantumVolumes: Array<QuantumVolumesPerEpoch>;
/** Total number of games played. */ /** Total number of games played. */
totalGamesPlayed: Scalars['Int']; totalGamesPlayed: Scalars['Int'];
/** Total of rewards accumulated over the requested epoch period, expressed in quantum value. */ /** Total of rewards accumulated over the requested epoch period, expressed in quantum value. */
@ -6533,6 +6553,8 @@ export type TeamStatistics = {
gamesPlayed: Array<Scalars['String']>; gamesPlayed: Array<Scalars['String']>;
/** List of rewards over the requested epoch period, expressed in quantum value for each epoch */ /** List of rewards over the requested epoch period, expressed in quantum value for each epoch */
quantumRewards: Array<QuantumRewardsPerEpoch>; quantumRewards: Array<QuantumRewardsPerEpoch>;
/** List of trading volume totals per epoch, over the requested epoch period, expressed in quantum value */
quantumVolumes: Array<QuantumVolumesPerEpoch>;
/** Team ID the statistics are related to. */ /** Team ID the statistics are related to. */
teamId: Scalars['String']; teamId: Scalars['String'];
/** Total of games played. */ /** Total of games played. */