Compare commits
32
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4733bc169c | ||
|
|
2b91aebc2a | ||
|
|
375f4da541 | ||
|
|
bc413ed314 | ||
|
|
2fab3daebd | ||
|
|
2e07ada966 | ||
|
|
b81c4bc948 | ||
|
|
4f8d6bd876 | ||
|
|
52e5a37da3 | ||
|
|
22599673ec | ||
|
|
3c6a806ad3 | ||
|
|
7101d49d1d | ||
|
|
72e0cb76aa | ||
|
|
ca64516a52 | ||
|
|
55d692ea6f | ||
|
|
f235c03abe | ||
|
|
546deb0e1c | ||
|
|
042919eca9 | ||
|
|
c4a56e0de3 | ||
|
|
3c3bfb7dac | ||
|
|
196ba78806 | ||
|
|
53ac2dadee | ||
|
|
e532f88daa | ||
|
|
7b06c05853 | ||
|
|
a92fe92778 | ||
|
|
b8725a7fa8 | ||
|
|
f556247e1a | ||
|
|
48d6be0adf | ||
|
|
be6f395ce4 | ||
|
|
9a37572f51 | ||
|
|
19fb406d49 | ||
|
|
a2a04c57d2 |
@@ -25,7 +25,7 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
rm package.json
|
rm package.json
|
||||||
npm install --no-save @commitlint/cli @commitlint/config-conventional @commitlint/config-nx-scopes nx
|
npm install --no-save @commitlint/cli@16.3.0 @commitlint/config-conventional@18.6.1 @commitlint/config-nx-scopes@18.6.1 nx@17.1.2
|
||||||
|
|
||||||
- name: Check PR title
|
- name: Check PR title
|
||||||
run: echo "${{ github.event.pull_request.title }}" | npx commitlint --config ./commitlint.config-ci.js
|
run: echo "${{ github.event.pull_request.title }}" | npx @commitlint/cli@16.3.0 --config ./commitlint.config-ci.js
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { getNewAssetTxBody } from '../support/governance.functions';
|
import { getNewAssetTxBody } from '../support/governance.functions';
|
||||||
|
|
||||||
context('Proposal page', { tags: '@smoke' }, function () {
|
context('Proposal page', { tags: '@smoke' }, function () {
|
||||||
describe('Verify elements on page', function () {
|
describe.skip('Verify elements on page', function () {
|
||||||
const proposalHeading = 'proposals-heading';
|
const proposalHeading = 'proposals-heading';
|
||||||
const dateTimeRegex =
|
const dateTimeRegex =
|
||||||
/(\d{1,2})\/(\d{1,2})\/(\d{4}), (\d{1,2}):(\d{1,2}):(\d{1,2})/gm;
|
/(\d{1,2})\/(\d{1,2})\/(\d{4}), (\d{1,2}):(\d{1,2}):(\d{1,2})/gm;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ export type AssetBalanceProps = {
|
|||||||
price: string;
|
price: string;
|
||||||
showAssetLink?: boolean;
|
showAssetLink?: boolean;
|
||||||
showAssetSymbol?: boolean;
|
showAssetSymbol?: boolean;
|
||||||
|
rounded?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -18,12 +19,17 @@ const AssetBalance = ({
|
|||||||
price,
|
price,
|
||||||
showAssetLink = true,
|
showAssetLink = true,
|
||||||
showAssetSymbol = false,
|
showAssetSymbol = false,
|
||||||
|
rounded = false,
|
||||||
}: AssetBalanceProps) => {
|
}: AssetBalanceProps) => {
|
||||||
const { data: asset, loading } = useAssetDataProvider(assetId);
|
const { data: asset, loading } = useAssetDataProvider(assetId);
|
||||||
|
|
||||||
const label =
|
const label =
|
||||||
!loading && asset && asset.decimals
|
!loading && asset && asset.decimals
|
||||||
? addDecimalsFixedFormatNumber(price, asset.decimals)
|
? addDecimalsFixedFormatNumber(
|
||||||
|
price,
|
||||||
|
asset.decimals,
|
||||||
|
rounded ? 0 : undefined
|
||||||
|
)
|
||||||
: price;
|
: price;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ export const Header = () => {
|
|||||||
Routes.ASSETS,
|
Routes.ASSETS,
|
||||||
Routes.MARKETS,
|
Routes.MARKETS,
|
||||||
Routes.GOVERNANCE,
|
Routes.GOVERNANCE,
|
||||||
|
Routes.TREASURY,
|
||||||
Routes.NETWORK_PARAMETERS,
|
Routes.NETWORK_PARAMETERS,
|
||||||
Routes.GENESIS,
|
Routes.GENESIS,
|
||||||
].map((n) => pages.find((r) => r.path === n))
|
].map((n) => pages.find((r) => r.path === n))
|
||||||
|
|||||||
@@ -32,9 +32,17 @@ export function getNameForParty(id: string, data?: ExplorerNodeNamesQuery) {
|
|||||||
export type PartyLinkProps = Partial<ComponentProps<typeof Link>> & {
|
export type PartyLinkProps = Partial<ComponentProps<typeof Link>> & {
|
||||||
id: string;
|
id: string;
|
||||||
truncate?: boolean;
|
truncate?: boolean;
|
||||||
|
networkLabel?: string;
|
||||||
|
truncateLength?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
const PartyLink = ({ id, truncate = false, ...props }: PartyLinkProps) => {
|
const PartyLink = ({
|
||||||
|
id,
|
||||||
|
truncate = false,
|
||||||
|
truncateLength = 4,
|
||||||
|
networkLabel = t('Network'),
|
||||||
|
...props
|
||||||
|
}: PartyLinkProps) => {
|
||||||
const { data } = useExplorerNodeNamesQuery();
|
const { data } = useExplorerNodeNamesQuery();
|
||||||
const name = useMemo(() => getNameForParty(id, data), [data, id]);
|
const name = useMemo(() => getNameForParty(id, data), [data, id]);
|
||||||
const useName = name !== id;
|
const useName = name !== id;
|
||||||
@@ -44,7 +52,7 @@ const PartyLink = ({ id, truncate = false, ...props }: PartyLinkProps) => {
|
|||||||
if (id === SPECIAL_CASE_NETWORK || id === SPECIAL_CASE_NETWORK_ID) {
|
if (id === SPECIAL_CASE_NETWORK || id === SPECIAL_CASE_NETWORK_ID) {
|
||||||
return (
|
return (
|
||||||
<span className="font-mono" data-testid="network">
|
<span className="font-mono" data-testid="network">
|
||||||
{t('Network')}
|
{networkLabel}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -70,7 +78,11 @@ const PartyLink = ({ id, truncate = false, ...props }: PartyLinkProps) => {
|
|||||||
{useName ? (
|
{useName ? (
|
||||||
name
|
name
|
||||||
) : (
|
) : (
|
||||||
<Hash text={truncate ? truncateMiddle(id, 4, 4) : id} />
|
<Hash
|
||||||
|
text={
|
||||||
|
truncate ? truncateMiddle(id, truncateLength, truncateLength) : id
|
||||||
|
}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</Link>
|
</Link>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -175,6 +175,7 @@ describe('Amend order details', () => {
|
|||||||
|
|
||||||
const res = renderExistingAmend('123', 1, amend);
|
const res = renderExistingAmend('123', 1, amend);
|
||||||
expect(await res.findByText('New size')).toBeInTheDocument();
|
expect(await res.findByText('New size')).toBeInTheDocument();
|
||||||
|
expect(await res.findByText('Size ±')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Renders Reference if provided', async () => {
|
it('Renders Reference if provided', async () => {
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ const AmendOrderDetails = ({ id, version, amend }: AmendOrderDetailsProps) => {
|
|||||||
{amend.sizeDelta && amend.sizeDelta !== '0' ? (
|
{amend.sizeDelta && amend.sizeDelta !== '0' ? (
|
||||||
<div className="mb-12 md:mb-0">
|
<div className="mb-12 md:mb-0">
|
||||||
<h2 className="text-dark mb-4 text-2xl font-bold">
|
<h2 className="text-dark mb-4 text-2xl font-bold">
|
||||||
{t('New size')}
|
{t('Size ±')}
|
||||||
</h2>
|
</h2>
|
||||||
<h5
|
<h5
|
||||||
className={`mb-0 text-lg font-medium capitalize text-gray-500 ${getSideDeltaColour(
|
className={`mb-0 text-lg font-medium capitalize text-gray-500 ${getSideDeltaColour(
|
||||||
@@ -93,6 +93,16 @@ const AmendOrderDetails = ({ id, version, amend }: AmendOrderDetailsProps) => {
|
|||||||
</h5>
|
</h5>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
{o && (
|
||||||
|
<div className="">
|
||||||
|
<h2 className="text-dark mb-4 text-2xl font-bold">
|
||||||
|
{t('New size')}
|
||||||
|
</h2>
|
||||||
|
<h5 className="mb-0 text-lg font-medium text-gray-500">
|
||||||
|
{o ? o.size : null}
|
||||||
|
</h5>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{amend.price && amend.price !== '0' ? (
|
{amend.price && amend.price !== '0' ? (
|
||||||
<div className="">
|
<div className="">
|
||||||
|
|||||||
@@ -34,10 +34,7 @@ export function TransferStatusView({ status, loading }: TransferStatusProps) {
|
|||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<p className="leading-10 my-2">
|
<p className="leading-10 my-2">
|
||||||
<Icon
|
<TransferStatusIcon status={status} />
|
||||||
name={getIconForStatus(status)}
|
|
||||||
className={getColourForStatus(status)}
|
|
||||||
/>
|
|
||||||
</p>
|
</p>
|
||||||
<p className="leading-10 my-2">{TransferStatusMapping[status]}</p>
|
<p className="leading-10 my-2">{TransferStatusMapping[status]}</p>
|
||||||
</>
|
</>
|
||||||
@@ -47,6 +44,21 @@ export function TransferStatusView({ status, loading }: TransferStatusProps) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface TransferStatusIconProps {
|
||||||
|
status: TransferStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function TransferStatusIcon({ status }: TransferStatusIconProps) {
|
||||||
|
return (
|
||||||
|
<span title={TransferStatusMapping[status]}>
|
||||||
|
<Icon
|
||||||
|
name={getIconForStatus(status)}
|
||||||
|
className={getColourForStatus(status)}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple mapping from status to icon name
|
* Simple mapping from status to icon name
|
||||||
* @param status TransferStatus
|
* @param status TransferStatus
|
||||||
@@ -60,6 +72,8 @@ export function getIconForStatus(status: TransferStatus): IconName {
|
|||||||
return IconNames.TICK;
|
return IconNames.TICK;
|
||||||
case TransferStatus.STATUS_REJECTED:
|
case TransferStatus.STATUS_REJECTED:
|
||||||
return IconNames.CROSS;
|
return IconNames.CROSS;
|
||||||
|
case TransferStatus.STATUS_CANCELLED:
|
||||||
|
return IconNames.CROSS;
|
||||||
default:
|
default:
|
||||||
return IconNames.TIME;
|
return IconNames.TIME;
|
||||||
}
|
}
|
||||||
@@ -78,6 +92,8 @@ export function getColourForStatus(status: TransferStatus): string {
|
|||||||
return 'text-green-500';
|
return 'text-green-500';
|
||||||
case TransferStatus.STATUS_REJECTED:
|
case TransferStatus.STATUS_REJECTED:
|
||||||
return 'text-red-500';
|
return 'text-red-500';
|
||||||
|
case TransferStatus.STATUS_CANCELLED:
|
||||||
|
return 'text-red-600';
|
||||||
default:
|
default:
|
||||||
return 'text-yellow-500';
|
return 'text-yellow-500';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,4 +12,5 @@ export const Routes = {
|
|||||||
ORACLES: 'oracles',
|
ORACLES: 'oracles',
|
||||||
NETWORK_PARAMETERS: 'network-parameters',
|
NETWORK_PARAMETERS: 'network-parameters',
|
||||||
DISCLAIMER: 'disclaimer',
|
DISCLAIMER: 'disclaimer',
|
||||||
|
TREASURY: 'treasury',
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import { PartyAccountsByAsset } from './parties/id/accounts';
|
|||||||
import { Disclaimer } from './pages/disclaimer';
|
import { Disclaimer } from './pages/disclaimer';
|
||||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||||
import RestrictedPage from './restricted';
|
import RestrictedPage from './restricted';
|
||||||
|
import { NetworkTreasury } from './treasury';
|
||||||
|
|
||||||
export type Navigable = {
|
export type Navigable = {
|
||||||
path: string;
|
path: string;
|
||||||
@@ -229,6 +230,17 @@ export const useRouterConfig = () => {
|
|||||||
]
|
]
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
|
const treasuryRoutes: Route[] = [
|
||||||
|
{
|
||||||
|
path: Routes.TREASURY,
|
||||||
|
handle: {
|
||||||
|
name: t('Treasury'),
|
||||||
|
text: t('Treasury'),
|
||||||
|
breadcrumb: () => <Link to={Routes.TREASURY}>{t('Treasury')}</Link>,
|
||||||
|
},
|
||||||
|
element: <NetworkTreasury />,
|
||||||
|
},
|
||||||
|
];
|
||||||
const validators: Route[] = featureFlags.EXPLORER_VALIDATORS
|
const validators: Route[] = featureFlags.EXPLORER_VALIDATORS
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
@@ -358,6 +370,7 @@ export const useRouterConfig = () => {
|
|||||||
...marketsRoutes,
|
...marketsRoutes,
|
||||||
...networkParametersRoutes,
|
...networkParametersRoutes,
|
||||||
...validators,
|
...validators,
|
||||||
|
...treasuryRoutes,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
query ExplorerTreasury {
|
||||||
|
assetsConnection(pagination: { last: 1000 }) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
networkTreasuryAccount {
|
||||||
|
balance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
query ExplorerTreasuryTransfers {
|
||||||
|
transfersConnection(
|
||||||
|
partyId: "network"
|
||||||
|
direction: ToOrFrom
|
||||||
|
pagination: { last: 200 }
|
||||||
|
) {
|
||||||
|
pageInfo {
|
||||||
|
hasNextPage
|
||||||
|
}
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
transfer {
|
||||||
|
timestamp
|
||||||
|
from
|
||||||
|
amount
|
||||||
|
to
|
||||||
|
status
|
||||||
|
reason
|
||||||
|
toAccountType
|
||||||
|
fromAccountType
|
||||||
|
asset {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
id
|
||||||
|
status
|
||||||
|
kind {
|
||||||
|
... on OneOffTransfer {
|
||||||
|
deliverOn
|
||||||
|
}
|
||||||
|
... on RecurringTransfer {
|
||||||
|
startEpoch
|
||||||
|
}
|
||||||
|
... on OneOffGovernanceTransfer {
|
||||||
|
deliverOn
|
||||||
|
}
|
||||||
|
... on RecurringGovernanceTransfer {
|
||||||
|
endEpoch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import * as Types from '@vegaprotocol/types';
|
||||||
|
|
||||||
|
import { gql } from '@apollo/client';
|
||||||
|
import * as Apollo from '@apollo/client';
|
||||||
|
const defaultOptions = {} as const;
|
||||||
|
export type ExplorerTreasuryQueryVariables = Types.Exact<{ [key: string]: never; }>;
|
||||||
|
|
||||||
|
|
||||||
|
export type ExplorerTreasuryQuery = { __typename?: 'Query', assetsConnection?: { __typename?: 'AssetsConnection', edges?: Array<{ __typename?: 'AssetEdge', node: { __typename?: 'Asset', id: string, networkTreasuryAccount?: { __typename?: 'AccountBalance', balance: string } | null } } | null> | null } | null };
|
||||||
|
|
||||||
|
|
||||||
|
export const ExplorerTreasuryDocument = gql`
|
||||||
|
query ExplorerTreasury {
|
||||||
|
assetsConnection(pagination: {last: 1000}) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
networkTreasuryAccount {
|
||||||
|
balance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __useExplorerTreasuryQuery__
|
||||||
|
*
|
||||||
|
* To run a query within a React component, call `useExplorerTreasuryQuery` and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `useExplorerTreasuryQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||||
|
* you can use to render your UI.
|
||||||
|
*
|
||||||
|
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const { data, loading, error } = useExplorerTreasuryQuery({
|
||||||
|
* variables: {
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export function useExplorerTreasuryQuery(baseOptions?: Apollo.QueryHookOptions<ExplorerTreasuryQuery, ExplorerTreasuryQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return Apollo.useQuery<ExplorerTreasuryQuery, ExplorerTreasuryQueryVariables>(ExplorerTreasuryDocument, options);
|
||||||
|
}
|
||||||
|
export function useExplorerTreasuryLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ExplorerTreasuryQuery, ExplorerTreasuryQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return Apollo.useLazyQuery<ExplorerTreasuryQuery, ExplorerTreasuryQueryVariables>(ExplorerTreasuryDocument, options);
|
||||||
|
}
|
||||||
|
export type ExplorerTreasuryQueryHookResult = ReturnType<typeof useExplorerTreasuryQuery>;
|
||||||
|
export type ExplorerTreasuryLazyQueryHookResult = ReturnType<typeof useExplorerTreasuryLazyQuery>;
|
||||||
|
export type ExplorerTreasuryQueryResult = Apollo.QueryResult<ExplorerTreasuryQuery, ExplorerTreasuryQueryVariables>;
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
import * as Types from '@vegaprotocol/types';
|
||||||
|
|
||||||
|
import { gql } from '@apollo/client';
|
||||||
|
import * as Apollo from '@apollo/client';
|
||||||
|
const defaultOptions = {} as const;
|
||||||
|
export type ExplorerTreasuryTransfersQueryVariables = Types.Exact<{ [key: string]: never; }>;
|
||||||
|
|
||||||
|
|
||||||
|
export type ExplorerTreasuryTransfersQuery = { __typename?: 'Query', transfersConnection?: { __typename?: 'TransferConnection', pageInfo: { __typename?: 'PageInfo', hasNextPage: boolean }, edges?: Array<{ __typename?: 'TransferEdge', node: { __typename?: 'TransferNode', transfer: { __typename?: 'Transfer', timestamp: any, from: string, amount: string, to: string, status: Types.TransferStatus, reason?: string | null, toAccountType: Types.AccountType, fromAccountType: Types.AccountType, id: string, asset?: { __typename?: 'Asset', id: string } | null, kind: { __typename?: 'OneOffGovernanceTransfer', deliverOn?: any | null } | { __typename?: 'OneOffTransfer', deliverOn?: any | null } | { __typename?: 'RecurringGovernanceTransfer', endEpoch?: number | null } | { __typename?: 'RecurringTransfer', startEpoch: number } } } } | null> | null } | null };
|
||||||
|
|
||||||
|
|
||||||
|
export const ExplorerTreasuryTransfersDocument = gql`
|
||||||
|
query ExplorerTreasuryTransfers {
|
||||||
|
transfersConnection(
|
||||||
|
partyId: "network"
|
||||||
|
direction: ToOrFrom
|
||||||
|
pagination: {last: 200}
|
||||||
|
) {
|
||||||
|
pageInfo {
|
||||||
|
hasNextPage
|
||||||
|
}
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
transfer {
|
||||||
|
timestamp
|
||||||
|
from
|
||||||
|
amount
|
||||||
|
to
|
||||||
|
status
|
||||||
|
reason
|
||||||
|
toAccountType
|
||||||
|
fromAccountType
|
||||||
|
asset {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
id
|
||||||
|
status
|
||||||
|
kind {
|
||||||
|
... on OneOffTransfer {
|
||||||
|
deliverOn
|
||||||
|
}
|
||||||
|
... on RecurringTransfer {
|
||||||
|
startEpoch
|
||||||
|
}
|
||||||
|
... on OneOffGovernanceTransfer {
|
||||||
|
deliverOn
|
||||||
|
}
|
||||||
|
... on RecurringGovernanceTransfer {
|
||||||
|
endEpoch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __useExplorerTreasuryTransfersQuery__
|
||||||
|
*
|
||||||
|
* To run a query within a React component, call `useExplorerTreasuryTransfersQuery` and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `useExplorerTreasuryTransfersQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||||
|
* you can use to render your UI.
|
||||||
|
*
|
||||||
|
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const { data, loading, error } = useExplorerTreasuryTransfersQuery({
|
||||||
|
* variables: {
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export function useExplorerTreasuryTransfersQuery(baseOptions?: Apollo.QueryHookOptions<ExplorerTreasuryTransfersQuery, ExplorerTreasuryTransfersQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return Apollo.useQuery<ExplorerTreasuryTransfersQuery, ExplorerTreasuryTransfersQueryVariables>(ExplorerTreasuryTransfersDocument, options);
|
||||||
|
}
|
||||||
|
export function useExplorerTreasuryTransfersLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ExplorerTreasuryTransfersQuery, ExplorerTreasuryTransfersQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return Apollo.useLazyQuery<ExplorerTreasuryTransfersQuery, ExplorerTreasuryTransfersQueryVariables>(ExplorerTreasuryTransfersDocument, options);
|
||||||
|
}
|
||||||
|
export type ExplorerTreasuryTransfersQueryHookResult = ReturnType<typeof useExplorerTreasuryTransfersQuery>;
|
||||||
|
export type ExplorerTreasuryTransfersLazyQueryHookResult = ReturnType<typeof useExplorerTreasuryTransfersLazyQuery>;
|
||||||
|
export type ExplorerTreasuryTransfersQueryResult = Apollo.QueryResult<ExplorerTreasuryTransfersQuery, ExplorerTreasuryTransfersQueryVariables>;
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
// NOTE: These are a temporary measure, pulled from an old branch on console.
|
||||||
|
|
||||||
|
import { IconNames } from '@blueprintjs/icons';
|
||||||
|
import { Icon } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import { USDc } from './usdc';
|
||||||
|
import { Vega } from './vega';
|
||||||
|
import { USDt } from './usdt';
|
||||||
|
|
||||||
|
export interface AssetIconProps {
|
||||||
|
symbol: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A poorly implemented, limited support for asset icons.
|
||||||
|
*
|
||||||
|
* These are committed as 'deprecated' to discourage use outside the Treasury page. Rather
|
||||||
|
* than use this, a better approach would be to use source contract addresses to match assets.
|
||||||
|
* This will be done separately.
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
export function AssetIcon({ symbol }: AssetIconProps) {
|
||||||
|
const s = symbol.toLowerCase();
|
||||||
|
switch (s) {
|
||||||
|
case 'a4a16e250a09a86061ec83c2f9466fc9dc33d332f86876ee74b6f128a5cd6710': // mainnet
|
||||||
|
case 'c9fe6fc24fce121b2cc72680543a886055abb560043fda394ba5376203b7527d': // mainnet
|
||||||
|
return <USDc size={32} />;
|
||||||
|
case 'd1984e3d365faa05bcafbe41f50f90e3663ee7c0da22bb1e24b164e9532691b2': // mainnet
|
||||||
|
case 'fc7fd956078fb1fc9db5c19b88f0874c4299b2a7639ad05a47a28c0aef291b55': // testnet
|
||||||
|
return <Vega size={32} />;
|
||||||
|
case 'bf1e88d19db4b3ca0d1d5bdb73718a01686b18cf731ca26adedf3c8b83802bba': // mainnet
|
||||||
|
case 'ede4076aef07fd79502d14326c54ab3911558371baaf697a19d077f4f89de399': // testnet
|
||||||
|
return <USDt size={32} />;
|
||||||
|
default:
|
||||||
|
return <Icon name={IconNames.BANK_ACCOUNT} size={8} />;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* See note in index.tsx. This component is intended as a placeholder for a
|
||||||
|
* better, more generic solution.
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
export const USDc = ({ size = 16 }: { size?: number }) => {
|
||||||
|
return (
|
||||||
|
<svg width={size} height={size} viewBox="0 0 2000 2000">
|
||||||
|
<path
|
||||||
|
d="M1000 2000c554.17 0 1000-445.83 1000-1000S1554.17 0 1000 0 0 445.83 0 1000s445.83 1000 1000 1000z"
|
||||||
|
fill="#2775ca"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M1275 1158.33c0-145.83-87.5-195.83-262.5-216.66-125-16.67-150-50-150-108.34s41.67-95.83 125-95.83c75 0 116.67 25 137.5 87.5 4.17 12.5 16.67 20.83 29.17 20.83h66.66c16.67 0 29.17-12.5 29.17-29.16v-4.17c-16.67-91.67-91.67-162.5-187.5-170.83v-100c0-16.67-12.5-29.17-33.33-33.34h-62.5c-16.67 0-29.17 12.5-33.34 33.34v95.83c-125 16.67-204.16 100-204.16 204.17 0 137.5 83.33 191.66 258.33 212.5 116.67 20.83 154.17 45.83 154.17 112.5s-58.34 112.5-137.5 112.5c-108.34 0-145.84-45.84-158.34-108.34-4.16-16.66-16.66-25-29.16-25h-70.84c-16.66 0-29.16 12.5-29.16 29.17v4.17c16.66 104.16 83.33 179.16 220.83 200v100c0 16.66 12.5 29.16 33.33 33.33h62.5c16.67 0 29.17-12.5 33.34-33.33v-100c125-20.84 208.33-108.34 208.33-220.84z"
|
||||||
|
fill="#fff"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M787.5 1595.83c-325-116.66-491.67-479.16-370.83-800 62.5-175 200-308.33 370.83-370.83 16.67-8.33 25-20.83 25-41.67V325c0-16.67-8.33-29.17-25-33.33-4.17 0-12.5 0-16.67 4.16-395.83 125-612.5 545.84-487.5 941.67 75 233.33 254.17 412.5 487.5 487.5 16.67 8.33 33.34 0 37.5-16.67 4.17-4.16 4.17-8.33 4.17-16.66v-58.34c0-12.5-12.5-29.16-25-37.5zM1229.17 295.83c-16.67-8.33-33.34 0-37.5 16.67-4.17 4.17-4.17 8.33-4.17 16.67v58.33c0 16.67 12.5 33.33 25 41.67 325 116.66 491.67 479.16 370.83 800-62.5 175-200 308.33-370.83 370.83-16.67 8.33-25 20.83-25 41.67V1700c0 16.67 8.33 29.17 25 33.33 4.17 0 12.5 0 16.67-4.16 395.83-125 612.5-545.84 487.5-941.67-75-237.5-258.34-416.67-487.5-491.67z"
|
||||||
|
fill="#fff"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* See note in index.tsx. This component is intended as a placeholder for a
|
||||||
|
* better, more generic solution.
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
export const USDt = ({ size = 16 }: { size?: number }) => {
|
||||||
|
return (
|
||||||
|
<svg width={size} height={size} viewBox="0 0 339.43 295.27">
|
||||||
|
<path
|
||||||
|
fill="#50af95"
|
||||||
|
d="M62.15,1.45l-61.89,130a2.52,2.52,0,0,0,.54,2.94L167.95,294.56a2.55,2.55,0,0,0,3.53,0L338.63,134.4a2.52,2.52,0,0,0,.54-2.94l-61.89-130A2.5,2.5,0,0,0,275,0H64.45a2.5,2.5,0,0,0-2.3,1.45h0Z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fill="#fff"
|
||||||
|
d="M191.19,144.8v0c-1.2.09-7.4,0.46-21.23,0.46-11,0-18.81-.33-21.55-0.46v0c-42.51-1.87-74.24-9.27-74.24-18.13s31.73-16.25,74.24-18.15v28.91c2.78,0.2,10.74.67,21.74,0.67,13.2,0,19.81-.55,21-0.66v-28.9c42.42,1.89,74.08,9.29,74.08,18.13s-31.65,16.24-74.08,18.12h0Zm0-39.25V79.68h59.2V40.23H89.21V79.68H148.4v25.86c-48.11,2.21-84.29,11.74-84.29,23.16s36.18,20.94,84.29,23.16v82.9h42.78V151.83c48-2.21,84.12-11.73,84.12-23.14s-36.09-20.93-84.12-23.15h0Zm0,0h0Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* See note in index.tsx. This component is intended as a placeholder for a
|
||||||
|
* better, more generic solution.
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
export const Vega = ({ size = 16 }: { size?: number }) => {
|
||||||
|
return (
|
||||||
|
<svg width={size} height={size} viewBox="0 0 42 42">
|
||||||
|
<rect width="42" height="42" rx="21" fill="black" />
|
||||||
|
<path d="M13 27.2726H16.4545V10H13V27.2726Z" fill="white" />
|
||||||
|
<path d="M25.667 23.8181H29.1215V10H25.667V23.8181Z" fill="white" />
|
||||||
|
<path d="M19.333 33.6059H22.7875V30.1514H19.333V33.6059Z" fill="white" />
|
||||||
|
<path
|
||||||
|
d="M22.7871 30.7271H26.2416V27.2726H22.7871V30.7271Z"
|
||||||
|
fill="white"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M29.1211 27.2726H31.9999V23.8181H29.1211V27.2726Z"
|
||||||
|
fill="white"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M16.4551 30.7271H19.3339V27.2726H16.4551V30.7271Z"
|
||||||
|
fill="white"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
import type { DeepPartial } from '@apollo/client/utilities';
|
||||||
|
import { parseResultsToAccounts } from './network-accounts-table';
|
||||||
|
import {
|
||||||
|
ExplorerTreasuryDocument,
|
||||||
|
type ExplorerTreasuryQuery,
|
||||||
|
} from '../__generated__/Treasury';
|
||||||
|
import { render, screen } from '@testing-library/react';
|
||||||
|
import { MockedProvider } from '@apollo/client/testing';
|
||||||
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
|
import { NetworkAccountsTable } from './network-accounts-table';
|
||||||
|
|
||||||
|
describe('parseResultsToAccounts', () => {
|
||||||
|
it('should return an array of non-zero treasury accounts', () => {
|
||||||
|
const data: DeepPartial<ExplorerTreasuryQuery> = {
|
||||||
|
assetsConnection: {
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
id: 'asset1',
|
||||||
|
networkTreasuryAccount: {
|
||||||
|
balance: '100',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
id: 'has0assets',
|
||||||
|
networkTreasuryAccount: {
|
||||||
|
balance: '0',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
id: 'asset3',
|
||||||
|
networkTreasuryAccount: {
|
||||||
|
balance: '50',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
id: 'hasnonetworktreasuryaccount',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = parseResultsToAccounts(data as ExplorerTreasuryQuery);
|
||||||
|
|
||||||
|
expect(result).toHaveLength(2);
|
||||||
|
expect(result).toEqual([
|
||||||
|
{
|
||||||
|
assetId: 'asset1',
|
||||||
|
balance: '100',
|
||||||
|
type: 'ACCOUNT_TYPE_NETWORK_TREASURY',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
assetId: 'asset3',
|
||||||
|
balance: '50',
|
||||||
|
type: 'ACCOUNT_TYPE_NETWORK_TREASURY',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return an empty array if no non-zero accounts are found', () => {
|
||||||
|
const data: DeepPartial<ExplorerTreasuryQuery> = {
|
||||||
|
assetsConnection: {
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
id: 'asset1',
|
||||||
|
networkTreasuryAccount: {
|
||||||
|
balance: '0',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
id: 'asset2',
|
||||||
|
networkTreasuryAccount: {
|
||||||
|
balance: '0',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = parseResultsToAccounts(data as ExplorerTreasuryQuery);
|
||||||
|
|
||||||
|
expect(result).toHaveLength(0);
|
||||||
|
expect(result).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle missing data', () => {
|
||||||
|
const result = parseResultsToAccounts(
|
||||||
|
undefined as unknown as ExplorerTreasuryQuery
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result).toHaveLength(0);
|
||||||
|
expect(result).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('NetworkAccountsTable', () => {
|
||||||
|
const mockData: ExplorerTreasuryQuery = {
|
||||||
|
assetsConnection: {
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
id: 'asset1',
|
||||||
|
networkTreasuryAccount: {
|
||||||
|
balance: '100',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
id: 'asset2',
|
||||||
|
networkTreasuryAccount: {
|
||||||
|
balance: '50',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const mocks = [
|
||||||
|
{
|
||||||
|
request: {
|
||||||
|
query: ExplorerTreasuryDocument,
|
||||||
|
},
|
||||||
|
result: {
|
||||||
|
data: mockData,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
it('should render network accounts (as many as match - often just 1)', async () => {
|
||||||
|
render(
|
||||||
|
<MockedProvider mocks={mocks} addTypename={false}>
|
||||||
|
<MemoryRouter>
|
||||||
|
<NetworkAccountsTable />
|
||||||
|
</MemoryRouter>
|
||||||
|
</MockedProvider>
|
||||||
|
);
|
||||||
|
|
||||||
|
// Wait for the data to load
|
||||||
|
await screen.findByText('Loading...');
|
||||||
|
|
||||||
|
// Assert that the network accounts are rendered
|
||||||
|
expect(screen.getByText('asset1')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('asset2')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle loading state', async () => {
|
||||||
|
render(
|
||||||
|
<MockedProvider mocks={mocks} addTypename={false}>
|
||||||
|
<MemoryRouter>
|
||||||
|
<NetworkAccountsTable />
|
||||||
|
</MemoryRouter>
|
||||||
|
</MockedProvider>
|
||||||
|
);
|
||||||
|
|
||||||
|
// Assert that the loading state is rendered
|
||||||
|
expect(screen.getByText('Loading...')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import {
|
||||||
|
type ExplorerTreasuryQuery,
|
||||||
|
useExplorerTreasuryQuery,
|
||||||
|
} from '../__generated__/Treasury';
|
||||||
|
import AssetBalance from '../../../components/asset-balance/asset-balance';
|
||||||
|
import { AssetLink } from '../../../components/links';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
|
||||||
|
import { AssetIcon } from './asset-icon';
|
||||||
|
import { type NonZeroAccount } from '../network-treasury';
|
||||||
|
import { AccountType } from '@vegaprotocol/types';
|
||||||
|
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
||||||
|
|
||||||
|
export const NetworkAccountsTable = () => {
|
||||||
|
const { data, loading, error } = useExplorerTreasuryQuery({
|
||||||
|
// This needs to ignore error as old assets may no longer properly resolve
|
||||||
|
errorPolicy: 'ignore',
|
||||||
|
});
|
||||||
|
const { screenSize } = useScreenDimensions();
|
||||||
|
const shouldRound = useMemo(
|
||||||
|
() => ['xs', 'sm', 'md', 'lg'].includes(screenSize),
|
||||||
|
[screenSize]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AsyncRenderer
|
||||||
|
data={data}
|
||||||
|
loading={loading}
|
||||||
|
error={error}
|
||||||
|
render={(data) => {
|
||||||
|
const c = parseResultsToAccounts(data);
|
||||||
|
return (
|
||||||
|
<section className="md:flex md:flex-row flex-wrap">
|
||||||
|
{c.map((a) => (
|
||||||
|
<div className="basis-1/2 md:basis-1/4">
|
||||||
|
<div className="bg-white rounded overflow-hidden shadow-lg dark:bg-black dark:border-slate-500 dark:border">
|
||||||
|
<div className="text-center p-6 bg-gray-100 dark:bg-slate-900 border-b dark:border-slate-500">
|
||||||
|
<p className="flex justify-center">
|
||||||
|
<AssetIcon symbol={a.assetId} />
|
||||||
|
</p>
|
||||||
|
<p className="mt-3" data-testid="name">
|
||||||
|
<AssetLink assetId={a.assetId} />
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="text-center py-5" data-testid="balance">
|
||||||
|
<AssetBalance
|
||||||
|
assetId={a.assetId}
|
||||||
|
price={a.balance}
|
||||||
|
showAssetSymbol={true}
|
||||||
|
rounded={shouldRound}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export function parseResultsToAccounts(
|
||||||
|
data: ExplorerTreasuryQuery
|
||||||
|
): NonZeroAccount[] {
|
||||||
|
const nonZeroAccounts: NonZeroAccount[] = [];
|
||||||
|
if (data?.assetsConnection?.edges) {
|
||||||
|
const edges = removePaginationWrapper(data?.assetsConnection?.edges);
|
||||||
|
if (edges) {
|
||||||
|
edges.forEach((edge) => {
|
||||||
|
if (
|
||||||
|
edge.networkTreasuryAccount &&
|
||||||
|
edge.networkTreasuryAccount?.balance !== '0'
|
||||||
|
) {
|
||||||
|
nonZeroAccounts.push({
|
||||||
|
assetId: edge.id,
|
||||||
|
balance: edge.networkTreasuryAccount?.balance,
|
||||||
|
type: AccountType.ACCOUNT_TYPE_NETWORK_TREASURY,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nonZeroAccounts;
|
||||||
|
}
|
||||||
@@ -0,0 +1,262 @@
|
|||||||
|
import { AccountType } from '@vegaprotocol/types';
|
||||||
|
import {
|
||||||
|
typeLabel,
|
||||||
|
getToAccountTypeLabel,
|
||||||
|
filterAccountTransfers,
|
||||||
|
} from './network-transfers-table';
|
||||||
|
import { render, screen } from '@testing-library/react';
|
||||||
|
import { NetworkTransfersTable } from './network-transfers-table';
|
||||||
|
import { MockedProvider } from '@apollo/client/testing';
|
||||||
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
|
import {
|
||||||
|
ExplorerTreasuryTransfersDocument,
|
||||||
|
type ExplorerTreasuryTransfersQuery,
|
||||||
|
} from '../__generated__/TreasuryTransfers';
|
||||||
|
import type { DeepPartial } from '@apollo/client/utilities';
|
||||||
|
|
||||||
|
describe('typeLabel', () => {
|
||||||
|
it('should return "Transfer" for "OneOffTransfer" kind', () => {
|
||||||
|
expect(typeLabel('OneOffTransfer')).toBe('Transfer');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return "Transfer" for "RecurringTransfer" kind', () => {
|
||||||
|
expect(typeLabel('RecurringTransfer')).toBe('Transfer');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return "Governance" for "OneOffGovernanceTransfer" kind', () => {
|
||||||
|
expect(typeLabel('OneOffGovernanceTransfer')).toBe('Governance');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return "Governance" for "RecurringGovernanceTransfer" kind', () => {
|
||||||
|
expect(typeLabel('RecurringGovernanceTransfer')).toBe('Governance');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return "Unknown" for unknown kind', () => {
|
||||||
|
expect(typeLabel()).toBe('Unknown');
|
||||||
|
expect(typeLabel('')).toBe('Unknown');
|
||||||
|
expect(typeLabel('InvalidKind')).toBe('Unknown');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getToAccountTypeLabel', () => {
|
||||||
|
it('should return "Treasury" when type is ACCOUNT_TYPE_NETWORK_TREASURY', () => {
|
||||||
|
expect(
|
||||||
|
getToAccountTypeLabel(AccountType.ACCOUNT_TYPE_NETWORK_TREASURY)
|
||||||
|
).toBe('Treasury');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return "Fees" when type is any of the fee account types', () => {
|
||||||
|
expect(
|
||||||
|
getToAccountTypeLabel(AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE)
|
||||||
|
).toBe('Fees');
|
||||||
|
expect(getToAccountTypeLabel(AccountType.ACCOUNT_TYPE_FEES_MAKER)).toBe(
|
||||||
|
'Fees'
|
||||||
|
);
|
||||||
|
expect(getToAccountTypeLabel(AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY)).toBe(
|
||||||
|
'Fees'
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
getToAccountTypeLabel(AccountType.ACCOUNT_TYPE_LP_LIQUIDITY_FEES)
|
||||||
|
).toBe('Fees');
|
||||||
|
expect(
|
||||||
|
getToAccountTypeLabel(
|
||||||
|
AccountType.ACCOUNT_TYPE_PENDING_FEE_REFERRAL_REWARD
|
||||||
|
)
|
||||||
|
).toBe('Fees');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return "Insurance" when type is ACCOUNT_TYPE_GLOBAL_INSURANCE', () => {
|
||||||
|
expect(
|
||||||
|
getToAccountTypeLabel(AccountType.ACCOUNT_TYPE_GLOBAL_INSURANCE)
|
||||||
|
).toBe('Insurance');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return "Rewards" when type is any of the reward account types', () => {
|
||||||
|
expect(getToAccountTypeLabel(AccountType.ACCOUNT_TYPE_GLOBAL_REWARD)).toBe(
|
||||||
|
'Rewards'
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
getToAccountTypeLabel(AccountType.ACCOUNT_TYPE_REWARD_AVERAGE_POSITION)
|
||||||
|
).toBe('Rewards');
|
||||||
|
expect(
|
||||||
|
getToAccountTypeLabel(AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES)
|
||||||
|
).toBe('Rewards');
|
||||||
|
expect(
|
||||||
|
getToAccountTypeLabel(AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES)
|
||||||
|
).toBe('Rewards');
|
||||||
|
expect(
|
||||||
|
getToAccountTypeLabel(AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES)
|
||||||
|
).toBe('Rewards');
|
||||||
|
expect(
|
||||||
|
getToAccountTypeLabel(AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS)
|
||||||
|
).toBe('Rewards');
|
||||||
|
expect(
|
||||||
|
getToAccountTypeLabel(AccountType.ACCOUNT_TYPE_REWARD_RELATIVE_RETURN)
|
||||||
|
).toBe('Rewards');
|
||||||
|
expect(
|
||||||
|
getToAccountTypeLabel(AccountType.ACCOUNT_TYPE_REWARD_RETURN_VOLATILITY)
|
||||||
|
).toBe('Rewards');
|
||||||
|
expect(
|
||||||
|
getToAccountTypeLabel(AccountType.ACCOUNT_TYPE_REWARD_VALIDATOR_RANKING)
|
||||||
|
).toBe('Rewards');
|
||||||
|
expect(getToAccountTypeLabel(AccountType.ACCOUNT_TYPE_VESTED_REWARDS)).toBe(
|
||||||
|
'Rewards'
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
getToAccountTypeLabel(AccountType.ACCOUNT_TYPE_VESTING_REWARDS)
|
||||||
|
).toBe('Rewards');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return "Other" for any other type', () => {
|
||||||
|
expect(getToAccountTypeLabel(undefined)).toBe('Other');
|
||||||
|
expect(getToAccountTypeLabel('unknown' as AccountType)).toBe('Other');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('filterAccountTransfers', () => {
|
||||||
|
it('filters out transactions that are not to or from a treasury account', () => {
|
||||||
|
const data: DeepPartial<ExplorerTreasuryTransfersQuery> = {
|
||||||
|
transfersConnection: {
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
transfer: {
|
||||||
|
toAccountType: AccountType.ACCOUNT_TYPE_NETWORK_TREASURY,
|
||||||
|
fromAccountType: AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
transfer: {
|
||||||
|
toAccountType: AccountType.ACCOUNT_TYPE_NETWORK_TREASURY,
|
||||||
|
fromAccountType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_AVERAGE_POSITION,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
transfer: {
|
||||||
|
toAccountType: AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||||
|
fromAccountType: AccountType.ACCOUNT_TYPE_NETWORK_TREASURY,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
transfer: {
|
||||||
|
toAccountType: AccountType.ACCOUNT_TYPE_REWARD_AVERAGE_POSITION,
|
||||||
|
fromAccountType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = filterAccountTransfers(
|
||||||
|
data as ExplorerTreasuryTransfersQuery
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result).toHaveLength(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return an empty array if no transfers match the filter', () => {
|
||||||
|
const data: DeepPartial<ExplorerTreasuryTransfersQuery> = {
|
||||||
|
transfersConnection: {
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
transfer: {
|
||||||
|
toAccountType: AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||||
|
fromAccountType: AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
transfer: {
|
||||||
|
toAccountType: AccountType.ACCOUNT_TYPE_REWARD_AVERAGE_POSITION,
|
||||||
|
fromAccountType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_AVERAGE_POSITION,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = filterAccountTransfers(
|
||||||
|
data as ExplorerTreasuryTransfersQuery
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result).toHaveLength(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('NetworkTransfersTable', () => {
|
||||||
|
it('renders table headers correctly', async () => {
|
||||||
|
const mocks = [
|
||||||
|
{
|
||||||
|
request: {
|
||||||
|
query: ExplorerTreasuryTransfersDocument,
|
||||||
|
},
|
||||||
|
result: {
|
||||||
|
data: {
|
||||||
|
transfersConnection: {
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
transfer: {
|
||||||
|
id: '123',
|
||||||
|
toAccountType: AccountType.ACCOUNT_TYPE_NETWORK_TREASURY,
|
||||||
|
fromAccountType:
|
||||||
|
AccountType.ACCOUNT_TYPE_NETWORK_TREASURY,
|
||||||
|
amount: '100',
|
||||||
|
asset: {
|
||||||
|
id: '1',
|
||||||
|
},
|
||||||
|
timestamp: '2022-01-01T00:00:00Z',
|
||||||
|
from: 'network',
|
||||||
|
to: '7100a8a82ef45adb9efa070cc821c6c5c48172d6dc5f842431549490fe5897a0',
|
||||||
|
reason: '',
|
||||||
|
status: 'COMPLETED',
|
||||||
|
kind: {
|
||||||
|
__typename: 'OneOffGovernanceTransfer',
|
||||||
|
deliverOn: '123',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
render(
|
||||||
|
<MockedProvider mocks={mocks} addTypename={true}>
|
||||||
|
<MemoryRouter>
|
||||||
|
<NetworkTransfersTable />
|
||||||
|
</MemoryRouter>
|
||||||
|
</MockedProvider>
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(await screen.findByText('Amount')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('Asset')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('Age')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('From')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('To')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('Status')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('Type')).toBeInTheDocument();
|
||||||
|
|
||||||
|
expect(screen.getByTestId('from-account').textContent).toEqual('Treasury');
|
||||||
|
expect(screen.getByTestId('to-account').textContent).toEqual('7100…97a0');
|
||||||
|
expect(screen.getByTestId('transfer-kind').textContent).toEqual(
|
||||||
|
'Governance'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,253 @@
|
|||||||
|
import { AsyncRenderer, Icon } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import AssetBalance from '../../../components/asset-balance/asset-balance';
|
||||||
|
import { AccountType, AccountTypeMapping } from '@vegaprotocol/types';
|
||||||
|
import { AssetLink, PartyLink } from '../../../components/links';
|
||||||
|
import {
|
||||||
|
type ExplorerTreasuryTransfersQuery,
|
||||||
|
useExplorerTreasuryTransfersQuery,
|
||||||
|
} from '../__generated__/TreasuryTransfers';
|
||||||
|
import { TimeAgo } from '../../../components/time-ago';
|
||||||
|
import { TransferStatusIcon } from '../../../components/txs/details/transfer/blocks/transfer-status';
|
||||||
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
import { IconNames } from '@blueprintjs/icons';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
|
||||||
|
|
||||||
|
export const colours = {
|
||||||
|
INCOMING: '!fill-vega-green-600 text-vega-green-600 mr-2',
|
||||||
|
OUTGOING: '!fill-vega-pink-600 text-vega-pink-600 mr-2',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const theadClasses =
|
||||||
|
'py-2 border text-center bg-vega-light-150 dark:bg-vega-dark-150';
|
||||||
|
|
||||||
|
export function getToAccountTypeLabel(type?: AccountType): string {
|
||||||
|
switch (type) {
|
||||||
|
case AccountType.ACCOUNT_TYPE_NETWORK_TREASURY:
|
||||||
|
return t('Treasury');
|
||||||
|
case AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE:
|
||||||
|
case AccountType.ACCOUNT_TYPE_FEES_MAKER:
|
||||||
|
case AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY:
|
||||||
|
case AccountType.ACCOUNT_TYPE_LP_LIQUIDITY_FEES:
|
||||||
|
case AccountType.ACCOUNT_TYPE_PENDING_FEE_REFERRAL_REWARD:
|
||||||
|
return t('Fees');
|
||||||
|
case AccountType.ACCOUNT_TYPE_GLOBAL_INSURANCE:
|
||||||
|
return t('Insurance');
|
||||||
|
case AccountType.ACCOUNT_TYPE_GLOBAL_REWARD:
|
||||||
|
case AccountType.ACCOUNT_TYPE_REWARD_AVERAGE_POSITION:
|
||||||
|
case AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES:
|
||||||
|
case AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES:
|
||||||
|
case AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES:
|
||||||
|
case AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS:
|
||||||
|
case AccountType.ACCOUNT_TYPE_REWARD_RELATIVE_RETURN:
|
||||||
|
case AccountType.ACCOUNT_TYPE_REWARD_RETURN_VOLATILITY:
|
||||||
|
case AccountType.ACCOUNT_TYPE_REWARD_VALIDATOR_RANKING:
|
||||||
|
case AccountType.ACCOUNT_TYPE_VESTED_REWARDS:
|
||||||
|
case AccountType.ACCOUNT_TYPE_VESTING_REWARDS:
|
||||||
|
return t('Rewards');
|
||||||
|
default:
|
||||||
|
return t('Other');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function typeLabel(kind?: string): string {
|
||||||
|
switch (kind) {
|
||||||
|
case 'OneOffTransfer':
|
||||||
|
case 'RecurringTransfer':
|
||||||
|
return t('Transfer');
|
||||||
|
case 'OneOffGovernanceTransfer':
|
||||||
|
case 'RecurringGovernanceTransfer':
|
||||||
|
return t('Governance');
|
||||||
|
default:
|
||||||
|
return t('Unknown');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function filterAccountTransfers(data: ExplorerTreasuryTransfersQuery) {
|
||||||
|
return data.transfersConnection?.edges
|
||||||
|
?.filter((edge) => {
|
||||||
|
if (
|
||||||
|
edge?.node.transfer.toAccountType ===
|
||||||
|
AccountType.ACCOUNT_TYPE_NETWORK_TREASURY
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
} else if (
|
||||||
|
edge?.node.transfer.fromAccountType ===
|
||||||
|
AccountType.ACCOUNT_TYPE_NETWORK_TREASURY
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
.map((edge) => {
|
||||||
|
return edge?.node.transfer;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const NetworkTransfersTable = () => {
|
||||||
|
const { data, loading, error } = useExplorerTreasuryTransfersQuery({
|
||||||
|
// This needs to ignore error as old assets may no longer properly resolve
|
||||||
|
errorPolicy: 'ignore',
|
||||||
|
});
|
||||||
|
|
||||||
|
const { screenSize } = useScreenDimensions();
|
||||||
|
const shouldRound = useMemo(
|
||||||
|
() => ['xs', 'sm', 'md', 'lg'].includes(screenSize),
|
||||||
|
[screenSize]
|
||||||
|
);
|
||||||
|
const shouldTruncate = useMemo(
|
||||||
|
() => ['xs', 'sm', 'md', 'lg', 'xl'].includes(screenSize),
|
||||||
|
[screenSize]
|
||||||
|
);
|
||||||
|
const shouldHideColumns = useMemo(
|
||||||
|
() => ['xs', 'sm'].includes(screenSize),
|
||||||
|
[screenSize]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section>
|
||||||
|
<AsyncRenderer
|
||||||
|
data={data}
|
||||||
|
loading={loading}
|
||||||
|
error={error}
|
||||||
|
render={(data) => {
|
||||||
|
const c = filterAccountTransfers(data);
|
||||||
|
if (!c) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<table className="table-fixed border-spacing-3">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th className={theadClasses}>{t('Amount')}</th>
|
||||||
|
<th className={theadClasses}>{t('Asset')}</th>
|
||||||
|
<th className={theadClasses}>{t('Age')}</th>
|
||||||
|
<th className={theadClasses}>{t('From')}</th>
|
||||||
|
<th className={theadClasses}>{t('To')}</th>
|
||||||
|
<th
|
||||||
|
className={`${theadClasses} ${
|
||||||
|
shouldHideColumns ? 'hidden' : ''
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{t('Status')}
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
className={`${theadClasses} ${
|
||||||
|
shouldHideColumns ? 'hidden' : ''
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{t('Type')}
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{c.map((a) => {
|
||||||
|
const isIncoming =
|
||||||
|
a?.toAccountType ===
|
||||||
|
AccountType.ACCOUNT_TYPE_NETWORK_TREASURY;
|
||||||
|
return (
|
||||||
|
<tr>
|
||||||
|
{a && a.amount && a.asset && (
|
||||||
|
<td
|
||||||
|
className={`px-2 py-1 border whitespace-nowrap text-right ${
|
||||||
|
isIncoming ? colours.INCOMING : colours.OUTGOING
|
||||||
|
}`}
|
||||||
|
title={a.amount}
|
||||||
|
>
|
||||||
|
{a &&
|
||||||
|
a.toAccountType ===
|
||||||
|
AccountType.ACCOUNT_TYPE_NETWORK_TREASURY ? (
|
||||||
|
<Icon
|
||||||
|
name={IconNames.PLUS}
|
||||||
|
className={colours.INCOMING}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Icon
|
||||||
|
name={IconNames.MINUS}
|
||||||
|
className={colours.OUTGOING}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<AssetBalance
|
||||||
|
assetId={a.asset.id}
|
||||||
|
price={a.amount}
|
||||||
|
showAssetLink={false}
|
||||||
|
rounded={shouldRound}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
)}
|
||||||
|
<td className="px-2 py-1 border whitespace-nowrap">
|
||||||
|
{a && a.amount && a.asset && (
|
||||||
|
<AssetLink
|
||||||
|
assetId={a.asset.id}
|
||||||
|
showAssetSymbol={true}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
<td className="px-2 py-1 border">
|
||||||
|
{a && a.timestamp && <TimeAgo date={a.timestamp} />}
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
className="px-2 py-1 border"
|
||||||
|
data-testid="from-account"
|
||||||
|
>
|
||||||
|
{a && a.from && (
|
||||||
|
<PartyLink
|
||||||
|
id={a.from}
|
||||||
|
truncate={true}
|
||||||
|
truncateLength={shouldTruncate ? 4 : 15}
|
||||||
|
networkLabel={t('Treasury')}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
<td className="px-2 py-1 border" data-testid="to-account">
|
||||||
|
{a && a.to && (
|
||||||
|
<PartyLink
|
||||||
|
id={a.to}
|
||||||
|
networkLabel={t('Treasury')}
|
||||||
|
truncate={true}
|
||||||
|
truncateLength={shouldTruncate ? 4 : 15}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{a && !a.to && (
|
||||||
|
<span
|
||||||
|
className="underline decoration-dotted"
|
||||||
|
title={AccountTypeMapping[a.toAccountType]}
|
||||||
|
>
|
||||||
|
{getToAccountTypeLabel(a.toAccountType)}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
className={`px-2 py-1 border text-center ${
|
||||||
|
shouldHideColumns ? 'hidden' : ''
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{a && a.status && (
|
||||||
|
<TransferStatusIcon status={a.status} />
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
className={`px-2 py-1 border ${
|
||||||
|
shouldHideColumns ? 'hidden' : ''
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
className="underline decoration-dotted"
|
||||||
|
title={a?.kind.__typename}
|
||||||
|
data-testid="transfer-kind"
|
||||||
|
>
|
||||||
|
{a && typeLabel(a.kind.__typename)}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from './network-treasury';
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { useDocumentTitle } from '../../hooks/use-document-title';
|
||||||
|
import type { AccountType } from '@vegaprotocol/types';
|
||||||
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
import { RouteTitle } from '../../components/route-title';
|
||||||
|
import { NetworkAccountsTable } from './components/network-accounts-table';
|
||||||
|
import { NetworkTransfersTable } from './components/network-transfers-table';
|
||||||
|
|
||||||
|
export type NonZeroAccount = {
|
||||||
|
assetId: string;
|
||||||
|
balance: string;
|
||||||
|
type: AccountType;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const NetworkTreasury = () => {
|
||||||
|
useDocumentTitle(['Network Treasury']);
|
||||||
|
return (
|
||||||
|
<section>
|
||||||
|
<RouteTitle data-testid="block-header">{t(`Treasury`)}</RouteTitle>
|
||||||
|
<div>
|
||||||
|
<NetworkAccountsTable />
|
||||||
|
</div>
|
||||||
|
<div className="mt-5">
|
||||||
|
<h2 className="text-3xl mb-2">{t('Transfers')}</h2>
|
||||||
|
<NetworkTransfersTable />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -7,7 +7,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('Links and buttons', function () {
|
describe('Links and buttons', function () {
|
||||||
it.skip('should have link for proposal page', function () {
|
it('should have link for proposal page', function () {
|
||||||
cy.getByTestId('home-proposals').within(() => {
|
cy.getByTestId('home-proposals').within(() => {
|
||||||
cy.get('[href="/proposals"]')
|
cy.get('[href="/proposals"]')
|
||||||
.should('exist')
|
.should('exist')
|
||||||
@@ -51,7 +51,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it.skip('should have external link for governance', function () {
|
it('should have external link for governance', function () {
|
||||||
cy.getByTestId('home-proposals').within(() => {
|
cy.getByTestId('home-proposals').within(() => {
|
||||||
cy.getByTestId('external-link')
|
cy.getByTestId('external-link')
|
||||||
.should('have.attr', 'href')
|
.should('have.attr', 'href')
|
||||||
@@ -59,7 +59,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it.skip('should have link for validator page', function () {
|
it('should have link for validator page', function () {
|
||||||
cy.getByTestId('home-validators').within(() => {
|
cy.getByTestId('home-validators').within(() => {
|
||||||
cy.get('[href="/validators"]')
|
cy.get('[href="/validators"]')
|
||||||
.first()
|
.first()
|
||||||
@@ -68,7 +68,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it.skip('should have external link for validators', function () {
|
it('should have external link for validators', function () {
|
||||||
cy.getByTestId('home-validators').within(() => {
|
cy.getByTestId('home-validators').within(() => {
|
||||||
cy.getByTestId('external-link')
|
cy.getByTestId('external-link')
|
||||||
.should('have.attr', 'href')
|
.should('have.attr', 'href')
|
||||||
@@ -79,29 +79,29 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it.skip('should have information on active nodes', function () {
|
it('should have information on active nodes', function () {
|
||||||
cy.getByTestId('node-information')
|
cy.getByTestId('node-information')
|
||||||
.first()
|
.first()
|
||||||
.should('contain.text', '2')
|
.should('contain.text', '1')
|
||||||
.and('contain.text', 'active nodes');
|
.and('contain.text', 'active nodes');
|
||||||
});
|
});
|
||||||
|
|
||||||
it.skip('should have information on consensus nodes', function () {
|
it('should have information on consensus nodes', function () {
|
||||||
cy.getByTestId('node-information')
|
cy.getByTestId('node-information')
|
||||||
.last()
|
.last()
|
||||||
.should('contain.text', '2')
|
.should('contain.text', '1')
|
||||||
.and('contain.text', 'consensus nodes');
|
.and('contain.text', 'consensus nodes');
|
||||||
});
|
});
|
||||||
|
|
||||||
it.skip('should contain link to specific validators', function () {
|
it('should contain link to specific validators', function () {
|
||||||
cy.getByTestId('validators')
|
cy.getByTestId('validators')
|
||||||
.should('have.length', '2')
|
.should('have.length', '1')
|
||||||
.each(($validator) => {
|
.each(($validator) => {
|
||||||
cy.wrap($validator).find('a').should('have.attr', 'href');
|
cy.wrap($validator).find('a').should('have.attr', 'href');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it.skip('should have link for rewards page', function () {
|
it('should have link for rewards page', function () {
|
||||||
cy.getByTestId('home-rewards').within(() => {
|
cy.getByTestId('home-rewards').within(() => {
|
||||||
cy.get('[href="/rewards"]')
|
cy.get('[href="/rewards"]')
|
||||||
.first()
|
.first()
|
||||||
@@ -110,7 +110,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it.skip('should have link for withdrawal page', function () {
|
it('should have link for withdrawal page', function () {
|
||||||
cy.getByTestId('home-vega-token').within(() => {
|
cy.getByTestId('home-vega-token').within(() => {
|
||||||
cy.get('[href="/token/withdraw"]')
|
cy.get('[href="/token/withdraw"]')
|
||||||
.first()
|
.first()
|
||||||
@@ -132,7 +132,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 0006-NETW-003 0006-NETW-008 0006-NETW-009 0006-NETW-010 0006-NETW-012 0006-NETW-013 0006-NETW-017 0006-NETW-018 0006-NETW-019 0006-NETW-020
|
// 0006-NETW-003 0006-NETW-008 0006-NETW-009 0006-NETW-010 0006-NETW-012 0006-NETW-013 0006-NETW-017 0006-NETW-018 0006-NETW-019 0006-NETW-020
|
||||||
it.skip('should have option to switch to different network node', function () {
|
it('should have option to switch to different network node', function () {
|
||||||
cy.getByTestId('git-network-data').within(() => {
|
cy.getByTestId('git-network-data').within(() => {
|
||||||
cy.getByTestId('link').click();
|
cy.getByTestId('link').click();
|
||||||
});
|
});
|
||||||
@@ -159,7 +159,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
|||||||
cy.getByTestId('node-url-custom').click({ force: true });
|
cy.getByTestId('node-url-custom').click({ force: true });
|
||||||
cy.get('input').should('exist');
|
cy.get('input').should('exist');
|
||||||
cy.getByTestId('connect').should('be.disabled');
|
cy.getByTestId('connect').should('be.disabled');
|
||||||
cy.getByTestId('icon-cross').click();
|
cy.getByTestId('dialog-close').click();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display eth data', function () {
|
it('should display eth data', function () {
|
||||||
|
|||||||
@@ -33,12 +33,12 @@ context(
|
|||||||
verifyTabHighlighted(navigation.proposals);
|
verifyTabHighlighted(navigation.proposals);
|
||||||
});
|
});
|
||||||
|
|
||||||
it.skip('should have GOVERNANCE header visible', function () {
|
it('should have GOVERNANCE header visible', function () {
|
||||||
verifyPageHeader('Proposals');
|
verifyPageHeader('Proposals');
|
||||||
});
|
});
|
||||||
|
|
||||||
// 3002-PROP-023 3004-PMAC-002 3005-PASN-002 3006-PASC-002 3007-PNEC-002 3008-PFRO-003
|
// 3002-PROP-023 3004-PMAC-002 3005-PASN-002 3006-PASC-002 3007-PNEC-002 3008-PFRO-003
|
||||||
it.skip('new proposal page should have button for link to more information on proposals', function () {
|
it('new proposal page should have button for link to more information on proposals', function () {
|
||||||
cy.getByTestId('new-proposal-link').click();
|
cy.getByTestId('new-proposal-link').click();
|
||||||
cy.url().should('include', '/proposals/propose/raw');
|
cy.url().should('include', '/proposals/propose/raw');
|
||||||
cy.contains('To see Explorer data on proposals visit').within(() => {
|
cy.contains('To see Explorer data on proposals visit').within(() => {
|
||||||
@@ -73,7 +73,7 @@ context(
|
|||||||
navigateTo(navigation.proposals);
|
navigateTo(navigation.proposals);
|
||||||
});
|
});
|
||||||
|
|
||||||
it.skip('should be able to see a working link for - find out more about Vega governance', function () {
|
it('should be able to see a working link for - find out more about Vega governance', function () {
|
||||||
// 3001-VOTE-001 // 3002-PROP-001
|
// 3001-VOTE-001 // 3002-PROP-001
|
||||||
cy.getByTestId(proposalDocumentationLink)
|
cy.getByTestId(proposalDocumentationLink)
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
|
|||||||
@@ -40,10 +40,10 @@ context('View functionality with public key', { tags: '@smoke' }, function () {
|
|||||||
verifyConnectedToPubKey();
|
verifyConnectedToPubKey();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Able to connect public key via wallet and view assets in wallet', function () {
|
it.skip('Able to connect public key via wallet and view assets in wallet', function () {
|
||||||
verifyConnectedToPubKey();
|
verifyConnectedToPubKey();
|
||||||
cy.getByTestId('currency-title', { timeout: 10000 })
|
cy.getByTestId('currency-title', { timeout: 10000 })
|
||||||
.should('have.length.at.least', 4)
|
.should('have.length.at.least', 2)
|
||||||
.and('contain.text', 'USDC (fake)');
|
.and('contain.text', 'USDC (fake)');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -46,11 +46,11 @@ context('Validators Page - verify elements on page', function () {
|
|||||||
|
|
||||||
// @ts-ignore clash between jest and cypress
|
// @ts-ignore clash between jest and cypress
|
||||||
describe('with wallets disconnected', { tags: '@smoke' }, function () {
|
describe('with wallets disconnected', { tags: '@smoke' }, function () {
|
||||||
it.skip('Should have validators tab highlighted', function () {
|
it('Should have validators tab highlighted', function () {
|
||||||
verifyTabHighlighted(navigation.validators);
|
verifyTabHighlighted(navigation.validators);
|
||||||
});
|
});
|
||||||
|
|
||||||
it.skip('Should have validators ON VEGA header visible', function () {
|
it('Should have validators ON VEGA header visible', function () {
|
||||||
verifyPageHeader('Validators');
|
verifyPageHeader('Validators');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -192,7 +192,7 @@ context('Validators Page - verify elements on page', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 1002-STKE-006
|
// 1002-STKE-006
|
||||||
it.skip('Should be able to see validator name', function () {
|
it('Should be able to see validator name', function () {
|
||||||
cy.getByTestId(validatorTitle).should('not.be.empty');
|
cy.getByTestId(validatorTitle).should('not.be.empty');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/m
|
|||||||
|
|
||||||
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
|
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
|
||||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||||
NX_DELEGATIONS_PAGINATION=50
|
|
||||||
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
|
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
|
||||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/main/announcements.json
|
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/main/announcements.json
|
||||||
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
||||||
@@ -24,7 +23,7 @@ NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks
|
|||||||
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
|
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
|
||||||
|
|
||||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
|
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
|
||||||
|
|
||||||
#Test configuration variables
|
#Test configuration variables
|
||||||
CYPRESS_FAIRGROUND=false
|
CYPRESS_FAIRGROUND=false
|
||||||
@@ -33,7 +32,7 @@ LC_ALL="en_US.UTF-8"
|
|||||||
# Cosmic elevator flags
|
# Cosmic elevator flags
|
||||||
NX_SUCCESSOR_MARKETS=true
|
NX_SUCCESSOR_MARKETS=true
|
||||||
NX_METAMASK_SNAPS=true
|
NX_METAMASK_SNAPS=true
|
||||||
NX_PRODUCT_PERPETUALS=false
|
NX_PRODUCT_PERPETUALS=true
|
||||||
NX_UPDATE_MARKET_STATE=false
|
NX_UPDATE_MARKET_STATE=true
|
||||||
NX_REFERRALS=true
|
NX_REFERRALS=true
|
||||||
NX_GOVERNANCE_TRANSFERS=false
|
NX_GOVERNANCE_TRANSFERS=true
|
||||||
|
|||||||
@@ -15,12 +15,11 @@ NX_ETH_WALLET_MNEMONIC=ozone access unlock valid olympic save include omit suppl
|
|||||||
NX_LOCAL_PROVIDER_URL=http://localhost:8545/
|
NX_LOCAL_PROVIDER_URL=http://localhost:8545/
|
||||||
NX_VEGA_WALLET_URL=http://localhost:1789
|
NX_VEGA_WALLET_URL=http://localhost:1789
|
||||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||||
NX_DELEGATIONS_PAGINATION=50
|
|
||||||
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
|
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
|
||||||
NX_VEGA_REST_URL=http://localhost:3008/api/v2/
|
NX_VEGA_REST_URL=http://localhost:3008/api/v2/
|
||||||
|
|
||||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
|
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
|
||||||
|
|
||||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||||
|
|
||||||
|
|||||||
@@ -8,14 +8,13 @@ NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
|||||||
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
||||||
NX_VEGA_EXPLORER_URL=#
|
NX_VEGA_EXPLORER_URL=#
|
||||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||||
NX_DELEGATIONS_PAGINATION=50
|
|
||||||
NX_TRANCHES_SERVICE_URL=https://tranches-devnet1-k8s.ops.vega.xyz
|
NX_TRANCHES_SERVICE_URL=https://tranches-devnet1-k8s.ops.vega.xyz
|
||||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||||
NX_VEGA_REST_URL=https://api.n00.devnet1.vega.xyz/api/v2/
|
NX_VEGA_REST_URL=https://api.n00.devnet1.vega.xyz/api/v2/
|
||||||
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
||||||
|
|
||||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
|
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
|
||||||
|
|
||||||
NX_TENDERMINT_URL=https://tm.be.devnet1.vega.xyz/
|
NX_TENDERMINT_URL=https://tm.be.devnet1.vega.xyz/
|
||||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.devnet1.vega.xyz/websocket
|
NX_TENDERMINT_WEBSOCKET_URL=wss://be.devnet1.vega.xyz/websocket
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/
|
|||||||
NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz
|
NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz
|
||||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/mainnet
|
NX_VEGA_DOCS_URL=https://docs.vega.xyz/mainnet
|
||||||
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40:87edc2605e544f888305d7fc4a9141bd@o286262.ingest.sentry.io/5882996
|
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40:87edc2605e544f888305d7fc4a9141bd@o286262.ingest.sentry.io/5882996
|
||||||
NX_DELEGATIONS_PAGINATION=50
|
|
||||||
NX_TRANCHES_SERVICE_URL=https://tranches-mainnet-k8s.ops.vega.xyz
|
NX_TRANCHES_SERVICE_URL=https://tranches-mainnet-k8s.ops.vega.xyz
|
||||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
|
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
|
||||||
NX_VEGA_REST_URL=https://api.vega.community/api/v2/
|
NX_VEGA_REST_URL=https://api.vega.community/api/v2/
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
|||||||
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
||||||
NX_VEGA_EXPLORER_URL=https://explorer.mainnet-mirror.vega.rocks
|
NX_VEGA_EXPLORER_URL=https://explorer.mainnet-mirror.vega.rocks
|
||||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/mainnet
|
NX_VEGA_DOCS_URL=https://docs.vega.xyz/mainnet
|
||||||
NX_DELEGATIONS_PAGINATION=50
|
|
||||||
NX_TRANCHES_SERVICE_URL=https://tranches-mainnet-mirror-k8s.ops.vega.xyz
|
NX_TRANCHES_SERVICE_URL=https://tranches-mainnet-mirror-k8s.ops.vega.xyz
|
||||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
|
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
|
||||||
NX_VEGA_REST_URL=https://api.mainnet-mirror.vega.rocks/api/v2/
|
NX_VEGA_REST_URL=https://api.mainnet-mirror.vega.rocks/api/v2/
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ NX_VEGA_NETWORKS='{"DEVNET":"https://dev.governance.vega.xyz","STAGNET1":"https:
|
|||||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/stagnet1/vegawallet-stagnet1.toml
|
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/stagnet1/vegawallet-stagnet1.toml
|
||||||
NX_VEGA_EXPLORER_URL=https://explorer.stagnet1.vega.rocks
|
NX_VEGA_EXPLORER_URL=https://explorer.stagnet1.vega.rocks
|
||||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||||
NX_DELEGATIONS_PAGINATION=50
|
|
||||||
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
|
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
|
||||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||||
NX_VEGA_REST_URL=https://api.n00.stagnet1.vega.xyz/api/v2/
|
NX_VEGA_REST_URL=https://api.n00.stagnet1.vega.xyz/api/v2/
|
||||||
@@ -13,7 +12,7 @@ NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/m
|
|||||||
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
||||||
|
|
||||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
|
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
|
||||||
|
|
||||||
NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks
|
NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks
|
||||||
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
|
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
|||||||
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
|
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
|
||||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||||
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
|
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
|
||||||
NX_DELEGATIONS_PAGINATION=50
|
|
||||||
NX_TRANCHES_SERVICE_URL=https://tranches-testnet-k8s.ops.vega.xyz
|
NX_TRANCHES_SERVICE_URL=https://tranches-testnet-k8s.ops.vega.xyz
|
||||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||||
NX_VEGA_REST_URL=https://api.n07.testnet.vega.xyz/api/v2/
|
NX_VEGA_REST_URL=https://api.n07.testnet.vega.xyz/api/v2/
|
||||||
@@ -18,7 +17,7 @@ NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/m
|
|||||||
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
||||||
|
|
||||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
|
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
|
||||||
|
|
||||||
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
|
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
|
||||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
|
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/
|
|||||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||||
|
|
||||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
|
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
|
||||||
|
|
||||||
NX_TENDERMINT_URL=https://tm.be.validators-testnet.vega.rocks
|
NX_TENDERMINT_URL=https://tm.be.validators-testnet.vega.rocks
|
||||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.
|
NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.
|
||||||
|
|||||||
@@ -62,6 +62,9 @@ const cache: InMemoryCacheConfig = {
|
|||||||
Account: {
|
Account: {
|
||||||
keyFields: false,
|
keyFields: false,
|
||||||
},
|
},
|
||||||
|
Instrument: {
|
||||||
|
keyFields: ['code'],
|
||||||
|
},
|
||||||
Delegation: {
|
Delegation: {
|
||||||
keyFields: false,
|
keyFields: false,
|
||||||
// Only get full updates
|
// Only get full updates
|
||||||
|
|||||||
@@ -15,19 +15,16 @@ export const CollapsibleToggle = ({
|
|||||||
dataTestId,
|
dataTestId,
|
||||||
children,
|
children,
|
||||||
}: CollapsibleToggleProps) => {
|
}: CollapsibleToggleProps) => {
|
||||||
const classes = classnames(
|
const classes = classnames('transition-transform ease-in-out duration-300', {
|
||||||
'mb-4 transition-transform ease-in-out duration-300',
|
'rotate-180': toggleState,
|
||||||
{
|
});
|
||||||
'rotate-180': toggleState,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
onClick={() => setToggleState(!toggleState)}
|
onClick={() => setToggleState(!toggleState)}
|
||||||
data-testid={dataTestId}
|
data-testid={dataTestId}
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-baseline gap-3">
|
||||||
{children}
|
{children}
|
||||||
<div className={classes} data-testid="toggle-icon-wrapper">
|
<div className={classes} data-testid="toggle-icon-wrapper">
|
||||||
<VegaIcon name={VegaIconNames.CHEVRON_DOWN} size={20} />
|
<VegaIcon name={VegaIconNames.CHEVRON_DOWN} size={20} />
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
import { type ReactNode } from 'react';
|
||||||
|
|
||||||
interface HeadingProps {
|
interface HeadingProps {
|
||||||
title?: string;
|
title?: ReactNode;
|
||||||
centerContent?: boolean;
|
centerContent?: boolean;
|
||||||
marginTop?: boolean;
|
marginTop?: boolean;
|
||||||
marginBottom?: boolean;
|
marginBottom?: boolean;
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import keyBy from 'lodash/keyBy';
|
|||||||
import uniq from 'lodash/uniq';
|
import uniq from 'lodash/uniq';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { ENV } from '../../config';
|
|
||||||
|
|
||||||
import noIcon from '../../images/token-no-icon.png';
|
import noIcon from '../../images/token-no-icon.png';
|
||||||
import vegaBlack from '../../images/vega_black.png';
|
import vegaBlack from '../../images/vega_black.png';
|
||||||
@@ -37,7 +36,6 @@ export const usePollForDelegations = () => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { pubKey } = useVegaWallet();
|
const { pubKey } = useVegaWallet();
|
||||||
const client = useApolloClient();
|
const client = useApolloClient();
|
||||||
const { delegationsPagination } = ENV;
|
|
||||||
const [delegations, setDelegations] = React.useState<
|
const [delegations, setDelegations] = React.useState<
|
||||||
WalletDelegationFieldsFragment[]
|
WalletDelegationFieldsFragment[]
|
||||||
>([]);
|
>([]);
|
||||||
@@ -68,11 +66,9 @@ export const usePollForDelegations = () => {
|
|||||||
query: DelegationsDocument,
|
query: DelegationsDocument,
|
||||||
variables: {
|
variables: {
|
||||||
partyId: pubKey,
|
partyId: pubKey,
|
||||||
delegationsPagination: delegationsPagination
|
delegationsPagination: {
|
||||||
? {
|
first: 50,
|
||||||
first: Number(delegationsPagination),
|
},
|
||||||
}
|
|
||||||
: undefined,
|
|
||||||
},
|
},
|
||||||
fetchPolicy: 'network-only',
|
fetchPolicy: 'network-only',
|
||||||
})
|
})
|
||||||
@@ -236,14 +232,14 @@ export const usePollForDelegations = () => {
|
|||||||
// will just continue to fail
|
// will just continue to fail
|
||||||
clearInterval(interval);
|
clearInterval(interval);
|
||||||
});
|
});
|
||||||
}, 1000);
|
}, 20000);
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
clearInterval(interval);
|
clearInterval(interval);
|
||||||
mounted = false;
|
mounted = false;
|
||||||
};
|
};
|
||||||
}, [delegationsPagination, client, decimals, pubKey, t, vegaToken.address]);
|
}, [client, decimals, pubKey, t, vegaToken.address]);
|
||||||
|
|
||||||
return { delegations, currentStakeAvailable, delegatedNodes, accounts };
|
return { delegations, currentStakeAvailable, delegatedNodes, accounts };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -65,7 +65,6 @@ export const ENV = {
|
|||||||
docsUrl: windowOrDefault('NX_VEGA_DOCS_URL'),
|
docsUrl: windowOrDefault('NX_VEGA_DOCS_URL'),
|
||||||
ethWalletMnemonic: windowOrDefault('NX_ETH_WALLET_MNEMONIC'),
|
ethWalletMnemonic: windowOrDefault('NX_ETH_WALLET_MNEMONIC'),
|
||||||
localProviderUrl: windowOrDefault('NX_LOCAL_PROVIDER_URL'),
|
localProviderUrl: windowOrDefault('NX_LOCAL_PROVIDER_URL'),
|
||||||
delegationsPagination: windowOrDefault('NX_DELEGATIONS_PAGINATION'),
|
|
||||||
rest: windowOrDefault('NX_VEGA_REST_URL'),
|
rest: windowOrDefault('NX_VEGA_REST_URL'),
|
||||||
addresses:
|
addresses:
|
||||||
ContractAddresses[(envName === 'local' ? 'CUSTOM' : envName) as Networks],
|
ContractAddresses[(envName === 'local' ? 'CUSTOM' : envName) as Networks],
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ describe('getMultisigStatus', () => {
|
|||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
multisigStatus: MultisigStatus.noNodes,
|
multisigStatus: MultisigStatus.noNodes,
|
||||||
showMultisigStatusError: true,
|
showMultisigStatusError: true,
|
||||||
|
zeroScoreNodes: [],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -35,6 +36,7 @@ describe('getMultisigStatus', () => {
|
|||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
multisigStatus: MultisigStatus.correct,
|
multisigStatus: MultisigStatus.correct,
|
||||||
showMultisigStatusError: false,
|
showMultisigStatusError: false,
|
||||||
|
zeroScoreNodes: [],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -50,6 +52,22 @@ describe('getMultisigStatus', () => {
|
|||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
multisigStatus: MultisigStatus.nodeNeedsRemoving,
|
multisigStatus: MultisigStatus.nodeNeedsRemoving,
|
||||||
showMultisigStatusError: true,
|
showMultisigStatusError: true,
|
||||||
|
zeroScoreNodes: [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
rewardScore: {
|
||||||
|
multisigScore: '0',
|
||||||
|
},
|
||||||
|
stakedTotal: '1000',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
rewardScore: {
|
||||||
|
multisigScore: '0',
|
||||||
|
},
|
||||||
|
stakedTotal: '1000',
|
||||||
|
},
|
||||||
|
],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -65,6 +83,15 @@ describe('getMultisigStatus', () => {
|
|||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
multisigStatus: MultisigStatus.nodeNeedsAdding,
|
multisigStatus: MultisigStatus.nodeNeedsAdding,
|
||||||
showMultisigStatusError: true,
|
showMultisigStatusError: true,
|
||||||
|
zeroScoreNodes: [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
rewardScore: {
|
||||||
|
multisigScore: '0',
|
||||||
|
},
|
||||||
|
stakedTotal: '1000',
|
||||||
|
},
|
||||||
|
],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
||||||
import type { PreviousEpochQuery } from '../routes/staking/__generated__/PreviousEpoch';
|
import type {
|
||||||
|
PreviousEpochQuery,
|
||||||
|
ValidatorNodeFragment,
|
||||||
|
} from '../routes/staking/__generated__/PreviousEpoch';
|
||||||
|
|
||||||
export enum MultisigStatus {
|
export enum MultisigStatus {
|
||||||
'correct' = 'correct',
|
'correct' = 'correct',
|
||||||
@@ -17,12 +20,15 @@ export const getMultisigStatusInfo = (
|
|||||||
previousEpochData?.epoch.validatorsConnection?.edges
|
previousEpochData?.epoch.validatorsConnection?.edges
|
||||||
);
|
);
|
||||||
|
|
||||||
const hasZero = allNodesInPreviousEpoch.some(
|
const zeroScore = (node: ValidatorNodeFragment) =>
|
||||||
(node) => Number(node?.rewardScore?.multisigScore) === 0
|
Number(node.rewardScore?.multisigScore) === 0;
|
||||||
);
|
const oneScore = (node: ValidatorNodeFragment) =>
|
||||||
const hasOne = allNodesInPreviousEpoch.some(
|
Number(node.rewardScore?.multisigScore) === 1;
|
||||||
(node) => Number(node?.rewardScore?.multisigScore) === 1
|
|
||||||
);
|
const hasZero = allNodesInPreviousEpoch.some(zeroScore);
|
||||||
|
const hasOne = allNodesInPreviousEpoch.some(oneScore);
|
||||||
|
|
||||||
|
const zeroScoreNodes = allNodesInPreviousEpoch.filter(zeroScore);
|
||||||
|
|
||||||
if (hasZero && hasOne) {
|
if (hasZero && hasOne) {
|
||||||
// If any individual node has 0 it means that node is missing from the multisig and needs to be added
|
// If any individual node has 0 it means that node is missing from the multisig and needs to be added
|
||||||
@@ -38,5 +44,6 @@ export const getMultisigStatusInfo = (
|
|||||||
return {
|
return {
|
||||||
showMultisigStatusError: status !== MultisigStatus.correct,
|
showMultisigStatusError: status !== MultisigStatus.correct,
|
||||||
multisigStatus: status,
|
multisigStatus: status,
|
||||||
|
zeroScoreNodes,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
+1
-1
@@ -160,7 +160,7 @@ describe('Proposal header', () => {
|
|||||||
screen.queryByTestId('proposal-description')
|
screen.queryByTestId('proposal-description')
|
||||||
).not.toBeInTheDocument();
|
).not.toBeInTheDocument();
|
||||||
expect(screen.getByTestId('proposal-details')).toHaveTextContent(
|
expect(screen.getByTestId('proposal-details')).toHaveTextContent(
|
||||||
'Update to market ID: MarketId'
|
'Update to market: MarketId'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+61
-29
@@ -36,6 +36,8 @@ import { type Proposal, type BatchProposal } from '../../types';
|
|||||||
import { type ProposalTermsFieldsFragment } from '../../__generated__/Proposals';
|
import { type ProposalTermsFieldsFragment } from '../../__generated__/Proposals';
|
||||||
import { differenceInHours, format, formatDistanceToNowStrict } from 'date-fns';
|
import { differenceInHours, format, formatDistanceToNowStrict } from 'date-fns';
|
||||||
import { DATE_FORMAT_DETAILED } from '../../../../lib/date-formats';
|
import { DATE_FORMAT_DETAILED } from '../../../../lib/date-formats';
|
||||||
|
import { MarketName } from '../proposal/market-name';
|
||||||
|
import { Indicator } from '../proposal/indicator';
|
||||||
|
|
||||||
const ProposalTypeTags = ({
|
const ProposalTypeTags = ({
|
||||||
proposal,
|
proposal,
|
||||||
@@ -52,11 +54,8 @@ const ProposalTypeTags = ({
|
|||||||
|
|
||||||
if (proposal.__typename === 'BatchProposal') {
|
if (proposal.__typename === 'BatchProposal') {
|
||||||
return (
|
return (
|
||||||
<div data-testid="proposal-type" className="flex gap-1">
|
<div data-testid="proposal-type">
|
||||||
{proposal.subProposals?.map((subProposal, i) => {
|
<ProposalInfoLabel variant="secondary">BatchProposal</ProposalInfoLabel>
|
||||||
if (!subProposal?.terms) return null;
|
|
||||||
return <ProposalTypeTag key={i} terms={subProposal.terms} />;
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -138,50 +137,77 @@ const ProposalDetails = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
case 'UpdateMarketState': {
|
case 'UpdateMarketState': {
|
||||||
|
const marketPageLink = consoleLink(
|
||||||
|
CONSOLE_MARKET_PAGE.replace(':marketId', terms.change.market.id)
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
{featureFlags.UPDATE_MARKET_STATE &&
|
{featureFlags.UPDATE_MARKET_STATE &&
|
||||||
terms.change?.market?.id &&
|
terms.change?.market?.id &&
|
||||||
terms.change.updateType ? (
|
terms.change.updateType ? (
|
||||||
<>
|
<>
|
||||||
{t(terms.change.updateType)}:{' '}
|
<span>{t(terms.change.updateType)}: </span>
|
||||||
{truncateMiddle(terms.change.market.id)}
|
<span className="inline-flex gap-2">
|
||||||
|
<span className="break-all">
|
||||||
|
<MarketName marketId={terms.change.market.id} />
|
||||||
|
</span>
|
||||||
|
<span className="inline-flex items-end gap-0">
|
||||||
|
<CopyWithTooltip
|
||||||
|
text={terms.change.market.id}
|
||||||
|
description={t('copyId')}
|
||||||
|
>
|
||||||
|
<button className="inline-block px-1">
|
||||||
|
<VegaIcon size={20} name={VegaIconNames.COPY} />
|
||||||
|
</button>
|
||||||
|
</CopyWithTooltip>
|
||||||
|
<Tooltip description={t('OpenInConsole')} align="center">
|
||||||
|
<Link
|
||||||
|
className="inline-block px-1"
|
||||||
|
to={marketPageLink}
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<VegaIcon
|
||||||
|
size={20}
|
||||||
|
name={VegaIconNames.OPEN_EXTERNAL}
|
||||||
|
/>
|
||||||
|
</Link>
|
||||||
|
</Tooltip>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
case 'UpdateMarket': {
|
case 'UpdateMarket': {
|
||||||
|
const marketPageLink = consoleLink(
|
||||||
|
CONSOLE_MARKET_PAGE.replace(':marketId', terms.change.marketId)
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<span>{t('UpdateToMarket')}:</span>{' '}
|
<span>{t('UpdateToMarket')}: </span>
|
||||||
<span className="inline-flex items-start gap-2">
|
<span className="inline-flex items-start gap-2">
|
||||||
<span className="break-all">{terms.change.marketId} </span>
|
<span className="break-all">
|
||||||
|
<MarketName marketId={terms.change.marketId} />
|
||||||
|
</span>
|
||||||
<span className="inline-flex items-end gap-0">
|
<span className="inline-flex items-end gap-0">
|
||||||
<CopyWithTooltip
|
<CopyWithTooltip
|
||||||
text={terms.change.marketId}
|
text={terms.change.marketId}
|
||||||
description={t('copyToClipboard')}
|
description={t('copyId')}
|
||||||
>
|
>
|
||||||
<button className="inline-block px-1">
|
<button className="inline-block px-1">
|
||||||
<VegaIcon size={20} name={VegaIconNames.COPY} />
|
<VegaIcon size={20} name={VegaIconNames.COPY} />
|
||||||
</button>
|
</button>
|
||||||
</CopyWithTooltip>
|
</CopyWithTooltip>
|
||||||
<Tooltip description={t('OpenInConsole')} align="center">
|
<Tooltip description={t('OpenInConsole')} align="center">
|
||||||
<button
|
<Link
|
||||||
className="inline-block px-1"
|
className="inline-block px-1"
|
||||||
onClick={() => {
|
target="_blank"
|
||||||
const marketPageLink = consoleLink(
|
to={marketPageLink}
|
||||||
CONSOLE_MARKET_PAGE.replace(
|
|
||||||
':marketId',
|
|
||||||
// @ts-ignore ts doesn't like this field even though its already a string above???
|
|
||||||
terms.change.marketId
|
|
||||||
)
|
|
||||||
);
|
|
||||||
window.open(marketPageLink, '_blank');
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<VegaIcon size={20} name={VegaIconNames.OPEN_EXTERNAL} />
|
<VegaIcon size={20} name={VegaIconNames.OPEN_EXTERNAL} />
|
||||||
</button>
|
</Link>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
@@ -224,7 +250,7 @@ const ProposalDetails = ({
|
|||||||
}}
|
}}
|
||||||
components={{
|
components={{
|
||||||
// @ts-ignore children passed by i18next
|
// @ts-ignore children passed by i18next
|
||||||
lozenge: <Lozenge />,
|
lozenge: <Lozenge className="text-xs" />,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -286,12 +312,18 @@ const ProposalDetails = ({
|
|||||||
{proposal.subProposals.map((p, i) => {
|
{proposal.subProposals.map((p, i) => {
|
||||||
if (!p?.terms) return null;
|
if (!p?.terms) return null;
|
||||||
return (
|
return (
|
||||||
<li key={i}>
|
<li
|
||||||
<div>{renderDetails(p.terms)}</div>
|
key={i}
|
||||||
<SubProposalStateText
|
className="grid grid-cols-[40px_minmax(0,1fr)] grid-rows-1 gap-3 items-center"
|
||||||
state={proposal.state}
|
>
|
||||||
enactmentDatetime={p.terms.enactmentDatetime}
|
<Indicator indicator={i + 1} />
|
||||||
/>
|
<span>
|
||||||
|
<div>{renderDetails(p.terms)}</div>
|
||||||
|
<SubProposalStateText
|
||||||
|
state={proposal.state}
|
||||||
|
enactmentDatetime={p.terms.enactmentDatetime}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -3,16 +3,8 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
|
import { SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
|
||||||
import { SubHeading } from '../../../../components/heading';
|
import { SubHeading } from '../../../../components/heading';
|
||||||
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
|
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
|
||||||
import {
|
|
||||||
type BatchProposalFieldsFragment,
|
|
||||||
type ProposalFieldsFragment,
|
|
||||||
} from '../../__generated__/Proposals';
|
|
||||||
|
|
||||||
export const ProposalJson = ({
|
export const ProposalJson = ({ proposal }: { proposal?: unknown }) => {
|
||||||
proposal,
|
|
||||||
}: {
|
|
||||||
proposal: ProposalFieldsFragment | BatchProposalFieldsFragment;
|
|
||||||
}) => {
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [showDetails, setShowDetails] = useState(false);
|
const [showDetails, setShowDetails] = useState(false);
|
||||||
|
|
||||||
@@ -26,7 +18,7 @@ export const ProposalJson = ({
|
|||||||
<SubHeading title={t('proposalJson')} />
|
<SubHeading title={t('proposalJson')} />
|
||||||
</CollapsibleToggle>
|
</CollapsibleToggle>
|
||||||
|
|
||||||
{showDetails && <SyntaxHighlighter data={proposal} />}
|
{showDetails && <SyntaxHighlighter size="smaller" data={proposal} />}
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+8
-3
@@ -5,6 +5,11 @@ import {
|
|||||||
} from './proposal-market-changes';
|
} from './proposal-market-changes';
|
||||||
import type { JsonValue } from '../../../../components/json-diff';
|
import type { JsonValue } from '../../../../components/json-diff';
|
||||||
|
|
||||||
|
jest.mock('../proposal/market-name.tsx', () => ({
|
||||||
|
...jest.requireActual('../proposal/market-name.tsx'),
|
||||||
|
MarketName: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
describe('applyImmutableKeysFromEarlierVersion', () => {
|
describe('applyImmutableKeysFromEarlierVersion', () => {
|
||||||
it('returns an empty object if any argument is not an object or null', () => {
|
it('returns an empty object if any argument is not an object or null', () => {
|
||||||
const earlierVersion: JsonValue = null;
|
const earlierVersion: JsonValue = null;
|
||||||
@@ -57,21 +62,21 @@ describe('applyImmutableKeysFromEarlierVersion', () => {
|
|||||||
describe('ProposalMarketChanges', () => {
|
describe('ProposalMarketChanges', () => {
|
||||||
it('renders correctly', () => {
|
it('renders correctly', () => {
|
||||||
const { getByTestId } = render(
|
const { getByTestId } = render(
|
||||||
<ProposalMarketChanges marketId="market-id" updatedProposal={{}} />
|
<ProposalMarketChanges marketId="market-id" updateProposalNode={null} />
|
||||||
);
|
);
|
||||||
expect(getByTestId('proposal-market-changes')).toBeInTheDocument();
|
expect(getByTestId('proposal-market-changes')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('JsonDiff is not visible when showChanges is false', () => {
|
it('JsonDiff is not visible when showChanges is false', () => {
|
||||||
const { queryByTestId } = render(
|
const { queryByTestId } = render(
|
||||||
<ProposalMarketChanges marketId="market-id" updatedProposal={{}} />
|
<ProposalMarketChanges marketId="market-id" updateProposalNode={null} />
|
||||||
);
|
);
|
||||||
expect(queryByTestId('json-diff')).not.toBeInTheDocument();
|
expect(queryByTestId('json-diff')).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('JsonDiff is visible when showChanges is true', async () => {
|
it('JsonDiff is visible when showChanges is true', async () => {
|
||||||
const { getByTestId } = render(
|
const { getByTestId } = render(
|
||||||
<ProposalMarketChanges marketId="market-id" updatedProposal={{}} />
|
<ProposalMarketChanges marketId="market-id" updateProposalNode={null} />
|
||||||
);
|
);
|
||||||
fireEvent.click(getByTestId('proposal-market-changes-toggle'));
|
fireEvent.click(getByTestId('proposal-market-changes-toggle'));
|
||||||
expect(getByTestId('json-diff')).toBeInTheDocument();
|
expect(getByTestId('json-diff')).toBeInTheDocument();
|
||||||
|
|||||||
+92
-53
@@ -1,14 +1,24 @@
|
|||||||
import cloneDeep from 'lodash/cloneDeep';
|
import cloneDeep from 'lodash/cloneDeep';
|
||||||
import set from 'lodash/set';
|
import set from 'lodash/set';
|
||||||
import get from 'lodash/get';
|
import get from 'lodash/get';
|
||||||
import { JsonDiff } from '../../../../components/json-diff';
|
import compact from 'lodash/compact';
|
||||||
|
import orderBy from 'lodash/orderBy';
|
||||||
|
import { JsonDiff, type JsonValue } from '../../../../components/json-diff';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
|
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
|
||||||
import { SubHeading } from '../../../../components/heading';
|
import { SubHeading } from '../../../../components/heading';
|
||||||
import type { JsonValue } from '../../../../components/json-diff';
|
import {
|
||||||
import { useFetch } from '@vegaprotocol/react-helpers';
|
useFetchProposal,
|
||||||
import { ENV } from '../../../../config';
|
useFetchProposals,
|
||||||
|
flatten,
|
||||||
|
isBatchProposalNode,
|
||||||
|
isSingleProposalNode,
|
||||||
|
type ProposalNode,
|
||||||
|
type SingleProposalData,
|
||||||
|
type SubProposalData,
|
||||||
|
} from '../proposal/proposal-utils';
|
||||||
|
import { MarketName } from '../proposal/market-name';
|
||||||
|
|
||||||
const immutableKeys = [
|
const immutableKeys = [
|
||||||
'decimalPlaces',
|
'decimalPlaces',
|
||||||
@@ -18,8 +28,8 @@ const immutableKeys = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export const applyImmutableKeysFromEarlierVersion = (
|
export const applyImmutableKeysFromEarlierVersion = (
|
||||||
earlierVersion: JsonValue,
|
earlierVersion: unknown,
|
||||||
updatedVersion: JsonValue
|
updatedVersion: unknown
|
||||||
) => {
|
) => {
|
||||||
if (
|
if (
|
||||||
typeof earlierVersion !== 'object' ||
|
typeof earlierVersion !== 'object' ||
|
||||||
@@ -35,7 +45,8 @@ export const applyImmutableKeysFromEarlierVersion = (
|
|||||||
|
|
||||||
// Overwrite the immutable keys in the updatedVersionCopy with the earlier values
|
// Overwrite the immutable keys in the updatedVersionCopy with the earlier values
|
||||||
immutableKeys.forEach((key) => {
|
immutableKeys.forEach((key) => {
|
||||||
set(updatedVersionCopy, key, get(earlierVersion, key));
|
const earlier = get(earlierVersion, key);
|
||||||
|
if (earlier) set(updatedVersionCopy, key, earlier);
|
||||||
});
|
});
|
||||||
|
|
||||||
return updatedVersionCopy;
|
return updatedVersionCopy;
|
||||||
@@ -43,49 +54,84 @@ export const applyImmutableKeysFromEarlierVersion = (
|
|||||||
|
|
||||||
interface ProposalMarketChangesProps {
|
interface ProposalMarketChangesProps {
|
||||||
marketId: string;
|
marketId: string;
|
||||||
updatedProposal: JsonValue;
|
/** This are the changes from proposal */
|
||||||
|
updateProposalNode: ProposalNode | null;
|
||||||
|
indicator?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ProposalMarketChanges = ({
|
export const ProposalMarketChanges = ({
|
||||||
marketId,
|
marketId,
|
||||||
updatedProposal,
|
updateProposalNode,
|
||||||
|
indicator,
|
||||||
}: ProposalMarketChangesProps) => {
|
}: ProposalMarketChangesProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [showChanges, setShowChanges] = useState(false);
|
const [showChanges, setShowChanges] = useState(false);
|
||||||
|
|
||||||
const {
|
const { data: originalProposalData } = useFetchProposal({
|
||||||
state: { data },
|
proposalId: marketId,
|
||||||
} = useFetch(`${ENV.rest}governance?proposalId=${marketId}`, undefined, true);
|
});
|
||||||
|
|
||||||
const {
|
const { data: enactedProposalsData } = useFetchProposals({
|
||||||
state: { data: enactedProposalData },
|
proposalState: 'STATE_ENACTED',
|
||||||
} = useFetch(
|
proposalType: 'TYPE_UPDATE_MARKET',
|
||||||
`${ENV.rest}governances?proposalState=STATE_ENACTED&proposalType=TYPE_UPDATE_MARKET`,
|
});
|
||||||
undefined,
|
|
||||||
true
|
let updateProposal: SingleProposalData | SubProposalData | undefined;
|
||||||
|
if (isBatchProposalNode(updateProposalNode)) {
|
||||||
|
updateProposal = updateProposalNode.proposals.find(
|
||||||
|
(p, i) =>
|
||||||
|
p.terms.updateMarket?.marketId === marketId &&
|
||||||
|
(indicator != null ? i === indicator - 1 : true)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (isSingleProposalNode(updateProposalNode)) {
|
||||||
|
updateProposal = updateProposalNode.proposal;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this should get the proposal before the current one
|
||||||
|
const enactedUpdateMarketProposals = orderBy(
|
||||||
|
compact(
|
||||||
|
flatten(enactedProposalsData).filter((enacted) => {
|
||||||
|
const related = enacted.terms.updateMarket?.marketId === marketId;
|
||||||
|
const notCurrent =
|
||||||
|
enacted.id !== updateProposal?.id ||
|
||||||
|
('batchId' in enacted && enacted.batchId !== updateProposal.id);
|
||||||
|
const beforeCurrent =
|
||||||
|
Number(enacted.terms.enactmentTimestamp) <
|
||||||
|
Number(updateProposal?.terms.enactmentTimestamp);
|
||||||
|
return related && notCurrent && beforeCurrent;
|
||||||
|
})
|
||||||
|
),
|
||||||
|
[(proposal) => Number(proposal.terms.enactmentTimestamp)],
|
||||||
|
'desc'
|
||||||
);
|
);
|
||||||
|
|
||||||
// @ts-ignore no types here :-/
|
const latestEnactedProposal =
|
||||||
const enacted = enactedProposalData?.connection?.edges
|
enactedUpdateMarketProposals.length > 0
|
||||||
.filter(
|
? enactedUpdateMarketProposals[0]
|
||||||
// @ts-ignore no type here
|
: undefined;
|
||||||
({ node }) => node?.proposal?.terms?.updateMarket?.marketId === marketId
|
|
||||||
)
|
|
||||||
// @ts-ignore no type here
|
|
||||||
.sort((a, b) => {
|
|
||||||
return (
|
|
||||||
new Date(a?.node?.terms?.enactmentTimestamp).getTime() -
|
|
||||||
new Date(b?.node?.terms?.enactmentTimestamp).getTime()
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const latestEnactedProposal = enacted?.length
|
let originalProposal;
|
||||||
? enacted[enacted.length - 1]
|
if (isBatchProposalNode(originalProposalData)) {
|
||||||
: undefined;
|
originalProposal = originalProposalData.proposals.find(
|
||||||
|
(proposal) => proposal.id === marketId && proposal.terms.newMarket != null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (isSingleProposalNode(originalProposalData)) {
|
||||||
|
originalProposal = originalProposalData.proposal;
|
||||||
|
}
|
||||||
|
|
||||||
const originalProposal =
|
// LEFT SIDE: update market proposal enacted just before this one
|
||||||
// @ts-ignore no types with useFetch TODO: check this is good
|
// or original new market proposal
|
||||||
data?.data?.proposal?.terms?.newMarket?.changes;
|
const left =
|
||||||
|
latestEnactedProposal?.terms.updateMarket?.changes ||
|
||||||
|
originalProposal?.terms.newMarket?.changes;
|
||||||
|
|
||||||
|
// RIGHT SIDE: this update market proposal
|
||||||
|
const right = applyImmutableKeysFromEarlierVersion(
|
||||||
|
left,
|
||||||
|
updateProposal?.terms.updateMarket?.changes
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section data-testid="proposal-market-changes">
|
<section data-testid="proposal-market-changes">
|
||||||
@@ -94,25 +140,18 @@ export const ProposalMarketChanges = ({
|
|||||||
setToggleState={setShowChanges}
|
setToggleState={setShowChanges}
|
||||||
dataTestId={'proposal-market-changes-toggle'}
|
dataTestId={'proposal-market-changes-toggle'}
|
||||||
>
|
>
|
||||||
<SubHeading title={t('updatesToMarket')} />
|
<SubHeading
|
||||||
|
title={
|
||||||
|
<>
|
||||||
|
{t('UpdateToMarket')}: <MarketName marketId={marketId} truncate />
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
</CollapsibleToggle>
|
</CollapsibleToggle>
|
||||||
|
|
||||||
{showChanges && (
|
{showChanges && (
|
||||||
<div className="mb-6">
|
<div className="mb-6 bg-vega-cdark-900 p-2 rounded-lg">
|
||||||
<JsonDiff
|
<JsonDiff left={left as JsonValue} right={right as JsonValue} />
|
||||||
left={latestEnactedProposal || originalProposal}
|
|
||||||
right={
|
|
||||||
latestEnactedProposal
|
|
||||||
? applyImmutableKeysFromEarlierVersion(
|
|
||||||
latestEnactedProposal,
|
|
||||||
updatedProposal
|
|
||||||
)
|
|
||||||
: applyImmutableKeysFromEarlierVersion(
|
|
||||||
originalProposal,
|
|
||||||
updatedProposal
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
+5
@@ -2,6 +2,11 @@ import { fireEvent, render, screen } from '@testing-library/react';
|
|||||||
import { ProposalUpdateMarketState } from './proposal-update-market-state';
|
import { ProposalUpdateMarketState } from './proposal-update-market-state';
|
||||||
import { MarketUpdateType } from '@vegaprotocol/types';
|
import { MarketUpdateType } from '@vegaprotocol/types';
|
||||||
|
|
||||||
|
jest.mock('../proposal/market-name.tsx', () => ({
|
||||||
|
...jest.requireActual('../proposal/market-name.tsx'),
|
||||||
|
MarketName: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
describe('<ProposalUpdateMarketState />', () => {
|
describe('<ProposalUpdateMarketState />', () => {
|
||||||
const suspendProposal = {
|
const suspendProposal = {
|
||||||
__typename: 'UpdateMarketState' as const,
|
__typename: 'UpdateMarketState' as const,
|
||||||
|
|||||||
+23
-7
@@ -9,6 +9,8 @@ import { useState } from 'react';
|
|||||||
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
|
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
|
||||||
import { SubHeading } from '../../../../components/heading';
|
import { SubHeading } from '../../../../components/heading';
|
||||||
import { type UpdateMarketStatesFragment } from '../../__generated__/Proposals';
|
import { type UpdateMarketStatesFragment } from '../../__generated__/Proposals';
|
||||||
|
import { MarketUpdateTypeMapping } from '@vegaprotocol/types';
|
||||||
|
import { MarketName } from '../proposal/market-name';
|
||||||
|
|
||||||
interface ProposalUpdateMarketStateProps {
|
interface ProposalUpdateMarketStateProps {
|
||||||
change: UpdateMarketStatesFragment | null;
|
change: UpdateMarketStatesFragment | null;
|
||||||
@@ -19,16 +21,18 @@ export const ProposalUpdateMarketState = ({
|
|||||||
}: ProposalUpdateMarketStateProps) => {
|
}: ProposalUpdateMarketStateProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [showDetails, setShowDetails] = useState(false);
|
const [showDetails, setShowDetails] = useState(false);
|
||||||
let market;
|
|
||||||
let isTerminate = false;
|
|
||||||
|
|
||||||
if (!change) {
|
if (!change || change.__typename !== 'UpdateMarketState') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (change.__typename === 'UpdateMarketState') {
|
const market = change?.market;
|
||||||
market = change?.market;
|
const isTerminate =
|
||||||
isTerminate = change?.updateType === 'MARKET_STATE_UPDATE_TYPE_TERMINATE';
|
change?.updateType === 'MARKET_STATE_UPDATE_TYPE_TERMINATE';
|
||||||
|
|
||||||
|
let toggleTitle = t(change.updateType);
|
||||||
|
if (toggleTitle.length === 0) {
|
||||||
|
toggleTitle = t('MarketDetails');
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -38,7 +42,13 @@ export const ProposalUpdateMarketState = ({
|
|||||||
setToggleState={setShowDetails}
|
setToggleState={setShowDetails}
|
||||||
dataTestId="proposal-market-data-toggle"
|
dataTestId="proposal-market-data-toggle"
|
||||||
>
|
>
|
||||||
<SubHeading title={t('MarketDetails')} />
|
<SubHeading
|
||||||
|
title={
|
||||||
|
<>
|
||||||
|
{toggleTitle}: <MarketName marketId={market?.id} />
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
</CollapsibleToggle>
|
</CollapsibleToggle>
|
||||||
|
|
||||||
{showDetails && (
|
{showDetails && (
|
||||||
@@ -49,6 +59,12 @@ export const ProposalUpdateMarketState = ({
|
|||||||
{t('marketId')}
|
{t('marketId')}
|
||||||
{market?.id}
|
{market?.id}
|
||||||
</KeyValueTableRow>
|
</KeyValueTableRow>
|
||||||
|
<KeyValueTableRow>
|
||||||
|
{t('State')}
|
||||||
|
<span className="bg-vega-green-650 px-1">
|
||||||
|
{MarketUpdateTypeMapping[change.updateType]}
|
||||||
|
</span>
|
||||||
|
</KeyValueTableRow>
|
||||||
<KeyValueTableRow>
|
<KeyValueTableRow>
|
||||||
{t('marketName')}
|
{t('marketName')}
|
||||||
{market?.tradableInstrument?.instrument?.name}
|
{market?.tradableInstrument?.instrument?.name}
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
// rainbow-ish order
|
||||||
|
const COLOURS = ['red', 'pink', 'orange', 'yellow', 'green', 'blue', 'purple'];
|
||||||
|
|
||||||
|
const getColour = (indicator: number, max = COLOURS.length) => {
|
||||||
|
const available =
|
||||||
|
max < COLOURS.length ? COLOURS.slice(COLOURS.length - max) : COLOURS;
|
||||||
|
const tiers = Object.keys(available).length;
|
||||||
|
let index = Math.abs(indicator - 1);
|
||||||
|
if (indicator >= tiers) {
|
||||||
|
index = index % tiers;
|
||||||
|
}
|
||||||
|
return available[index];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getStyle = (indicator: number, max = COLOURS.length) =>
|
||||||
|
classNames({
|
||||||
|
'bg-vega-yellow-400 before:bg-vega-yellow-400':
|
||||||
|
'yellow' === getColour(indicator, max),
|
||||||
|
'bg-vega-green-400 before:bg-vega-green-400':
|
||||||
|
'green' === getColour(indicator, max),
|
||||||
|
'bg-vega-blue-400 before:bg-vega-blue-400':
|
||||||
|
'blue' === getColour(indicator, max),
|
||||||
|
'bg-vega-purple-400 before:bg-vega-purple-400':
|
||||||
|
'purple' === getColour(indicator, max),
|
||||||
|
'bg-vega-pink-400 before:bg-vega-pink-400':
|
||||||
|
'pink' === getColour(indicator, max),
|
||||||
|
'bg-vega-orange-400 before:bg-vega-orange-400':
|
||||||
|
'orange' === getColour(indicator, max),
|
||||||
|
'bg-vega-red-400 before:bg-vega-red-400':
|
||||||
|
'red' === getColour(indicator, max),
|
||||||
|
'bg-vega-clight-600 before:bg-vega-clight-600':
|
||||||
|
'none' === getColour(indicator, max),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getIndicatorStyle = (indicator: number) =>
|
||||||
|
classNames(
|
||||||
|
'rounded-sm text-black inline-block px-1 py-1 font-alpha calt h-8 w-7 text-center',
|
||||||
|
'text-border-1',
|
||||||
|
getStyle(indicator),
|
||||||
|
// Comment below if you want to remove the "chevron"
|
||||||
|
'relative mr-[11px] z-1',
|
||||||
|
'before:absolute before:z-0 before:top-1 before:right-[-11px] before:rounded-sm',
|
||||||
|
"before:w-[22.62px] before:h-[22.62px] before:rotate-45 before:content-['']"
|
||||||
|
);
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { getIndicatorStyle } from './colours';
|
||||||
|
|
||||||
|
export const Indicator = ({ indicator }: { indicator: number }) => (
|
||||||
|
<div className={getIndicatorStyle(indicator)}>
|
||||||
|
<span className="absolute top-0 left-0 p-1 w-full text-center z-1">
|
||||||
|
{indicator}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { useMarketInfoQuery } from '@vegaprotocol/markets';
|
||||||
|
import { truncateMiddle } from '@vegaprotocol/ui-toolkit';
|
||||||
|
|
||||||
|
export const MarketName = ({
|
||||||
|
marketId,
|
||||||
|
truncate,
|
||||||
|
}: {
|
||||||
|
marketId?: string;
|
||||||
|
truncate?: boolean;
|
||||||
|
}) => {
|
||||||
|
const { data } = useMarketInfoQuery({
|
||||||
|
variables: {
|
||||||
|
marketId: marketId || '',
|
||||||
|
},
|
||||||
|
skip: !marketId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const id = truncate ? truncateMiddle(marketId || '') : marketId;
|
||||||
|
|
||||||
|
return <span>{data?.market?.tradableInstrument.instrument.code || id}</span>;
|
||||||
|
};
|
||||||
+67
-26
@@ -1,3 +1,4 @@
|
|||||||
|
import { Trans, useTranslation } from 'react-i18next';
|
||||||
import { type ProposalTermsFieldsFragment } from '../../__generated__/Proposals';
|
import { type ProposalTermsFieldsFragment } from '../../__generated__/Proposals';
|
||||||
import { type Proposal, type BatchProposal } from '../../types';
|
import { type Proposal, type BatchProposal } from '../../types';
|
||||||
import { ListAsset } from '../list-asset';
|
import { ListAsset } from '../list-asset';
|
||||||
@@ -12,21 +13,29 @@ import {
|
|||||||
import { ProposalUpdateBenefitTiers } from '../proposal-update-benefit-tiers';
|
import { ProposalUpdateBenefitTiers } from '../proposal-update-benefit-tiers';
|
||||||
import { ProposalUpdateMarketState } from '../proposal-update-market-state';
|
import { ProposalUpdateMarketState } from '../proposal-update-market-state';
|
||||||
import { ProposalVolumeDiscountProgramDetails } from '../proposal-volume-discount-program-details';
|
import { ProposalVolumeDiscountProgramDetails } from '../proposal-volume-discount-program-details';
|
||||||
|
import { type ProposalNode } from './proposal-utils';
|
||||||
|
import { Lozenge } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import { Indicator } from './indicator';
|
||||||
|
import { SubHeading } from '../../../../components/heading';
|
||||||
|
|
||||||
export const ProposalChangeDetails = ({
|
export const ProposalChangeDetails = ({
|
||||||
proposal,
|
proposal,
|
||||||
terms,
|
terms,
|
||||||
restData,
|
restData,
|
||||||
|
indicator,
|
||||||
}: {
|
}: {
|
||||||
proposal: Proposal | BatchProposal;
|
proposal: Proposal | BatchProposal;
|
||||||
terms: ProposalTermsFieldsFragment;
|
terms: ProposalTermsFieldsFragment;
|
||||||
// eslint-disable-next-line
|
restData: ProposalNode | null;
|
||||||
restData: any;
|
indicator?: number;
|
||||||
}) => {
|
}) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
let details = null;
|
||||||
|
|
||||||
switch (terms.change.__typename) {
|
switch (terms.change.__typename) {
|
||||||
case 'NewAsset': {
|
case 'NewAsset': {
|
||||||
if (proposal.id && terms.change.source.__typename === 'ERC20') {
|
if (proposal.id && terms.change.source.__typename === 'ERC20') {
|
||||||
return (
|
details = (
|
||||||
<div>
|
<div>
|
||||||
<ListAsset
|
<ListAsset
|
||||||
assetId={proposal.id}
|
assetId={proposal.id}
|
||||||
@@ -37,62 +46,64 @@ export const ProposalChangeDetails = ({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return null;
|
break;
|
||||||
}
|
}
|
||||||
case 'UpdateAsset': {
|
case 'UpdateAsset': {
|
||||||
if (proposal.id) {
|
if (proposal.id) {
|
||||||
return (
|
details = (
|
||||||
<ProposalAssetDetails
|
<ProposalAssetDetails
|
||||||
change={terms.change}
|
change={terms.change}
|
||||||
assetId={terms.change.assetId}
|
assetId={terms.change.assetId}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return null;
|
break;
|
||||||
}
|
}
|
||||||
case 'NewMarket': {
|
case 'NewMarket': {
|
||||||
if (proposal.id) {
|
if (proposal.id) {
|
||||||
return <ProposalMarketData proposalId={proposal.id} />;
|
details = <ProposalMarketData proposalId={proposal.id} />;
|
||||||
}
|
}
|
||||||
return null;
|
break;
|
||||||
}
|
}
|
||||||
case 'UpdateMarket': {
|
case 'UpdateMarket': {
|
||||||
if (proposal.id) {
|
if (proposal.id) {
|
||||||
return (
|
details = (
|
||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-4">
|
||||||
<ProposalMarketData proposalId={proposal.id} />
|
<ProposalMarketData proposalId={proposal.id} />
|
||||||
<ProposalMarketChanges
|
<ProposalMarketChanges
|
||||||
|
indicator={indicator}
|
||||||
marketId={terms.change.marketId}
|
marketId={terms.change.marketId}
|
||||||
updatedProposal={
|
updateProposalNode={restData}
|
||||||
restData?.data?.proposal?.terms?.updateMarket?.changes
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
break;
|
||||||
}
|
}
|
||||||
case 'NewTransfer': {
|
case 'NewTransfer': {
|
||||||
if (proposal.id) {
|
if (proposal.id) {
|
||||||
return <ProposalTransferDetails proposalId={proposal.id} />;
|
details = <ProposalTransferDetails proposalId={proposal.id} />;
|
||||||
}
|
}
|
||||||
return null;
|
break;
|
||||||
}
|
}
|
||||||
case 'CancelTransfer': {
|
case 'CancelTransfer': {
|
||||||
if (proposal.id) {
|
if (proposal.id) {
|
||||||
return <ProposalCancelTransferDetails proposalId={proposal.id} />;
|
details = <ProposalCancelTransferDetails proposalId={proposal.id} />;
|
||||||
}
|
}
|
||||||
return null;
|
break;
|
||||||
}
|
}
|
||||||
case 'UpdateMarketState': {
|
case 'UpdateMarketState': {
|
||||||
return <ProposalUpdateMarketState change={terms.change} />;
|
details = <ProposalUpdateMarketState change={terms.change} />;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case 'UpdateReferralProgram': {
|
case 'UpdateReferralProgram': {
|
||||||
return <ProposalReferralProgramDetails change={terms.change} />;
|
details = <ProposalReferralProgramDetails change={terms.change} />;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case 'UpdateVolumeDiscountProgram': {
|
case 'UpdateVolumeDiscountProgram': {
|
||||||
return <ProposalVolumeDiscountProgramDetails change={terms.change} />;
|
details = <ProposalVolumeDiscountProgramDetails change={terms.change} />;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case 'UpdateNetworkParameter': {
|
case 'UpdateNetworkParameter': {
|
||||||
if (
|
if (
|
||||||
@@ -100,18 +111,48 @@ export const ProposalChangeDetails = ({
|
|||||||
terms.change.networkParameter.key ===
|
terms.change.networkParameter.key ===
|
||||||
'rewards.activityStreak.benefitTiers'
|
'rewards.activityStreak.benefitTiers'
|
||||||
) {
|
) {
|
||||||
return <ProposalUpdateBenefitTiers change={terms.change} />;
|
details = <ProposalUpdateBenefitTiers change={terms.change} />;
|
||||||
|
} else {
|
||||||
|
details = (
|
||||||
|
<div className="mb-4">
|
||||||
|
<SubHeading title={t(terms.change.__typename as string)} />
|
||||||
|
<span>
|
||||||
|
<Trans
|
||||||
|
i18nKey="Change <lozenge>{{key}}</lozenge> to <lozenge>{{value}}</lozenge>"
|
||||||
|
values={{
|
||||||
|
key: terms.change.networkParameter.key,
|
||||||
|
value: terms.change.networkParameter.value,
|
||||||
|
}}
|
||||||
|
components={{
|
||||||
|
// @ts-ignore children passed by i18next
|
||||||
|
lozenge: <Lozenge />,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
break;
|
||||||
}
|
}
|
||||||
case 'NewFreeform':
|
case 'NewFreeform':
|
||||||
case 'NewSpotMarket':
|
case 'NewSpotMarket':
|
||||||
case 'UpdateSpotMarket': {
|
case 'UpdateSpotMarket':
|
||||||
return null;
|
|
||||||
}
|
|
||||||
default: {
|
default: {
|
||||||
return null;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (indicator != null && details != null) {
|
||||||
|
details = (
|
||||||
|
<div className="grid grid-cols-[40px_minmax(0,1fr)] grid-rows-1 gap-3 mb-3">
|
||||||
|
<div className="w-10">
|
||||||
|
<Indicator indicator={indicator} />
|
||||||
|
</div>
|
||||||
|
<div>{details}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return details;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,261 @@
|
|||||||
|
import compact from 'lodash/compact';
|
||||||
|
import { ENV } from '../../../../config';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
type Maybe<T> = T | null | undefined;
|
||||||
|
|
||||||
|
type ProposalState =
|
||||||
|
| 'STATE_UNSPECIFIED'
|
||||||
|
| 'STATE_FAILED'
|
||||||
|
| 'STATE_OPEN'
|
||||||
|
| 'STATE_PASSED'
|
||||||
|
| 'STATE_REJECTED'
|
||||||
|
| 'STATE_DECLINED'
|
||||||
|
| 'STATE_ENACTED'
|
||||||
|
| 'STATE_WAITING_FOR_NODE_VOTE';
|
||||||
|
|
||||||
|
type ProposalType =
|
||||||
|
| 'TYPE_UNSPECIFIED'
|
||||||
|
| 'TYPE_ALL'
|
||||||
|
| 'TYPE_NEW_MARKET'
|
||||||
|
| 'TYPE_UPDATE_MARKET'
|
||||||
|
| 'TYPE_NETWORK_PARAMETERS'
|
||||||
|
| 'TYPE_NEW_ASSET'
|
||||||
|
| 'TYPE_NEW_FREE_FORM'
|
||||||
|
| 'TYPE_UPDATE_ASSET'
|
||||||
|
| 'TYPE_NEW_SPOT_MARKET'
|
||||||
|
| 'TYPE_UPDATE_SPOT_MARKET'
|
||||||
|
| 'TYPE_NEW_TRANSFER'
|
||||||
|
| 'TYPE_CANCEL_TRANSFER'
|
||||||
|
| 'TYPE_UPDATE_MARKET_STATE'
|
||||||
|
| 'TYPE_UPDATE_REFERRAL_PROGRAM'
|
||||||
|
| 'TYPE_UPDATE_VOLUME_DISCOUNT_PROGRAM';
|
||||||
|
|
||||||
|
type ProposalNodeType = 'TYPE_SINGLE_OR_UNSPECIFIED' | 'TYPE_BATCH';
|
||||||
|
|
||||||
|
type ProposalData = {
|
||||||
|
id: string;
|
||||||
|
rationale: {
|
||||||
|
description: string;
|
||||||
|
title: string;
|
||||||
|
};
|
||||||
|
state: ProposalState;
|
||||||
|
timestamp: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Terms = {
|
||||||
|
cancelTransfer?: { changes: unknown };
|
||||||
|
enactmentTimestamp: string;
|
||||||
|
newAsset?: { changes: unknown };
|
||||||
|
newFreeform: object;
|
||||||
|
newMarket?: { changes: unknown };
|
||||||
|
newSpotMarket?: { changes: unknown };
|
||||||
|
newTransfer?: { changes: unknown };
|
||||||
|
updateAsset?: { assetId: string; changes: unknown };
|
||||||
|
updateMarket?: { marketId: string; changes: unknown };
|
||||||
|
updateMarketState?: {
|
||||||
|
changes: {
|
||||||
|
marketId: string;
|
||||||
|
price: string;
|
||||||
|
updateType:
|
||||||
|
| 'MARKET_STATE_UPDATE_TYPE_UNSPECIFIED'
|
||||||
|
| 'MARKET_STATE_UPDATE_TYPE_TERMINATE'
|
||||||
|
| 'MARKET_STATE_UPDATE_TYPE_SUSPEND'
|
||||||
|
| 'MARKET_STATE_UPDATE_TYPE_RESUME';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
updateNetworkParameter?: { changes: unknown };
|
||||||
|
updateReferralProgram?: { changes: unknown };
|
||||||
|
updateSpotMarket?: { marketId: string; changes: unknown };
|
||||||
|
updateVolumeDiscountProgram?: { changes: unknown };
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SingleProposalData = ProposalData & {
|
||||||
|
terms: Terms & {
|
||||||
|
closingTimestamp: string;
|
||||||
|
validationTimestamp: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
type BatchProposalData = ProposalData & {
|
||||||
|
batchTerms: {
|
||||||
|
changes: Terms[];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SubProposalData = SingleProposalData & {
|
||||||
|
batchId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ProposalNode = {
|
||||||
|
proposal: ProposalData;
|
||||||
|
proposalType: ProposalNodeType;
|
||||||
|
proposals: SubProposalData[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type SingleProposalNode = ProposalNode & {
|
||||||
|
proposal: SingleProposalData;
|
||||||
|
proposalType: 'TYPE_SINGLE_OR_UNSPECIFIED';
|
||||||
|
proposals: [];
|
||||||
|
};
|
||||||
|
|
||||||
|
type BatchProposalNode = ProposalNode & {
|
||||||
|
proposal: BatchProposalData;
|
||||||
|
proposalType: 'TYPE_BATCH';
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isProposalNode = (node: unknown): node is ProposalNode =>
|
||||||
|
Boolean(
|
||||||
|
typeof node === 'object' &&
|
||||||
|
node &&
|
||||||
|
'proposal' in node &&
|
||||||
|
typeof node.proposal === 'object' &&
|
||||||
|
node?.proposal &&
|
||||||
|
'id' in node.proposal &&
|
||||||
|
node?.proposal?.id
|
||||||
|
);
|
||||||
|
|
||||||
|
export const isSingleProposalNode = (
|
||||||
|
node: Maybe<ProposalNode>
|
||||||
|
): node is SingleProposalNode =>
|
||||||
|
Boolean(
|
||||||
|
node &&
|
||||||
|
node?.proposalType === 'TYPE_SINGLE_OR_UNSPECIFIED' &&
|
||||||
|
node?.proposal
|
||||||
|
);
|
||||||
|
|
||||||
|
export const isBatchProposalNode = (
|
||||||
|
node: Maybe<ProposalNode>
|
||||||
|
): node is BatchProposalNode =>
|
||||||
|
Boolean(
|
||||||
|
node &&
|
||||||
|
node?.proposalType === 'TYPE_BATCH' &&
|
||||||
|
node?.proposal &&
|
||||||
|
'batchTerms' in node.proposal &&
|
||||||
|
node?.proposals?.length > 0
|
||||||
|
);
|
||||||
|
|
||||||
|
// this includes also batch proposals with `updateMarket`s 👍
|
||||||
|
const PROPOSALS_ENDPOINT = `${ENV.rest}governances?proposalState=:proposalState&proposalType=:proposalType`;
|
||||||
|
|
||||||
|
// this can be queried also by sub proposal id as `proposalId` and it will
|
||||||
|
// return full batch proposal data with all of its sub proposals including
|
||||||
|
// the requested one inside `proposals` array.
|
||||||
|
const PROPOSAL_ENDPOINT = `${ENV.rest}governance?proposalId=:proposalId`;
|
||||||
|
|
||||||
|
export const getProposals = async ({
|
||||||
|
proposalState,
|
||||||
|
proposalType,
|
||||||
|
}: {
|
||||||
|
proposalState: ProposalState;
|
||||||
|
proposalType: ProposalType;
|
||||||
|
}) => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(
|
||||||
|
PROPOSALS_ENDPOINT.replace(':proposalState', proposalState).replace(
|
||||||
|
':proposalType',
|
||||||
|
proposalType
|
||||||
|
)
|
||||||
|
);
|
||||||
|
if (response.ok) {
|
||||||
|
const data = await response.json();
|
||||||
|
if (
|
||||||
|
data &&
|
||||||
|
'connection' in data &&
|
||||||
|
data.connection &&
|
||||||
|
'edges' in data.connection &&
|
||||||
|
data.connection.edges?.length > 0
|
||||||
|
) {
|
||||||
|
const nodes = compact(
|
||||||
|
data.connection.edges.map((e: { node?: object }) => e?.node)
|
||||||
|
).filter(isProposalNode);
|
||||||
|
|
||||||
|
return nodes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// NOOP - ignore errors
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getProposal = async ({ proposalId }: { proposalId: string }) => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(
|
||||||
|
PROPOSAL_ENDPOINT.replace(':proposalId', proposalId)
|
||||||
|
);
|
||||||
|
if (response.ok) {
|
||||||
|
const data = await response.json();
|
||||||
|
if (data && 'data' in data && isProposalNode(data.data)) {
|
||||||
|
return data.data as ProposalNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// NOOP - ignore errors
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useFetchProposal = ({ proposalId }: { proposalId?: string }) => {
|
||||||
|
const [data, setData] = useState<ProposalNode | null>(null);
|
||||||
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const cb = async () => {
|
||||||
|
if (!proposalId) return;
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
const data = await getProposal({ proposalId });
|
||||||
|
setLoading(false);
|
||||||
|
if (data) {
|
||||||
|
setData(data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
cb();
|
||||||
|
}, [proposalId]);
|
||||||
|
|
||||||
|
return { data, loading };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useFetchProposals = ({
|
||||||
|
proposalState,
|
||||||
|
proposalType,
|
||||||
|
}: {
|
||||||
|
proposalState: ProposalState;
|
||||||
|
proposalType: ProposalType;
|
||||||
|
}) => {
|
||||||
|
const [data, setData] = useState<ProposalNode[]>([]);
|
||||||
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const cb = async () => {
|
||||||
|
setLoading(true);
|
||||||
|
const data = await getProposals({ proposalState, proposalType });
|
||||||
|
setLoading(false);
|
||||||
|
if (data) {
|
||||||
|
setData(data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
cb();
|
||||||
|
}, [proposalState, proposalType]);
|
||||||
|
|
||||||
|
return { data, loading };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const flatten = (
|
||||||
|
nodes: ProposalNode[]
|
||||||
|
): (SingleProposalData | SubProposalData)[] => {
|
||||||
|
const flattenNodes = [];
|
||||||
|
for (const node of nodes) {
|
||||||
|
if (isSingleProposalNode(node)) {
|
||||||
|
flattenNodes.push(node.proposal);
|
||||||
|
}
|
||||||
|
if (isBatchProposalNode(node)) {
|
||||||
|
for (const sub of node.proposals) {
|
||||||
|
flattenNodes.push(sub);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return flattenNodes;
|
||||||
|
};
|
||||||
@@ -61,7 +61,7 @@ const renderComponent = (proposal: IProposal) => {
|
|||||||
<MemoryRouter>
|
<MemoryRouter>
|
||||||
<MockedProvider>
|
<MockedProvider>
|
||||||
<VegaWalletProvider config={vegaWalletConfig}>
|
<VegaWalletProvider config={vegaWalletConfig}>
|
||||||
<Proposal restData={{}} proposal={proposal} />
|
<Proposal restData={null} proposal={proposal} />
|
||||||
</VegaWalletProvider>
|
</VegaWalletProvider>
|
||||||
</MockedProvider>
|
</MockedProvider>
|
||||||
</MemoryRouter>
|
</MemoryRouter>
|
||||||
|
|||||||
@@ -8,15 +8,17 @@ import { ProposalJson } from '../proposal-json';
|
|||||||
import { UserVote } from '../vote-details';
|
import { UserVote } from '../vote-details';
|
||||||
import Routes from '../../../routes';
|
import Routes from '../../../routes';
|
||||||
import { ProposalState } from '@vegaprotocol/types';
|
import { ProposalState } from '@vegaprotocol/types';
|
||||||
|
import { type ProposalNode } from './proposal-utils';
|
||||||
import { useVoteSubmit } from '@vegaprotocol/proposals';
|
import { useVoteSubmit } from '@vegaprotocol/proposals';
|
||||||
import { useUserVote } from '../vote-details/use-user-vote';
|
import { useUserVote } from '../vote-details/use-user-vote';
|
||||||
import { type Proposal as IProposal, type BatchProposal } from '../../types';
|
import { type Proposal as IProposal, type BatchProposal } from '../../types';
|
||||||
import { ProposalChangeDetails } from './proposal-change-details';
|
import { ProposalChangeDetails } from './proposal-change-details';
|
||||||
|
import { type JsonValue } from 'type-fest';
|
||||||
|
|
||||||
export interface ProposalProps {
|
export interface ProposalProps {
|
||||||
proposal: IProposal | BatchProposal;
|
proposal: IProposal | BatchProposal;
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
restData: any;
|
restData: ProposalNode | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Proposal = ({ proposal, restData }: ProposalProps) => {
|
export const Proposal = ({ proposal, restData }: ProposalProps) => {
|
||||||
@@ -58,7 +60,7 @@ export const Proposal = ({ proposal, restData }: ProposalProps) => {
|
|||||||
<ProposalDescription description={proposal.rationale.description} />
|
<ProposalDescription description={proposal.rationale.description} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mb-4">
|
<div className="mb-4 flex flex-col gap-0">
|
||||||
{proposal.__typename === 'Proposal' ? (
|
{proposal.__typename === 'Proposal' ? (
|
||||||
<ProposalChangeDetails
|
<ProposalChangeDetails
|
||||||
proposal={proposal}
|
proposal={proposal}
|
||||||
@@ -70,6 +72,7 @@ export const Proposal = ({ proposal, restData }: ProposalProps) => {
|
|||||||
if (!p?.terms) return null;
|
if (!p?.terms) return null;
|
||||||
return (
|
return (
|
||||||
<ProposalChangeDetails
|
<ProposalChangeDetails
|
||||||
|
indicator={i + 1}
|
||||||
key={i}
|
key={i}
|
||||||
proposal={proposal}
|
proposal={proposal}
|
||||||
terms={p.terms}
|
terms={p.terms}
|
||||||
@@ -94,7 +97,7 @@ export const Proposal = ({ proposal, restData }: ProposalProps) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<ProposalJson proposal={restData?.data?.proposal} />
|
<ProposalJson proposal={restData?.proposal as unknown as JsonValue} />
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -206,7 +206,7 @@ export const ProposalsList = ({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<section className="-mx-4 p-4 mb-8 bg-vega-dark-100">
|
<section className="-mx-4 p-4 mb-8 bg-vega-cdark-900 rounded-l-sm">
|
||||||
<SubHeading title={t('openProposals')} />
|
<SubHeading title={t('openProposals')} />
|
||||||
|
|
||||||
{sortedProposals.open.length > 0 ||
|
{sortedProposals.open.length > 0 ||
|
||||||
|
|||||||
+163
-120
@@ -15,6 +15,8 @@ import {
|
|||||||
type VoteFieldsFragment,
|
type VoteFieldsFragment,
|
||||||
} from '../../__generated__/Proposals';
|
} from '../../__generated__/Proposals';
|
||||||
import { useBatchVoteInformation } from '../../hooks/use-vote-information';
|
import { useBatchVoteInformation } from '../../hooks/use-vote-information';
|
||||||
|
import { MarketName } from '../proposal/market-name';
|
||||||
|
import { Indicator } from '../proposal/indicator';
|
||||||
|
|
||||||
export const CompactVotes = ({ number }: { number: BigNumber }) => (
|
export const CompactVotes = ({ number }: { number: BigNumber }) => (
|
||||||
<CompactNumber
|
<CompactNumber
|
||||||
@@ -39,8 +41,9 @@ const VoteProgress = ({
|
|||||||
children,
|
children,
|
||||||
}: VoteProgressProps) => {
|
}: VoteProgressProps) => {
|
||||||
const containerClasses = classNames(
|
const containerClasses = classNames(
|
||||||
'relative h-10 rounded-md border border-vega-dark-300 overflow-hidden',
|
'relative h-2 rounded-md overflow-hidden',
|
||||||
colourfulBg ? 'bg-vega-pink' : 'bg-vega-dark-400'
|
// 'border border-vega-dark-300',
|
||||||
|
colourfulBg ? 'bg-vega-red' : 'bg-vega-dark-200'
|
||||||
);
|
);
|
||||||
|
|
||||||
const progressClasses = classNames(
|
const progressClasses = classNames(
|
||||||
@@ -49,17 +52,19 @@ const VoteProgress = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const textClasses = classNames(
|
const textClasses = classNames(
|
||||||
'absolute top-0 left-0 w-full h-full flex items-center justify-start px-3 text-black'
|
'w-full flex items-center justify-start text-white text-sm pb-1'
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={containerClasses}>
|
<div>
|
||||||
<div
|
|
||||||
className={progressClasses}
|
|
||||||
style={{ width: `${percentageFor}%` }}
|
|
||||||
data-testid={testId}
|
|
||||||
/>
|
|
||||||
<div className={textClasses}>{children}</div>
|
<div className={textClasses}>{children}</div>
|
||||||
|
<div className={containerClasses}>
|
||||||
|
<div
|
||||||
|
className={progressClasses}
|
||||||
|
style={{ width: `${percentageFor}%` }}
|
||||||
|
data-testid={testId}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -78,14 +83,22 @@ const Status = ({ reached, threshold, text, testId }: StatusProps) => {
|
|||||||
<div data-testid={testId}>
|
<div data-testid={testId}>
|
||||||
{reached ? (
|
{reached ? (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<VegaIcon name={VegaIconNames.TICK} size={20} />
|
<VegaIcon
|
||||||
|
name={VegaIconNames.TICK}
|
||||||
|
className="text-vega-green"
|
||||||
|
size={20}
|
||||||
|
/>
|
||||||
<span>
|
<span>
|
||||||
{threshold.toString()}% {text} {t('met')}
|
{threshold.toString()}% {text} {t('met')}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<VegaIcon name={VegaIconNames.CROSS} size={20} />
|
<VegaIcon
|
||||||
|
name={VegaIconNames.CROSS}
|
||||||
|
className="text-vega-red"
|
||||||
|
size={20}
|
||||||
|
/>
|
||||||
<span>
|
<span>
|
||||||
{threshold.toString()}% {text} {t('not met')}
|
{threshold.toString()}% {text} {t('not met')}
|
||||||
</span>
|
</span>
|
||||||
@@ -151,7 +164,7 @@ const VoteBreakdownBatch = ({ proposal }: { proposal: BatchProposal }) => {
|
|||||||
<p className="flex gap-2 m-0 items-center">
|
<p className="flex gap-2 m-0 items-center">
|
||||||
<VegaIcon
|
<VegaIcon
|
||||||
name={VegaIconNames.CROSS}
|
name={VegaIconNames.CROSS}
|
||||||
className="text-vega-pink"
|
className="text-vega-red"
|
||||||
size={20}
|
size={20}
|
||||||
/>
|
/>
|
||||||
{t(
|
{t(
|
||||||
@@ -176,6 +189,7 @@ const VoteBreakdownBatch = ({ proposal }: { proposal: BatchProposal }) => {
|
|||||||
if (!p?.terms) return null;
|
if (!p?.terms) return null;
|
||||||
return (
|
return (
|
||||||
<VoteBreakdownBatchSubProposal
|
<VoteBreakdownBatchSubProposal
|
||||||
|
indicator={i + 1}
|
||||||
key={i}
|
key={i}
|
||||||
proposal={proposal}
|
proposal={proposal}
|
||||||
votes={proposal.votes}
|
votes={proposal.votes}
|
||||||
@@ -213,7 +227,7 @@ const VoteBreakdownBatch = ({ proposal }: { proposal: BatchProposal }) => {
|
|||||||
<p className="flex gap-2 m-0 items-center">
|
<p className="flex gap-2 m-0 items-center">
|
||||||
<VegaIcon
|
<VegaIcon
|
||||||
name={VegaIconNames.CROSS}
|
name={VegaIconNames.CROSS}
|
||||||
className="text-vega-pink"
|
className="text-vega-red"
|
||||||
size={20}
|
size={20}
|
||||||
/>
|
/>
|
||||||
{t('Proposal failed: {{count}} of {{total}} proposals passed', {
|
{t('Proposal failed: {{count}} of {{total}} proposals passed', {
|
||||||
@@ -235,6 +249,7 @@ const VoteBreakdownBatch = ({ proposal }: { proposal: BatchProposal }) => {
|
|||||||
if (!p?.terms) return null;
|
if (!p?.terms) return null;
|
||||||
return (
|
return (
|
||||||
<VoteBreakdownBatchSubProposal
|
<VoteBreakdownBatchSubProposal
|
||||||
|
indicator={i + 1}
|
||||||
key={i}
|
key={i}
|
||||||
proposal={proposal}
|
proposal={proposal}
|
||||||
votes={proposal.votes}
|
votes={proposal.votes}
|
||||||
@@ -255,10 +270,12 @@ const VoteBreakdownBatchSubProposal = ({
|
|||||||
proposal,
|
proposal,
|
||||||
votes,
|
votes,
|
||||||
terms,
|
terms,
|
||||||
|
indicator,
|
||||||
}: {
|
}: {
|
||||||
proposal: BatchProposal;
|
proposal: BatchProposal;
|
||||||
votes: VoteFieldsFragment;
|
votes: VoteFieldsFragment;
|
||||||
terms: ProposalTermsFieldsFragment;
|
terms: ProposalTermsFieldsFragment;
|
||||||
|
indicator?: number;
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const voteInfo = useVoteInformation({
|
const voteInfo = useVoteInformation({
|
||||||
@@ -269,14 +286,37 @@ const VoteBreakdownBatchSubProposal = ({
|
|||||||
const isProposalOpen = proposal?.state === ProposalState.STATE_OPEN;
|
const isProposalOpen = proposal?.state === ProposalState.STATE_OPEN;
|
||||||
const isUpdateMarket = terms?.change?.__typename === 'UpdateMarket';
|
const isUpdateMarket = terms?.change?.__typename === 'UpdateMarket';
|
||||||
|
|
||||||
|
let marketId = undefined;
|
||||||
|
if (terms?.change?.__typename === 'UpdateMarket') {
|
||||||
|
marketId = terms.change.marketId;
|
||||||
|
}
|
||||||
|
if (terms?.change?.__typename === 'UpdateMarketState') {
|
||||||
|
marketId = terms.change.market.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
const marketName = marketId ? (
|
||||||
|
<>
|
||||||
|
: <MarketName marketId={marketId} />
|
||||||
|
</>
|
||||||
|
) : null;
|
||||||
|
|
||||||
|
const indicatorElement = indicator && <Indicator indicator={indicator} />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="mb-6">
|
||||||
<h4>{t(terms.change.__typename)}</h4>
|
<div className="flex items-center gap-3 mb-3">
|
||||||
<VoteBreakDownUI
|
{indicatorElement}
|
||||||
voteInfo={voteInfo}
|
<h4>
|
||||||
isProposalOpen={isProposalOpen}
|
{t(terms.change.__typename)} {marketName}
|
||||||
isUpdateMarket={isUpdateMarket}
|
</h4>
|
||||||
/>
|
</div>
|
||||||
|
<div className="rounded-sm bg-vega-dark-100 p-3">
|
||||||
|
<VoteBreakDownUI
|
||||||
|
voteInfo={voteInfo}
|
||||||
|
isProposalOpen={isProposalOpen}
|
||||||
|
isUpdateMarket={isUpdateMarket}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -291,11 +331,13 @@ const VoteBreakdownNormal = ({ proposal }: { proposal: Proposal }) => {
|
|||||||
const isUpdateMarket = proposal?.terms?.change?.__typename === 'UpdateMarket';
|
const isUpdateMarket = proposal?.terms?.change?.__typename === 'UpdateMarket';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<VoteBreakDownUI
|
<div className="mb-6">
|
||||||
voteInfo={voteInfo}
|
<VoteBreakDownUI
|
||||||
isProposalOpen={isProposalOpen}
|
voteInfo={voteInfo}
|
||||||
isUpdateMarket={isUpdateMarket}
|
isProposalOpen={isProposalOpen}
|
||||||
/>
|
isUpdateMarket={isUpdateMarket}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -322,7 +364,6 @@ const VoteBreakDownUI = ({
|
|||||||
noPercentage,
|
noPercentage,
|
||||||
noLPPercentage,
|
noLPPercentage,
|
||||||
yesPercentage,
|
yesPercentage,
|
||||||
yesLPPercentage,
|
|
||||||
yesTokens,
|
yesTokens,
|
||||||
noTokens,
|
noTokens,
|
||||||
totalEquityLikeShareWeight,
|
totalEquityLikeShareWeight,
|
||||||
@@ -335,6 +376,7 @@ const VoteBreakDownUI = ({
|
|||||||
majorityLPMet,
|
majorityLPMet,
|
||||||
willPassByTokenVote,
|
willPassByTokenVote,
|
||||||
willPassByLPVote,
|
willPassByLPVote,
|
||||||
|
lpVoteWeight,
|
||||||
} = voteInfo;
|
} = voteInfo;
|
||||||
|
|
||||||
const participationThresholdProgress = BigNumber.min(
|
const participationThresholdProgress = BigNumber.min(
|
||||||
@@ -359,13 +401,13 @@ const VoteBreakDownUI = ({
|
|||||||
'flex justify-between flex-wrap gap-6'
|
'flex justify-between flex-wrap gap-6'
|
||||||
);
|
);
|
||||||
const sectionClasses = classNames('min-w-[300px] flex-1 flex-grow');
|
const sectionClasses = classNames('min-w-[300px] flex-1 flex-grow');
|
||||||
const headingClasses = classNames('mb-2 text-vega-dark-400');
|
const headingClasses = classNames('mb-2 text-sm text-white font-bold');
|
||||||
const progressDetailsClasses = classNames(
|
const progressDetailsClasses = classNames(
|
||||||
'flex justify-between flex-wrap mt-2 text-sm'
|
'flex justify-between flex-wrap mt-2 text-sm'
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mb-6">
|
<div>
|
||||||
{isProposalOpen && (
|
{isProposalOpen && (
|
||||||
<div
|
<div
|
||||||
data-testid="vote-status"
|
data-testid="vote-status"
|
||||||
@@ -382,7 +424,7 @@ const VoteBreakDownUI = ({
|
|||||||
<VegaIcon
|
<VegaIcon
|
||||||
name={VegaIconNames.CROSS}
|
name={VegaIconNames.CROSS}
|
||||||
size={20}
|
size={20}
|
||||||
className="text-vega-pink"
|
className="text-vega-red"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
@@ -398,103 +440,13 @@ const VoteBreakDownUI = ({
|
|||||||
<p className="m-0">
|
<p className="m-0">
|
||||||
<Trans
|
<Trans
|
||||||
i18nKey={'Currently expected to <0>fail</0>'}
|
i18nKey={'Currently expected to <0>fail</0>'}
|
||||||
components={[<span className="text-vega-pink" />]}
|
components={[<span className="text-vega-red" />]}
|
||||||
/>
|
/>
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isUpdateMarket && (
|
|
||||||
<div className="mb-4">
|
|
||||||
<h3 className={headingClasses}>{t('liquidityProviderVote')}</h3>
|
|
||||||
<div className={sectionWrapperClasses}>
|
|
||||||
<section
|
|
||||||
className={sectionClasses}
|
|
||||||
data-testid="lp-majority-breakdown"
|
|
||||||
>
|
|
||||||
<VoteProgress
|
|
||||||
percentageFor={yesLPPercentage}
|
|
||||||
colourfulBg={true}
|
|
||||||
testId="lp-majority-progress"
|
|
||||||
>
|
|
||||||
<Status
|
|
||||||
reached={majorityLPMet}
|
|
||||||
threshold={requiredMajorityLPPercentage}
|
|
||||||
text={t('majorityThreshold')}
|
|
||||||
testId={
|
|
||||||
majorityLPMet ? 'lp-majority-met' : 'lp-majority-not-met'
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</VoteProgress>
|
|
||||||
|
|
||||||
<div className={progressDetailsClasses}>
|
|
||||||
<div className="flex items-center gap-1">
|
|
||||||
<span>{t('liquidityProviderVotesFor')}:</span>
|
|
||||||
<Tooltip
|
|
||||||
description={
|
|
||||||
<span>{yesLPPercentage.toFixed(defaultDP)}%</span>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<button>{yesLPPercentage.toFixed(1)}%</button>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center gap-1">
|
|
||||||
<span>{t('liquidityProviderVotesAgainst')}:</span>
|
|
||||||
<span>
|
|
||||||
<Tooltip
|
|
||||||
description={
|
|
||||||
<span>{noLPPercentage.toFixed(defaultDP)}%</span>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<button>{noLPPercentage.toFixed(1)}%</button>
|
|
||||||
</Tooltip>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section
|
|
||||||
className={sectionClasses}
|
|
||||||
data-testid="lp-participation-breakdown"
|
|
||||||
>
|
|
||||||
<VoteProgress
|
|
||||||
percentageFor={
|
|
||||||
lpParticipationThresholdProgress || new BigNumber(0)
|
|
||||||
}
|
|
||||||
testId="lp-participation-progress"
|
|
||||||
>
|
|
||||||
<Status
|
|
||||||
reached={participationLPMet}
|
|
||||||
threshold={requiredParticipationLP || new BigNumber(1)}
|
|
||||||
text={t('participationThreshold')}
|
|
||||||
testId={
|
|
||||||
participationLPMet
|
|
||||||
? 'lp-participation-met'
|
|
||||||
: 'lp-participation-not-met'
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</VoteProgress>
|
|
||||||
|
|
||||||
<div className="flex mt-2 text-sm">
|
|
||||||
<div className="flex items-center gap-1">
|
|
||||||
<span>{t('totalLiquidityProviderTokensVoted')}:</span>
|
|
||||||
<Tooltip
|
|
||||||
description={formatNumber(
|
|
||||||
totalEquityLikeShareWeight,
|
|
||||||
defaultDP
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<span>{totalEquityLikeShareWeight.toFixed(1)}%</span>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{isUpdateMarket && <h3 className={headingClasses}>{t('tokenVote')}</h3>}
|
{isUpdateMarket && <h3 className={headingClasses}>{t('tokenVote')}</h3>}
|
||||||
<div className={sectionWrapperClasses}>
|
<div className={sectionWrapperClasses}>
|
||||||
<section
|
<section
|
||||||
@@ -594,6 +546,97 @@ const VoteBreakDownUI = ({
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/** Liquidity provider vote */}
|
||||||
|
{isUpdateMarket && (
|
||||||
|
<div className="mt-3">
|
||||||
|
<h3 className={headingClasses}>{t('liquidityProviderVote')}</h3>
|
||||||
|
<div className={sectionWrapperClasses}>
|
||||||
|
<section
|
||||||
|
className={sectionClasses}
|
||||||
|
data-testid="lp-majority-breakdown"
|
||||||
|
>
|
||||||
|
<VoteProgress
|
||||||
|
percentageFor={lpVoteWeight}
|
||||||
|
colourfulBg={true}
|
||||||
|
testId="lp-majority-progress"
|
||||||
|
>
|
||||||
|
<Status
|
||||||
|
reached={majorityLPMet}
|
||||||
|
threshold={requiredMajorityLPPercentage}
|
||||||
|
text={t('majorityThreshold')}
|
||||||
|
testId={
|
||||||
|
majorityLPMet ? 'lp-majority-met' : 'lp-majority-not-met'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</VoteProgress>
|
||||||
|
|
||||||
|
<div className={progressDetailsClasses}>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<span>{t('liquidityProviderVotesFor')}:</span>
|
||||||
|
<Tooltip
|
||||||
|
description={
|
||||||
|
<span>{lpVoteWeight.toFixed(defaultDP)}%</span>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<button>{lpVoteWeight.toFixed(1)}%</button>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<span>{t('liquidityProviderVotesAgainst')}:</span>
|
||||||
|
<span>
|
||||||
|
<Tooltip
|
||||||
|
description={
|
||||||
|
<span>{noLPPercentage.toFixed(defaultDP)}%</span>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<button>{noLPPercentage.toFixed(1)}%</button>
|
||||||
|
</Tooltip>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section
|
||||||
|
className={sectionClasses}
|
||||||
|
data-testid="lp-participation-breakdown"
|
||||||
|
>
|
||||||
|
<VoteProgress
|
||||||
|
percentageFor={
|
||||||
|
lpParticipationThresholdProgress || new BigNumber(0)
|
||||||
|
}
|
||||||
|
testId="lp-participation-progress"
|
||||||
|
>
|
||||||
|
<Status
|
||||||
|
reached={participationLPMet}
|
||||||
|
threshold={requiredParticipationLP || new BigNumber(1)}
|
||||||
|
text={t('participationThreshold')}
|
||||||
|
testId={
|
||||||
|
participationLPMet
|
||||||
|
? 'lp-participation-met'
|
||||||
|
: 'lp-participation-not-met'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</VoteProgress>
|
||||||
|
|
||||||
|
<div className="flex mt-2 text-sm">
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<span>{t('totalLiquidityProviderTokensVoted')}:</span>
|
||||||
|
<Tooltip
|
||||||
|
description={formatNumber(
|
||||||
|
totalEquityLikeShareWeight,
|
||||||
|
defaultDP
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<span>{totalEquityLikeShareWeight.toFixed(1)}%</span>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -273,4 +273,74 @@ describe('use-vote-information', () => {
|
|||||||
expect(current?.willPassByTokenVote).toEqual(false);
|
expect(current?.willPassByTokenVote).toEqual(false);
|
||||||
expect(current?.willPassByLPVote).toEqual(true);
|
expect(current?.willPassByLPVote).toEqual(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('mainnet recreation: only yes LP votes equal passing', () => {
|
||||||
|
const yesVotes = 0;
|
||||||
|
const noVotes = 70;
|
||||||
|
const yesEquityLikeShareWeight = '0.21';
|
||||||
|
const noEquityLikeShareWeight = '0';
|
||||||
|
const fixedTokenValue = 1000000000000000000;
|
||||||
|
|
||||||
|
const proposal = generateProposal({
|
||||||
|
terms: {
|
||||||
|
change: {
|
||||||
|
__typename: 'UpdateMarket',
|
||||||
|
marketId: '12345',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
votes: {
|
||||||
|
__typename: 'ProposalVotes',
|
||||||
|
yes: generateYesVotes(
|
||||||
|
yesVotes,
|
||||||
|
fixedTokenValue,
|
||||||
|
yesEquityLikeShareWeight
|
||||||
|
),
|
||||||
|
no: generateNoVotes(noVotes, fixedTokenValue, noEquityLikeShareWeight),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
result: { current },
|
||||||
|
} = renderHook(() =>
|
||||||
|
useVoteInformation({ terms: proposal.terms, votes: proposal.votes })
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(current?.willPassByTokenVote).toEqual(false);
|
||||||
|
expect(current?.willPassByLPVote).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('mainnet recreation: mixed yes and no LP votes equal failing', () => {
|
||||||
|
const yesVotes = 0;
|
||||||
|
const noVotes = 70;
|
||||||
|
const yesEquityLikeShareWeight = '0.21';
|
||||||
|
const noEquityLikeShareWeight = '0.22';
|
||||||
|
const fixedTokenValue = 1000000000000000000;
|
||||||
|
|
||||||
|
const proposal = generateProposal({
|
||||||
|
terms: {
|
||||||
|
change: {
|
||||||
|
__typename: 'UpdateMarket',
|
||||||
|
marketId: '12345',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
votes: {
|
||||||
|
__typename: 'ProposalVotes',
|
||||||
|
yes: generateYesVotes(
|
||||||
|
yesVotes,
|
||||||
|
fixedTokenValue,
|
||||||
|
yesEquityLikeShareWeight
|
||||||
|
),
|
||||||
|
no: generateNoVotes(noVotes, fixedTokenValue, noEquityLikeShareWeight),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
result: { current },
|
||||||
|
} = renderHook(() =>
|
||||||
|
useVoteInformation({ terms: proposal.terms, votes: proposal.votes })
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(current?.willPassByTokenVote).toEqual(false);
|
||||||
|
expect(current?.willPassByLPVote).toEqual(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -123,15 +123,19 @@ const getVoteData = (
|
|||||||
totalSupply.multipliedBy(params.requiredParticipation)
|
totalSupply.multipliedBy(params.requiredParticipation)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const lpVoteWeight = yesEquityLikeShareWeight
|
||||||
|
.dividedBy(totalEquityLikeShareWeight)
|
||||||
|
.multipliedBy(100);
|
||||||
|
|
||||||
const participationLPMet = params.requiredParticipationLP
|
const participationLPMet = params.requiredParticipationLP
|
||||||
? totalEquityLikeShareWeight.isGreaterThan(params.requiredParticipationLP)
|
? lpVoteWeight.isGreaterThan(params.requiredParticipationLP)
|
||||||
: false;
|
: false;
|
||||||
|
|
||||||
const majorityMet = yesPercentage.isGreaterThanOrEqualTo(
|
const majorityMet = yesPercentage.isGreaterThanOrEqualTo(
|
||||||
requiredMajorityPercentage
|
requiredMajorityPercentage
|
||||||
);
|
);
|
||||||
|
|
||||||
const majorityLPMet = yesLPPercentage.isGreaterThanOrEqualTo(
|
const majorityLPMet = lpVoteWeight.isGreaterThanOrEqualTo(
|
||||||
requiredMajorityLPPercentage
|
requiredMajorityLPPercentage
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -149,14 +153,12 @@ const getVoteData = (
|
|||||||
|
|
||||||
const willPassByLPVote =
|
const willPassByLPVote =
|
||||||
participationLPMet &&
|
participationLPMet &&
|
||||||
new BigNumber(yesLPPercentage).isGreaterThanOrEqualTo(
|
lpVoteWeight.isGreaterThanOrEqualTo(requiredMajorityLPPercentage);
|
||||||
requiredMajorityLPPercentage
|
|
||||||
);
|
|
||||||
|
|
||||||
let willPass = false;
|
let willPass = false;
|
||||||
|
|
||||||
if (changeType === 'UpdateMarket' || changeType === 'UpdateMarketState') {
|
if (changeType === 'UpdateMarket' || changeType === 'UpdateMarketState') {
|
||||||
willPass = willPassByTokenVote && willPassByLPVote;
|
willPass = willPassByTokenVote || willPassByLPVote;
|
||||||
} else {
|
} else {
|
||||||
willPass = willPassByTokenVote;
|
willPass = willPassByTokenVote;
|
||||||
}
|
}
|
||||||
@@ -182,6 +184,7 @@ const getVoteData = (
|
|||||||
totalLPTokensPercentage,
|
totalLPTokensPercentage,
|
||||||
willPassByTokenVote,
|
willPassByTokenVote,
|
||||||
willPassByLPVote,
|
willPassByLPVote,
|
||||||
|
lpVoteWeight: lpVoteWeight.isNaN() ? new BigNumber(0) : lpVoteWeight,
|
||||||
yesVotes: new BigNumber(votes.yes.totalNumber ?? 0),
|
yesVotes: new BigNumber(votes.yes.totalNumber ?? 0),
|
||||||
noVotes: new BigNumber(votes.no.totalNumber ?? 0),
|
noVotes: new BigNumber(votes.no.totalNumber ?? 0),
|
||||||
totalVotes: new BigNumber(votes.yes.totalNumber ?? 0).plus(
|
totalVotes: new BigNumber(votes.yes.totalNumber ?? 0).plus(
|
||||||
|
|||||||
@@ -1,17 +1,16 @@
|
|||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||||
import { useFetch } from '@vegaprotocol/react-helpers';
|
|
||||||
import { ENV } from '../../../config';
|
|
||||||
import { Proposal } from '../components/proposal';
|
import { Proposal } from '../components/proposal';
|
||||||
import { ProposalNotFound } from '../components/proposal-not-found';
|
import { ProposalNotFound } from '../components/proposal-not-found';
|
||||||
import { useProposalQuery } from '../__generated__/Proposals';
|
import { useProposalQuery } from '../__generated__/Proposals';
|
||||||
|
import { useFetchProposal } from '../components/proposal/proposal-utils';
|
||||||
|
|
||||||
export const ProposalContainer = () => {
|
export const ProposalContainer = () => {
|
||||||
const params = useParams<{ proposalId: string }>();
|
const params = useParams<{ proposalId: string }>();
|
||||||
|
|
||||||
const {
|
const { data: restData, loading: restLoading } = useFetchProposal({
|
||||||
state: { data: restData, loading: restLoading, error: restError },
|
proposalId: params.proposalId,
|
||||||
} = useFetch(`${ENV.rest}governance?proposalId=${params.proposalId}`);
|
});
|
||||||
|
|
||||||
const { data, loading, error } = useProposalQuery({
|
const { data, loading, error } = useProposalQuery({
|
||||||
fetchPolicy: 'network-only',
|
fetchPolicy: 'network-only',
|
||||||
@@ -26,7 +25,7 @@ export const ProposalContainer = () => {
|
|||||||
return (
|
return (
|
||||||
<AsyncRenderer
|
<AsyncRenderer
|
||||||
loading={Boolean(loading || restLoading)}
|
loading={Boolean(loading || restLoading)}
|
||||||
error={error || restError}
|
error={error}
|
||||||
data={{
|
data={{
|
||||||
...data,
|
...data,
|
||||||
...(restData ? { restData } : {}),
|
...(restData ? { restData } : {}),
|
||||||
|
|||||||
+8
-45
@@ -1,10 +1,9 @@
|
|||||||
import { useMemo, useEffect, useState, useCallback, useRef } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { AsyncRenderer, Pagination } from '@vegaprotocol/ui-toolkit';
|
import { AsyncRenderer, Pagination } from '@vegaprotocol/ui-toolkit';
|
||||||
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
||||||
import type { EpochFieldsFragment } from '../home/__generated__/Rewards';
|
import type { EpochFieldsFragment } from '../home/__generated__/Rewards';
|
||||||
import { useRewardsQuery } from '../home/__generated__/Rewards';
|
import { useRewardsQuery } from '../home/__generated__/Rewards';
|
||||||
import { ENV } from '../../../config';
|
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
import { EpochIndividualRewardsTable } from './epoch-individual-rewards-table';
|
import { EpochIndividualRewardsTable } from './epoch-individual-rewards-table';
|
||||||
import { generateEpochIndividualRewardsList } from './generate-epoch-individual-rewards-list';
|
import { generateEpochIndividualRewardsList } from './generate-epoch-individual-rewards-list';
|
||||||
@@ -27,22 +26,16 @@ export const EpochIndividualRewards = ({
|
|||||||
const [page, setPage] = useState(1);
|
const [page, setPage] = useState(1);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { pubKey } = useVegaWallet();
|
const { pubKey } = useVegaWallet();
|
||||||
const { delegationsPagination } = ENV;
|
|
||||||
const { param: marketCreationQuantumMultiple } = useNetworkParam(
|
const { param: marketCreationQuantumMultiple } = useNetworkParam(
|
||||||
'rewards_marketCreationQuantumMultiple'
|
'rewards_marketCreationQuantumMultiple'
|
||||||
);
|
);
|
||||||
|
|
||||||
const { data, loading, error, refetch } = useRewardsQuery({
|
const { data, loading, error } = useRewardsQuery({
|
||||||
notifyOnNetworkStatusChange: true,
|
notifyOnNetworkStatusChange: true,
|
||||||
variables: {
|
variables: {
|
||||||
partyId: pubKey || '',
|
partyId: pubKey || '',
|
||||||
fromEpoch: epochId - EPOCHS_PAGE_SIZE,
|
...calculateEpochOffset({ epochId, page, size: EPOCHS_PAGE_SIZE }),
|
||||||
toEpoch: epochId,
|
delegationsPagination: { first: 50 },
|
||||||
delegationsPagination: delegationsPagination
|
|
||||||
? {
|
|
||||||
first: Number(delegationsPagination),
|
|
||||||
}
|
|
||||||
: undefined,
|
|
||||||
},
|
},
|
||||||
skip: !pubKey,
|
skip: !pubKey,
|
||||||
});
|
});
|
||||||
@@ -70,36 +63,6 @@ export const EpochIndividualRewards = ({
|
|||||||
});
|
});
|
||||||
}, [data?.party, epochId, epochRewardSummaries, page, rewards]);
|
}, [data?.party, epochId, epochRewardSummaries, page, rewards]);
|
||||||
|
|
||||||
const refetchData = useCallback(
|
|
||||||
async (toPage?: number) => {
|
|
||||||
const targetPage = toPage ?? page;
|
|
||||||
await refetch({
|
|
||||||
partyId: pubKey || '',
|
|
||||||
...calculateEpochOffset({ epochId, page, size: EPOCHS_PAGE_SIZE }),
|
|
||||||
delegationsPagination: delegationsPagination
|
|
||||||
? {
|
|
||||||
first: Number(delegationsPagination),
|
|
||||||
}
|
|
||||||
: undefined,
|
|
||||||
});
|
|
||||||
setPage(targetPage);
|
|
||||||
},
|
|
||||||
[epochId, page, refetch, delegationsPagination, pubKey]
|
|
||||||
);
|
|
||||||
|
|
||||||
const prevEpochIdRef = useRef<number | null>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (prevEpochIdRef.current === null) {
|
|
||||||
prevEpochIdRef.current = epochId;
|
|
||||||
} else if (epochId !== prevEpochIdRef.current) {
|
|
||||||
// When the epoch changes, we want to refetch the data to update the current page
|
|
||||||
refetchData();
|
|
||||||
}
|
|
||||||
|
|
||||||
prevEpochIdRef.current = epochId;
|
|
||||||
}, [epochId, refetchData]);
|
|
||||||
|
|
||||||
// Workarounds for the error handling of AsyncRenderer
|
// Workarounds for the error handling of AsyncRenderer
|
||||||
const filteredErrors = filterAcceptableGraphqlErrors(error);
|
const filteredErrors = filterAcceptableGraphqlErrors(error);
|
||||||
const filteredData = data || [];
|
const filteredData = data || [];
|
||||||
@@ -131,10 +94,10 @@ export const EpochIndividualRewards = ({
|
|||||||
isLoading={loading}
|
isLoading={loading}
|
||||||
hasPrevPage={page > 1}
|
hasPrevPage={page > 1}
|
||||||
hasNextPage={page < totalPages}
|
hasNextPage={page < totalPages}
|
||||||
onBack={() => refetchData(page - 1)}
|
onBack={() => setPage((x) => x - 1)}
|
||||||
onNext={() => refetchData(page + 1)}
|
onNext={() => setPage((x) => x + 1)}
|
||||||
onFirst={() => refetchData(1)}
|
onFirst={() => setPage(1)}
|
||||||
onLast={() => refetchData(totalPages)}
|
onLast={() => setPage(totalPages)}
|
||||||
>
|
>
|
||||||
{t('Page')} {page}
|
{t('Page')} {page}
|
||||||
</Pagination>
|
</Pagination>
|
||||||
|
|||||||
+7
-211
@@ -23,36 +23,6 @@ describe('generateEpochIndividualRewardsList', () => {
|
|||||||
epoch: { id: '2' },
|
epoch: { id: '2' },
|
||||||
};
|
};
|
||||||
|
|
||||||
const reward3: RewardFieldsFragment = {
|
|
||||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
|
||||||
amount: '200',
|
|
||||||
percentageOfTotal: '0.2',
|
|
||||||
receivedAt: new Date(),
|
|
||||||
asset: { id: 'gbp', symbol: 'GBP', name: 'GBP', decimals: 7 },
|
|
||||||
party: { id: 'blah' },
|
|
||||||
epoch: { id: '2' },
|
|
||||||
};
|
|
||||||
|
|
||||||
const reward4: RewardFieldsFragment = {
|
|
||||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
|
||||||
amount: '100',
|
|
||||||
percentageOfTotal: '0.1',
|
|
||||||
receivedAt: new Date(),
|
|
||||||
asset: { id: 'usd', symbol: 'USD', name: 'USD', decimals: 6 },
|
|
||||||
party: { id: 'blah' },
|
|
||||||
epoch: { id: '1' },
|
|
||||||
};
|
|
||||||
|
|
||||||
const reward5: RewardFieldsFragment = {
|
|
||||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
|
||||||
amount: '150',
|
|
||||||
percentageOfTotal: '0.15',
|
|
||||||
receivedAt: new Date(),
|
|
||||||
asset: { id: 'usd', symbol: 'USD', name: 'USD', decimals: 6 },
|
|
||||||
party: { id: 'blah' },
|
|
||||||
epoch: { id: '3' },
|
|
||||||
};
|
|
||||||
|
|
||||||
const rewardWrongType: RewardFieldsFragment = {
|
const rewardWrongType: RewardFieldsFragment = {
|
||||||
rewardType: AccountType.ACCOUNT_TYPE_INSURANCE,
|
rewardType: AccountType.ACCOUNT_TYPE_INSURANCE,
|
||||||
amount: '50',
|
amount: '50',
|
||||||
@@ -120,26 +90,10 @@ describe('generateEpochIndividualRewardsList', () => {
|
|||||||
amount: '0',
|
amount: '0',
|
||||||
percentageOfTotal: '0',
|
percentageOfTotal: '0',
|
||||||
},
|
},
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
|
|
||||||
amount: '0',
|
|
||||||
percentageOfTotal: '0',
|
|
||||||
},
|
|
||||||
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
|
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
|
||||||
amount: '100',
|
amount: '100',
|
||||||
percentageOfTotal: '0.1',
|
percentageOfTotal: '0.1',
|
||||||
},
|
},
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES]: {
|
|
||||||
amount: '0',
|
|
||||||
percentageOfTotal: '0',
|
|
||||||
},
|
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES]: {
|
|
||||||
amount: '0',
|
|
||||||
percentageOfTotal: '0',
|
|
||||||
},
|
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS]: {
|
|
||||||
amount: '0',
|
|
||||||
percentageOfTotal: '0',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -147,7 +101,7 @@ describe('generateEpochIndividualRewardsList', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return an array sorted by epoch descending', () => {
|
it('should return an array sorted by epoch descending', () => {
|
||||||
const rewards = [reward1, reward2, reward3, reward4];
|
const rewards = [reward1, reward2];
|
||||||
const result1 = generateEpochIndividualRewardsList({
|
const result1 = generateEpochIndividualRewardsList({
|
||||||
rewards,
|
rewards,
|
||||||
epochId: 2,
|
epochId: 2,
|
||||||
@@ -157,7 +111,7 @@ describe('generateEpochIndividualRewardsList', () => {
|
|||||||
expect(result1[0].epoch).toEqual(2);
|
expect(result1[0].epoch).toEqual(2);
|
||||||
expect(result1[1].epoch).toEqual(1);
|
expect(result1[1].epoch).toEqual(1);
|
||||||
|
|
||||||
const reorderedRewards = [reward4, reward3, reward2, reward1];
|
const reorderedRewards = [reward2, reward1];
|
||||||
const result2 = generateEpochIndividualRewardsList({
|
const result2 = generateEpochIndividualRewardsList({
|
||||||
rewards: reorderedRewards,
|
rewards: reorderedRewards,
|
||||||
epochId: 2,
|
epochId: 2,
|
||||||
@@ -170,7 +124,7 @@ describe('generateEpochIndividualRewardsList', () => {
|
|||||||
|
|
||||||
it('returns data in the expected shape', () => {
|
it('returns data in the expected shape', () => {
|
||||||
// Just sanity checking the whole structure here
|
// Just sanity checking the whole structure here
|
||||||
const rewards = [reward1, reward2, reward3, reward4];
|
const rewards = [reward1, reward2];
|
||||||
const result = generateEpochIndividualRewardsList({
|
const result = generateEpochIndividualRewardsList({
|
||||||
rewards,
|
rewards,
|
||||||
epochId: 2,
|
epochId: 2,
|
||||||
@@ -181,37 +135,6 @@ describe('generateEpochIndividualRewardsList', () => {
|
|||||||
{
|
{
|
||||||
epoch: 2,
|
epoch: 2,
|
||||||
rewards: [
|
rewards: [
|
||||||
{
|
|
||||||
asset: 'GBP',
|
|
||||||
totalAmount: '200',
|
|
||||||
decimals: 7,
|
|
||||||
rewardTypes: {
|
|
||||||
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
|
|
||||||
amount: '0',
|
|
||||||
percentageOfTotal: '0',
|
|
||||||
},
|
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
|
|
||||||
amount: '200',
|
|
||||||
percentageOfTotal: '0.2',
|
|
||||||
},
|
|
||||||
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
|
|
||||||
amount: '0',
|
|
||||||
percentageOfTotal: '0',
|
|
||||||
},
|
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES]: {
|
|
||||||
amount: '0',
|
|
||||||
percentageOfTotal: '0',
|
|
||||||
},
|
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES]: {
|
|
||||||
amount: '0',
|
|
||||||
percentageOfTotal: '0',
|
|
||||||
},
|
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS]: {
|
|
||||||
amount: '0',
|
|
||||||
percentageOfTotal: '0',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
asset: 'EUR',
|
asset: 'EUR',
|
||||||
totalAmount: '50',
|
totalAmount: '50',
|
||||||
@@ -221,26 +144,10 @@ describe('generateEpochIndividualRewardsList', () => {
|
|||||||
amount: '0',
|
amount: '0',
|
||||||
percentageOfTotal: '0',
|
percentageOfTotal: '0',
|
||||||
},
|
},
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
|
|
||||||
amount: '0',
|
|
||||||
percentageOfTotal: '0',
|
|
||||||
},
|
|
||||||
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
|
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
|
||||||
amount: '50',
|
amount: '50',
|
||||||
percentageOfTotal: '0.05',
|
percentageOfTotal: '0.05',
|
||||||
},
|
},
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES]: {
|
|
||||||
amount: '0',
|
|
||||||
percentageOfTotal: '0',
|
|
||||||
},
|
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES]: {
|
|
||||||
amount: '0',
|
|
||||||
percentageOfTotal: '0',
|
|
||||||
},
|
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS]: {
|
|
||||||
amount: '0',
|
|
||||||
percentageOfTotal: '0',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -250,33 +157,17 @@ describe('generateEpochIndividualRewardsList', () => {
|
|||||||
rewards: [
|
rewards: [
|
||||||
{
|
{
|
||||||
asset: 'USD',
|
asset: 'USD',
|
||||||
totalAmount: '200',
|
totalAmount: '100',
|
||||||
decimals: 6,
|
decimals: 6,
|
||||||
rewardTypes: {
|
rewardTypes: {
|
||||||
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
|
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
|
||||||
amount: '0',
|
amount: '0',
|
||||||
percentageOfTotal: '0',
|
percentageOfTotal: '0',
|
||||||
},
|
},
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
|
|
||||||
amount: '100',
|
|
||||||
percentageOfTotal: '0.1',
|
|
||||||
},
|
|
||||||
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
|
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
|
||||||
amount: '100',
|
amount: '100',
|
||||||
percentageOfTotal: '0.1',
|
percentageOfTotal: '0.1',
|
||||||
},
|
},
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES]: {
|
|
||||||
amount: '0',
|
|
||||||
percentageOfTotal: '0',
|
|
||||||
},
|
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES]: {
|
|
||||||
amount: '0',
|
|
||||||
percentageOfTotal: '0',
|
|
||||||
},
|
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS]: {
|
|
||||||
amount: '0',
|
|
||||||
percentageOfTotal: '0',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -285,7 +176,7 @@ describe('generateEpochIndividualRewardsList', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('returns data correctly for the requested epoch range', () => {
|
it('returns data correctly for the requested epoch range', () => {
|
||||||
const rewards = [reward1, reward2, reward3, reward4, reward5];
|
const rewards = [reward1, reward2];
|
||||||
const resultPageOne = generateEpochIndividualRewardsList({
|
const resultPageOne = generateEpochIndividualRewardsList({
|
||||||
rewards,
|
rewards,
|
||||||
epochId: 3,
|
epochId: 3,
|
||||||
@@ -297,74 +188,11 @@ describe('generateEpochIndividualRewardsList', () => {
|
|||||||
expect(resultPageOne).toEqual([
|
expect(resultPageOne).toEqual([
|
||||||
{
|
{
|
||||||
epoch: 3,
|
epoch: 3,
|
||||||
rewards: [
|
rewards: [],
|
||||||
{
|
|
||||||
asset: 'USD',
|
|
||||||
decimals: 6,
|
|
||||||
totalAmount: '150',
|
|
||||||
rewardTypes: {
|
|
||||||
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
|
|
||||||
amount: '0',
|
|
||||||
percentageOfTotal: '0',
|
|
||||||
},
|
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
|
|
||||||
amount: '150',
|
|
||||||
percentageOfTotal: '0.15',
|
|
||||||
},
|
|
||||||
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
|
|
||||||
amount: '0',
|
|
||||||
percentageOfTotal: '0',
|
|
||||||
},
|
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES]: {
|
|
||||||
amount: '0',
|
|
||||||
percentageOfTotal: '0',
|
|
||||||
},
|
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES]: {
|
|
||||||
amount: '0',
|
|
||||||
percentageOfTotal: '0',
|
|
||||||
},
|
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS]: {
|
|
||||||
amount: '0',
|
|
||||||
percentageOfTotal: '0',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
epoch: 2,
|
epoch: 2,
|
||||||
rewards: [
|
rewards: [
|
||||||
{
|
|
||||||
asset: 'GBP',
|
|
||||||
totalAmount: '200',
|
|
||||||
decimals: 7,
|
|
||||||
rewardTypes: {
|
|
||||||
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
|
|
||||||
amount: '0',
|
|
||||||
percentageOfTotal: '0',
|
|
||||||
},
|
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
|
|
||||||
amount: '200',
|
|
||||||
percentageOfTotal: '0.2',
|
|
||||||
},
|
|
||||||
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
|
|
||||||
amount: '0',
|
|
||||||
percentageOfTotal: '0',
|
|
||||||
},
|
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES]: {
|
|
||||||
amount: '0',
|
|
||||||
percentageOfTotal: '0',
|
|
||||||
},
|
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES]: {
|
|
||||||
amount: '0',
|
|
||||||
percentageOfTotal: '0',
|
|
||||||
},
|
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS]: {
|
|
||||||
amount: '0',
|
|
||||||
percentageOfTotal: '0',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
asset: 'EUR',
|
asset: 'EUR',
|
||||||
totalAmount: '50',
|
totalAmount: '50',
|
||||||
@@ -374,26 +202,10 @@ describe('generateEpochIndividualRewardsList', () => {
|
|||||||
amount: '0',
|
amount: '0',
|
||||||
percentageOfTotal: '0',
|
percentageOfTotal: '0',
|
||||||
},
|
},
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
|
|
||||||
amount: '0',
|
|
||||||
percentageOfTotal: '0',
|
|
||||||
},
|
|
||||||
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
|
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
|
||||||
amount: '50',
|
amount: '50',
|
||||||
percentageOfTotal: '0.05',
|
percentageOfTotal: '0.05',
|
||||||
},
|
},
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES]: {
|
|
||||||
amount: '0',
|
|
||||||
percentageOfTotal: '0',
|
|
||||||
},
|
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES]: {
|
|
||||||
amount: '0',
|
|
||||||
percentageOfTotal: '0',
|
|
||||||
},
|
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS]: {
|
|
||||||
amount: '0',
|
|
||||||
percentageOfTotal: '0',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -414,33 +226,17 @@ describe('generateEpochIndividualRewardsList', () => {
|
|||||||
rewards: [
|
rewards: [
|
||||||
{
|
{
|
||||||
asset: 'USD',
|
asset: 'USD',
|
||||||
totalAmount: '200',
|
totalAmount: '100',
|
||||||
decimals: 6,
|
decimals: 6,
|
||||||
rewardTypes: {
|
rewardTypes: {
|
||||||
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
|
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
|
||||||
amount: '0',
|
amount: '0',
|
||||||
percentageOfTotal: '0',
|
percentageOfTotal: '0',
|
||||||
},
|
},
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
|
|
||||||
amount: '100',
|
|
||||||
percentageOfTotal: '0.1',
|
|
||||||
},
|
|
||||||
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
|
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
|
||||||
amount: '100',
|
amount: '100',
|
||||||
percentageOfTotal: '0.1',
|
percentageOfTotal: '0.1',
|
||||||
},
|
},
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES]: {
|
|
||||||
amount: '0',
|
|
||||||
percentageOfTotal: '0',
|
|
||||||
},
|
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES]: {
|
|
||||||
amount: '0',
|
|
||||||
percentageOfTotal: '0',
|
|
||||||
},
|
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS]: {
|
|
||||||
amount: '0',
|
|
||||||
percentageOfTotal: '0',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useState, useCallback, useEffect } from 'react';
|
import { useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { AsyncRenderer, Pagination } from '@vegaprotocol/ui-toolkit';
|
import { AsyncRenderer, Pagination } from '@vegaprotocol/ui-toolkit';
|
||||||
import type { EpochFieldsFragment } from '../home/__generated__/Rewards';
|
import type { EpochFieldsFragment } from '../home/__generated__/Rewards';
|
||||||
@@ -23,37 +23,17 @@ export const EpochTotalRewards = ({ currentEpoch }: EpochTotalRewardsProps) => {
|
|||||||
'rewards_marketCreationQuantumMultiple'
|
'rewards_marketCreationQuantumMultiple'
|
||||||
);
|
);
|
||||||
const [page, setPage] = useState(1);
|
const [page, setPage] = useState(1);
|
||||||
const { data, loading, error, refetch } = useEpochAssetsRewardsQuery({
|
const { data, loading, error } = useEpochAssetsRewardsQuery({
|
||||||
notifyOnNetworkStatusChange: true,
|
notifyOnNetworkStatusChange: true,
|
||||||
variables: {
|
variables: {
|
||||||
epochRewardSummariesFilter: {
|
epochRewardSummariesFilter: calculateEpochOffset({
|
||||||
fromEpoch: epochId - EPOCHS_PAGE_SIZE,
|
epochId,
|
||||||
},
|
page: page,
|
||||||
|
size: EPOCHS_PAGE_SIZE,
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const refetchData = useCallback(
|
|
||||||
async (toPage?: number) => {
|
|
||||||
const targetPage = toPage ?? page;
|
|
||||||
await refetch({
|
|
||||||
epochRewardSummariesFilter: calculateEpochOffset({
|
|
||||||
epochId,
|
|
||||||
page: targetPage,
|
|
||||||
size: EPOCHS_PAGE_SIZE,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
setPage(targetPage);
|
|
||||||
},
|
|
||||||
[epochId, page, refetch]
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// when the epoch changes, we want to refetch the data to update the current page
|
|
||||||
if (data) {
|
|
||||||
refetchData();
|
|
||||||
}
|
|
||||||
}, [epochId, data, refetchData]);
|
|
||||||
|
|
||||||
const epochTotalRewardSummaries =
|
const epochTotalRewardSummaries =
|
||||||
generateEpochTotalRewardsList({
|
generateEpochTotalRewardsList({
|
||||||
data,
|
data,
|
||||||
@@ -85,10 +65,10 @@ export const EpochTotalRewards = ({ currentEpoch }: EpochTotalRewardsProps) => {
|
|||||||
isLoading={loading}
|
isLoading={loading}
|
||||||
hasPrevPage={page > 1}
|
hasPrevPage={page > 1}
|
||||||
hasNextPage={page < totalPages}
|
hasNextPage={page < totalPages}
|
||||||
onBack={() => refetchData(page - 1)}
|
onBack={() => setPage((x) => x - 1)}
|
||||||
onNext={() => refetchData(page + 1)}
|
onNext={() => setPage((x) => x + 1)}
|
||||||
onFirst={() => refetchData(1)}
|
onFirst={() => setPage(1)}
|
||||||
onLast={() => refetchData(totalPages)}
|
onLast={() => setPage(totalPages)}
|
||||||
>
|
>
|
||||||
{t('Page')} {page}
|
{t('Page')} {page}
|
||||||
</Pagination>
|
</Pagination>
|
||||||
|
|||||||
+4
-250
@@ -149,38 +149,6 @@ describe('generateEpochAssetRewardsList', () => {
|
|||||||
amount: '0',
|
amount: '0',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
|
||||||
{
|
|
||||||
rewardType:
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
|
||||||
amount: '0',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
|
||||||
{
|
|
||||||
rewardType:
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
|
||||||
amount: '0',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
|
||||||
{
|
|
||||||
rewardType:
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
|
||||||
amount: '0',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
|
||||||
{
|
|
||||||
rewardType:
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
|
||||||
amount: '0',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
]),
|
]),
|
||||||
totalAmount: '123',
|
totalAmount: '123',
|
||||||
},
|
},
|
||||||
@@ -291,40 +259,8 @@ describe('generateEpochAssetRewardsList', () => {
|
|||||||
amount: '100',
|
amount: '100',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
|
||||||
{
|
|
||||||
rewardType:
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
|
||||||
amount: '123',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
|
||||||
{
|
|
||||||
rewardType:
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
|
||||||
amount: '0',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
|
||||||
{
|
|
||||||
rewardType:
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
|
||||||
amount: '0',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
|
||||||
{
|
|
||||||
rewardType:
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
|
||||||
amount: '0',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
]),
|
]),
|
||||||
totalAmount: '223',
|
totalAmount: '100',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
]),
|
]),
|
||||||
@@ -334,66 +270,7 @@ describe('generateEpochAssetRewardsList', () => {
|
|||||||
'2',
|
'2',
|
||||||
{
|
{
|
||||||
epoch: 2,
|
epoch: 2,
|
||||||
assetRewards: new Map([
|
assetRewards: new Map(),
|
||||||
[
|
|
||||||
'1',
|
|
||||||
{
|
|
||||||
assetId: '1',
|
|
||||||
decimals: 18,
|
|
||||||
name: 'Asset 1',
|
|
||||||
rewards: new Map([
|
|
||||||
[
|
|
||||||
AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
|
||||||
{
|
|
||||||
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
|
||||||
amount: '0',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[
|
|
||||||
AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
|
||||||
{
|
|
||||||
rewardType:
|
|
||||||
AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
|
||||||
amount: '0',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
|
||||||
{
|
|
||||||
rewardType:
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
|
||||||
amount: '0',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
|
||||||
{
|
|
||||||
rewardType:
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
|
||||||
amount: '0',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
|
||||||
{
|
|
||||||
rewardType:
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
|
||||||
amount: '5',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
|
||||||
{
|
|
||||||
rewardType:
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
|
||||||
amount: '0',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
]),
|
|
||||||
totalAmount: '5',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
]),
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
])
|
])
|
||||||
@@ -489,66 +366,7 @@ describe('generateEpochAssetRewardsList', () => {
|
|||||||
'2',
|
'2',
|
||||||
{
|
{
|
||||||
epoch: 2,
|
epoch: 2,
|
||||||
assetRewards: new Map([
|
assetRewards: new Map(),
|
||||||
[
|
|
||||||
'1',
|
|
||||||
{
|
|
||||||
assetId: '1',
|
|
||||||
name: 'Asset 1',
|
|
||||||
decimals: 18,
|
|
||||||
rewards: new Map([
|
|
||||||
[
|
|
||||||
AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
|
||||||
{
|
|
||||||
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
|
||||||
amount: '0',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[
|
|
||||||
AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
|
||||||
{
|
|
||||||
rewardType:
|
|
||||||
AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
|
||||||
amount: '0',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
|
||||||
{
|
|
||||||
rewardType:
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
|
||||||
amount: '0',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
|
||||||
{
|
|
||||||
rewardType:
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
|
||||||
amount: '0',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
|
||||||
{
|
|
||||||
rewardType:
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
|
||||||
amount: '33',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
|
||||||
{
|
|
||||||
rewardType:
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
|
||||||
amount: '0',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
]),
|
|
||||||
totalAmount: '33',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
]),
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@@ -578,38 +396,6 @@ describe('generateEpochAssetRewardsList', () => {
|
|||||||
amount: '15',
|
amount: '15',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
|
||||||
{
|
|
||||||
rewardType:
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
|
||||||
amount: '0',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
|
||||||
{
|
|
||||||
rewardType:
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
|
||||||
amount: '0',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
|
||||||
{
|
|
||||||
rewardType:
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
|
||||||
amount: '0',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
|
||||||
{
|
|
||||||
rewardType:
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
|
||||||
amount: '0',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
]),
|
]),
|
||||||
totalAmount: '15',
|
totalAmount: '15',
|
||||||
},
|
},
|
||||||
@@ -656,40 +442,8 @@ describe('generateEpochAssetRewardsList', () => {
|
|||||||
amount: '100',
|
amount: '100',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
|
||||||
{
|
|
||||||
rewardType:
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
|
||||||
amount: '123',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
|
||||||
{
|
|
||||||
rewardType:
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
|
||||||
amount: '0',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
|
||||||
{
|
|
||||||
rewardType:
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
|
||||||
amount: '0',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
|
||||||
{
|
|
||||||
rewardType:
|
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
|
||||||
amount: '0',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
]),
|
]),
|
||||||
totalAmount: '223',
|
totalAmount: '100',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
]),
|
]),
|
||||||
|
|||||||
+1
-17
@@ -18,22 +18,6 @@ export const RowAccountTypes = {
|
|||||||
columnTitle: 'rewardsColInfraHeader',
|
columnTitle: 'rewardsColInfraHeader',
|
||||||
description: 'rewardsColInfraTooltip',
|
description: 'rewardsColInfraTooltip',
|
||||||
},
|
},
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES]: {
|
|
||||||
columnTitle: 'rewardsColPriceTakingHeader',
|
|
||||||
description: 'rewardsColPriceTakingTooltip',
|
|
||||||
},
|
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES]: {
|
|
||||||
columnTitle: 'rewardsColPriceMakingHeader',
|
|
||||||
description: 'rewardsColPriceMakingTooltip',
|
|
||||||
},
|
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
|
|
||||||
columnTitle: 'rewardsColLiquidityProvisionHeader',
|
|
||||||
description: 'rewardsColLiquidityProvisionTooltip',
|
|
||||||
},
|
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS]: {
|
|
||||||
columnTitle: 'rewardsColMarketCreationHeader',
|
|
||||||
description: 'rewardsColMarketCreationTooltip',
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
interface ColumnHeaderProps {
|
interface ColumnHeaderProps {
|
||||||
@@ -55,7 +39,7 @@ export const rowGridItemStyles = (last = false) =>
|
|||||||
});
|
});
|
||||||
|
|
||||||
const gridStyles = classNames(
|
const gridStyles = classNames(
|
||||||
'grid grid-cols-[repeat(8,minmax(100px,auto))] max-w-full overflow-auto',
|
'grid grid-cols-[repeat(4,minmax(100px,auto))] max-w-full overflow-auto',
|
||||||
`border-t border-vega-dark-200`,
|
`border-t border-vega-dark-200`,
|
||||||
'text-sm'
|
'text-sm'
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,27 +1,31 @@
|
|||||||
|
fragment ValidatorNode on Node {
|
||||||
|
id
|
||||||
|
stakedTotal
|
||||||
|
rewardScore {
|
||||||
|
rawValidatorScore
|
||||||
|
performanceScore
|
||||||
|
multisigScore
|
||||||
|
validatorScore
|
||||||
|
normalisedScore
|
||||||
|
validatorStatus
|
||||||
|
}
|
||||||
|
rankingScore {
|
||||||
|
status
|
||||||
|
previousStatus
|
||||||
|
rankingScore
|
||||||
|
stakeScore
|
||||||
|
performanceScore
|
||||||
|
votingPower
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
query PreviousEpoch($epochId: ID) {
|
query PreviousEpoch($epochId: ID) {
|
||||||
epoch(id: $epochId) {
|
epoch(id: $epochId) {
|
||||||
id
|
id
|
||||||
validatorsConnection {
|
validatorsConnection {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
id
|
...ValidatorNode
|
||||||
stakedTotal
|
|
||||||
rewardScore {
|
|
||||||
rawValidatorScore
|
|
||||||
performanceScore
|
|
||||||
multisigScore
|
|
||||||
validatorScore
|
|
||||||
normalisedScore
|
|
||||||
validatorStatus
|
|
||||||
}
|
|
||||||
rankingScore {
|
|
||||||
status
|
|
||||||
previousStatus
|
|
||||||
rankingScore
|
|
||||||
stakeScore
|
|
||||||
performanceScore
|
|
||||||
votingPower
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+26
-20
@@ -3,6 +3,8 @@ 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 ValidatorNodeFragment = { __typename?: 'Node', id: string, stakedTotal: string, rewardScore?: { __typename?: 'RewardScore', rawValidatorScore: string, performanceScore: string, multisigScore: string, validatorScore: string, normalisedScore: string, validatorStatus: Types.ValidatorStatus } | null, rankingScore: { __typename?: 'RankingScore', status: Types.ValidatorStatus, previousStatus: Types.ValidatorStatus, rankingScore: string, stakeScore: string, performanceScore: string, votingPower: string } };
|
||||||
|
|
||||||
export type PreviousEpochQueryVariables = Types.Exact<{
|
export type PreviousEpochQueryVariables = Types.Exact<{
|
||||||
epochId?: Types.InputMaybe<Types.Scalars['ID']>;
|
epochId?: Types.InputMaybe<Types.Scalars['ID']>;
|
||||||
}>;
|
}>;
|
||||||
@@ -10,7 +12,28 @@ export type PreviousEpochQueryVariables = Types.Exact<{
|
|||||||
|
|
||||||
export type PreviousEpochQuery = { __typename?: 'Query', epoch: { __typename?: 'Epoch', id: string, validatorsConnection?: { __typename?: 'NodesConnection', edges?: Array<{ __typename?: 'NodeEdge', node: { __typename?: 'Node', id: string, stakedTotal: string, rewardScore?: { __typename?: 'RewardScore', rawValidatorScore: string, performanceScore: string, multisigScore: string, validatorScore: string, normalisedScore: string, validatorStatus: Types.ValidatorStatus } | null, rankingScore: { __typename?: 'RankingScore', status: Types.ValidatorStatus, previousStatus: Types.ValidatorStatus, rankingScore: string, stakeScore: string, performanceScore: string, votingPower: string } } } | null> | null } | null } };
|
export type PreviousEpochQuery = { __typename?: 'Query', epoch: { __typename?: 'Epoch', id: string, validatorsConnection?: { __typename?: 'NodesConnection', edges?: Array<{ __typename?: 'NodeEdge', node: { __typename?: 'Node', id: string, stakedTotal: string, rewardScore?: { __typename?: 'RewardScore', rawValidatorScore: string, performanceScore: string, multisigScore: string, validatorScore: string, normalisedScore: string, validatorStatus: Types.ValidatorStatus } | null, rankingScore: { __typename?: 'RankingScore', status: Types.ValidatorStatus, previousStatus: Types.ValidatorStatus, rankingScore: string, stakeScore: string, performanceScore: string, votingPower: string } } } | null> | null } | null } };
|
||||||
|
|
||||||
|
export const ValidatorNodeFragmentDoc = gql`
|
||||||
|
fragment ValidatorNode on Node {
|
||||||
|
id
|
||||||
|
stakedTotal
|
||||||
|
rewardScore {
|
||||||
|
rawValidatorScore
|
||||||
|
performanceScore
|
||||||
|
multisigScore
|
||||||
|
validatorScore
|
||||||
|
normalisedScore
|
||||||
|
validatorStatus
|
||||||
|
}
|
||||||
|
rankingScore {
|
||||||
|
status
|
||||||
|
previousStatus
|
||||||
|
rankingScore
|
||||||
|
stakeScore
|
||||||
|
performanceScore
|
||||||
|
votingPower
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
export const PreviousEpochDocument = gql`
|
export const PreviousEpochDocument = gql`
|
||||||
query PreviousEpoch($epochId: ID) {
|
query PreviousEpoch($epochId: ID) {
|
||||||
epoch(id: $epochId) {
|
epoch(id: $epochId) {
|
||||||
@@ -18,30 +41,13 @@ export const PreviousEpochDocument = gql`
|
|||||||
validatorsConnection {
|
validatorsConnection {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
id
|
...ValidatorNode
|
||||||
stakedTotal
|
|
||||||
rewardScore {
|
|
||||||
rawValidatorScore
|
|
||||||
performanceScore
|
|
||||||
multisigScore
|
|
||||||
validatorScore
|
|
||||||
normalisedScore
|
|
||||||
validatorStatus
|
|
||||||
}
|
|
||||||
rankingScore {
|
|
||||||
status
|
|
||||||
previousStatus
|
|
||||||
rankingScore
|
|
||||||
stakeScore
|
|
||||||
performanceScore
|
|
||||||
votingPower
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
${ValidatorNodeFragmentDoc}`;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* __usePreviousEpochQuery__
|
* __usePreviousEpochQuery__
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import { usePreviousEpochQuery } from '../__generated__/PreviousEpoch';
|
|||||||
import { ValidatorTables } from './validator-tables';
|
import { ValidatorTables } from './validator-tables';
|
||||||
import { useRefreshAfterEpoch } from '../../../hooks/use-refresh-after-epoch';
|
import { useRefreshAfterEpoch } from '../../../hooks/use-refresh-after-epoch';
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
import { ENV } from '../../../config';
|
|
||||||
import { getMultisigStatusInfo } from '../../../lib/get-multisig-status-info';
|
import { getMultisigStatusInfo } from '../../../lib/get-multisig-status-info';
|
||||||
import { MultisigIncorrectNotice } from '../../../components/multisig-incorrect-notice';
|
import { MultisigIncorrectNotice } from '../../../components/multisig-incorrect-notice';
|
||||||
|
|
||||||
@@ -20,7 +19,6 @@ export const EpochData = () => {
|
|||||||
refetch: nodesRefetch,
|
refetch: nodesRefetch,
|
||||||
} = useNodesQuery();
|
} = useNodesQuery();
|
||||||
|
|
||||||
const { delegationsPagination } = ENV;
|
|
||||||
const {
|
const {
|
||||||
data: userStakingData,
|
data: userStakingData,
|
||||||
error: userStakingError,
|
error: userStakingError,
|
||||||
@@ -29,11 +27,9 @@ export const EpochData = () => {
|
|||||||
} = useStakingQuery({
|
} = useStakingQuery({
|
||||||
variables: {
|
variables: {
|
||||||
partyId: pubKey || '',
|
partyId: pubKey || '',
|
||||||
delegationsPagination: delegationsPagination
|
delegationsPagination: {
|
||||||
? {
|
first: 50,
|
||||||
first: Number(delegationsPagination),
|
},
|
||||||
}
|
|
||||||
: undefined,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const { data: previousEpochData } = usePreviousEpochQuery({
|
const { data: previousEpochData } = usePreviousEpochQuery({
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { ENV } from '../../../config';
|
|
||||||
import { Callout, Intent, Splash } from '@vegaprotocol/ui-toolkit';
|
import { Callout, Intent, Splash } from '@vegaprotocol/ui-toolkit';
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@@ -28,16 +27,13 @@ export const NodeContainer = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { pubKey } = useVegaWallet();
|
const { pubKey } = useVegaWallet();
|
||||||
const { delegationsPagination } = ENV;
|
|
||||||
|
|
||||||
const { data, loading, error, refetch } = useStakingQuery({
|
const { data, loading, error, refetch } = useStakingQuery({
|
||||||
variables: {
|
variables: {
|
||||||
partyId: pubKey || '',
|
partyId: pubKey || '',
|
||||||
delegationsPagination: delegationsPagination
|
delegationsPagination: {
|
||||||
? {
|
first: 50,
|
||||||
first: Number(delegationsPagination),
|
},
|
||||||
}
|
|
||||||
: undefined,
|
|
||||||
},
|
},
|
||||||
errorPolicy: 'all',
|
errorPolicy: 'all',
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import * as Sentry from '@sentry/react';
|
|||||||
import React, { useCallback, useState } from 'react';
|
import React, { useCallback, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { ENV } from '../../../config';
|
|
||||||
import { usePartyDelegationsLazyQuery } from './__generated__/PartyDelegations';
|
import { usePartyDelegationsLazyQuery } from './__generated__/PartyDelegations';
|
||||||
import { TokenInput } from '../../../components/token-input';
|
import { TokenInput } from '../../../components/token-input';
|
||||||
import { useAppState } from '../../../contexts/app-state/app-state-context';
|
import { useAppState } from '../../../contexts/app-state/app-state-context';
|
||||||
@@ -79,7 +78,6 @@ export const StakingForm = ({
|
|||||||
const [error, setError] = useState<Error | null>(null);
|
const [error, setError] = useState<Error | null>(null);
|
||||||
const [isDialogVisible, setIsDialogVisible] = useState(false);
|
const [isDialogVisible, setIsDialogVisible] = useState(false);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { delegationsPagination } = ENV;
|
|
||||||
const [action, setAction] = React.useState<StakeAction>(
|
const [action, setAction] = React.useState<StakeAction>(
|
||||||
params.action as StakeAction
|
params.action as StakeAction
|
||||||
);
|
);
|
||||||
@@ -154,11 +152,9 @@ export const StakingForm = ({
|
|||||||
const [delegationSearch, { data }] = usePartyDelegationsLazyQuery({
|
const [delegationSearch, { data }] = usePartyDelegationsLazyQuery({
|
||||||
variables: {
|
variables: {
|
||||||
partyId: pubKey,
|
partyId: pubKey,
|
||||||
delegationsPagination: delegationsPagination
|
delegationsPagination: {
|
||||||
? {
|
first: 50,
|
||||||
first: Number(delegationsPagination),
|
},
|
||||||
}
|
|
||||||
: undefined,
|
|
||||||
},
|
},
|
||||||
fetchPolicy: 'network-only',
|
fetchPolicy: 'network-only',
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import {
|
|||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
import type { StakingNodeFieldsFragment } from '../__generated__/Staking';
|
import type { StakingNodeFieldsFragment } from '../__generated__/Staking';
|
||||||
import type { PreviousEpochQuery } from '../__generated__/PreviousEpoch';
|
import type { PreviousEpochQuery } from '../__generated__/PreviousEpoch';
|
||||||
|
import { getMultisigStatusInfo } from '../../../lib/get-multisig-status-info';
|
||||||
|
|
||||||
const statuses = {
|
const statuses = {
|
||||||
[Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_ERSATZ]: 'status-ersatz',
|
[Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_ERSATZ]: 'status-ersatz',
|
||||||
@@ -104,6 +105,10 @@ export const ValidatorTable = ({
|
|||||||
};
|
};
|
||||||
}, [node, previousEpochData?.epoch.validatorsConnection?.edges]);
|
}, [node, previousEpochData?.epoch.validatorsConnection?.edges]);
|
||||||
|
|
||||||
|
const multisigStatus = previousEpochData
|
||||||
|
? getMultisigStatusInfo(previousEpochData)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<p className="mb-12">
|
<p className="mb-12">
|
||||||
@@ -281,6 +286,34 @@ export const ValidatorTable = ({
|
|||||||
</span>
|
</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</KeyValueTableRow>
|
</KeyValueTableRow>
|
||||||
|
<KeyValueTableRow>
|
||||||
|
<span className="uppercase">{t('multisigPenalty')}</span>
|
||||||
|
|
||||||
|
<span
|
||||||
|
data-testid="multisig-penalty"
|
||||||
|
className="flex gap-2 items-baseline"
|
||||||
|
>
|
||||||
|
{multisigStatus?.zeroScoreNodes.find(
|
||||||
|
(n) => n.id === node.id
|
||||||
|
) ? (
|
||||||
|
<Tooltip
|
||||||
|
description={t('multisigPenaltyThisNodeIndicator')}
|
||||||
|
>
|
||||||
|
<span className="inline-block w-2 h-2 rounded-full bg-vega-red-500"></span>
|
||||||
|
</Tooltip>
|
||||||
|
) : null}
|
||||||
|
<Tooltip description={t('multisigPenaltyDescription')}>
|
||||||
|
<span>
|
||||||
|
{formatNumberPercentage(
|
||||||
|
BigNumber(
|
||||||
|
multisigStatus?.showMultisigStatusError ? 100 : 0
|
||||||
|
),
|
||||||
|
2
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</Tooltip>
|
||||||
|
</span>
|
||||||
|
</KeyValueTableRow>
|
||||||
<KeyValueTableRow noBorder={true}>
|
<KeyValueTableRow noBorder={true}>
|
||||||
<span>
|
<span>
|
||||||
<strong>{t('TOTAL PENALTIES')}</strong>
|
<strong>{t('TOTAL PENALTIES')}</strong>
|
||||||
|
|||||||
+1
-1
@@ -14,7 +14,7 @@ NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
|
|||||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||||
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
|
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
|
||||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
|
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
|
||||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||||
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
|||||||
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
|
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
|
||||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json
|
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json
|
||||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
|
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
|
||||||
|
|
||||||
NX_ETH_LOCAL_PROVIDER_URL=http://localhost:8545/
|
NX_ETH_LOCAL_PROVIDER_URL=http://localhost:8545/
|
||||||
NX_ETH_WALLET_MNEMONIC="ozone access unlock valid olympic save include omit supply green clown session"
|
NX_ETH_WALLET_MNEMONIC="ozone access unlock valid olympic save include omit supply green clown session"
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega-dev-releases/releases
|
|||||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||||
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
|
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
|
||||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
|
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
|
||||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||||
|
|
||||||
# Cosmic elevator flags
|
# Cosmic elevator flags
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
|
|||||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||||
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
|
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
|
||||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
|
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
|
||||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||||
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/annou
|
|||||||
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
|
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
|
||||||
NX_VEGA_CONSOLE_URL=https://console.fairground.wtf
|
NX_VEGA_CONSOLE_URL=https://console.fairground.wtf
|
||||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
|
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
|
||||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||||
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
|
|||||||
NX_VEGA_CONSOLE_URL=https://trading.validators-testnet.vega.rocks
|
NX_VEGA_CONSOLE_URL=https://trading.validators-testnet.vega.rocks
|
||||||
|
|
||||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
|
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
|
||||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||||
|
|
||||||
# Cosmic elevator flags
|
# Cosmic elevator flags
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
import { useT } from '../../lib/use-t';
|
import { useT } from '../../lib/use-t';
|
||||||
import { ErrorBoundary } from '@sentry/react';
|
import { ErrorBoundary } from '@sentry/react';
|
||||||
import { CompetitionsHeader } from '../../components/competitions/competitions-header';
|
import { CompetitionsHeader } from '../../components/competitions/competitions-header';
|
||||||
import { Intent, Loader, TradingButton } from '@vegaprotocol/ui-toolkit';
|
import {
|
||||||
|
ExternalLink,
|
||||||
import { useGameCards } from '../../lib/hooks/use-game-cards';
|
Intent,
|
||||||
|
Loader,
|
||||||
|
TradingButton,
|
||||||
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
import { useEpochInfoQuery } 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';
|
||||||
@@ -18,6 +21,9 @@ import take from 'lodash/take';
|
|||||||
import { usePageTitle } from '../../lib/hooks/use-page-title';
|
import { usePageTitle } from '../../lib/hooks/use-page-title';
|
||||||
import { TeamCard } from '../../components/competitions/team-card';
|
import { TeamCard } from '../../components/competitions/team-card';
|
||||||
import { useMyTeam } from '../../lib/hooks/use-my-team';
|
import { useMyTeam } from '../../lib/hooks/use-my-team';
|
||||||
|
import { useRewards } from '../../lib/hooks/use-rewards';
|
||||||
|
import { Trans } from 'react-i18next';
|
||||||
|
import { DocsLinks } from '@vegaprotocol/environment';
|
||||||
|
|
||||||
export const CompetitionsHome = () => {
|
export const CompetitionsHome = () => {
|
||||||
const t = useT();
|
const t = useT();
|
||||||
@@ -28,9 +34,9 @@ export const CompetitionsHome = () => {
|
|||||||
const { data: epochData } = useEpochInfoQuery();
|
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 } = useRewards({
|
||||||
onlyActive: true,
|
onlyActive: true,
|
||||||
currentEpoch,
|
scopeToTeams: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data: teamsData, loading: teamsLoading } = useTeams();
|
const { data: teamsData, loading: teamsLoading } = useTeams();
|
||||||
@@ -45,10 +51,34 @@ export const CompetitionsHome = () => {
|
|||||||
return (
|
return (
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<CompetitionsHeader title={t('Competitions')}>
|
<CompetitionsHeader title={t('Competitions')}>
|
||||||
|
<p className="text-lg mb-3">
|
||||||
|
<Trans
|
||||||
|
i18nKey={
|
||||||
|
'Check the cards below to see what community-created, on-chain games are active and how to compete. Joining a team also lets you take part in the on-chain <0>referral program</0>.'
|
||||||
|
}
|
||||||
|
components={[
|
||||||
|
<Link className="underline" key="ref-prog" to={Links.REFERRALS()}>
|
||||||
|
referral program
|
||||||
|
</Link>,
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
<p className="text-lg mb-1">
|
<p className="text-lg mb-1">
|
||||||
{t(
|
<Trans
|
||||||
'Be a team player! Participate in games and work together to rake in as much profit to win.'
|
i18nKey={
|
||||||
)}
|
'Got an idea for a competition? Anyone can define and fund one -- <0>propose an on-chain game</0> yourself.'
|
||||||
|
}
|
||||||
|
components={[
|
||||||
|
<ExternalLink
|
||||||
|
className="underline"
|
||||||
|
key="propose"
|
||||||
|
href={DocsLinks?.ASSET_TRANSFER_PROPOSAL}
|
||||||
|
>
|
||||||
|
propose an on-chain game
|
||||||
|
</ExternalLink>,
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
{/** Docs: https://docs.vega.xyz/mainnet/tutorials/proposals/asset-transfer-proposal */}
|
||||||
</p>
|
</p>
|
||||||
</CompetitionsHeader>
|
</CompetitionsHeader>
|
||||||
|
|
||||||
@@ -75,7 +105,7 @@ export const CompetitionsHome = () => {
|
|||||||
variant="A"
|
variant="A"
|
||||||
title={t('Create a team')}
|
title={t('Create a team')}
|
||||||
description={t(
|
description={t(
|
||||||
'Lorem ipsum dolor sit amet, consectetur adipisicing elit placeat ipsum minus nemo error dicta.'
|
'Create a new team, share your code with potential members, or set a whitelist for an exclusive group.'
|
||||||
)}
|
)}
|
||||||
actionElement={
|
actionElement={
|
||||||
<TradingButton
|
<TradingButton
|
||||||
@@ -94,7 +124,7 @@ export const CompetitionsHome = () => {
|
|||||||
variant="B"
|
variant="B"
|
||||||
title={t('Solo team / lone wolf')}
|
title={t('Solo team / lone wolf')}
|
||||||
description={t(
|
description={t(
|
||||||
'Lorem ipsum dolor sit amet, consectetur adipisicing elit placeat ipsum minus nemo error dicta.'
|
'Want to compete but think the best team size is one? This is the option for you.'
|
||||||
)}
|
)}
|
||||||
actionElement={
|
actionElement={
|
||||||
<TradingButton
|
<TradingButton
|
||||||
@@ -112,7 +142,7 @@ export const CompetitionsHome = () => {
|
|||||||
variant="C"
|
variant="C"
|
||||||
title={t('Join a team')}
|
title={t('Join a team')}
|
||||||
description={t(
|
description={t(
|
||||||
'Lorem ipsum dolor sit amet, consectetur adipisicing elit placeat ipsum minus nemo error dicta.'
|
'Browse existing public teams to find your perfect match.'
|
||||||
)}
|
)}
|
||||||
actionElement={
|
actionElement={
|
||||||
<TradingButton
|
<TradingButton
|
||||||
@@ -131,27 +161,57 @@ export const CompetitionsHome = () => {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/** List of available games */}
|
{/** List of available games */}
|
||||||
<h2 className="text-2xl mb-6">{t('Games')}</h2>
|
<h2 className="text-2xl mb-1">{t('Games')}</h2>
|
||||||
|
<p className="mb-6 text-sm">
|
||||||
|
<Trans
|
||||||
|
i18nKey={
|
||||||
|
'See all the live games on the cards below. Every on-chain game is community funded and designed. <0>Find out how to create one</0>.'
|
||||||
|
}
|
||||||
|
components={[
|
||||||
|
<ExternalLink
|
||||||
|
className="underline"
|
||||||
|
key="find-out"
|
||||||
|
href={DocsLinks?.ASSET_TRANSFER_PROPOSAL}
|
||||||
|
>
|
||||||
|
Find out how to create one
|
||||||
|
</ExternalLink>,
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
{/** Docs: https://docs.vega.xyz/mainnet/tutorials/proposals/asset-transfer-proposal */}
|
||||||
|
</p>
|
||||||
|
|
||||||
{gamesLoading ? (
|
<div className="mb-12 flex">
|
||||||
<Loader size="small" />
|
{gamesLoading ? (
|
||||||
) : (
|
<Loader size="small" />
|
||||||
<GamesContainer data={gamesData} currentEpoch={currentEpoch} />
|
) : (
|
||||||
)}
|
<GamesContainer data={gamesData} currentEpoch={currentEpoch} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
{/** The teams ranking */}
|
{/** The teams ranking */}
|
||||||
<div className="mb-6 flex flex-row items-baseline justify-between">
|
<div className="mb-1 flex flex-row items-baseline gap-3 justify-between">
|
||||||
<h2 className="text-2xl">{t('Leaderboard')}</h2>
|
<h2 className="text-2xl">
|
||||||
|
<Link to={Links.COMPETITIONS_TEAMS()} className=" underline">
|
||||||
|
{t('Leaderboard')}
|
||||||
|
</Link>
|
||||||
|
</h2>
|
||||||
<Link to={Links.COMPETITIONS_TEAMS()} className="text-sm underline">
|
<Link to={Links.COMPETITIONS_TEAMS()} className="text-sm underline">
|
||||||
{t('View all teams')}
|
{t('View all teams')}
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
<p className="mb-6 text-sm">
|
||||||
|
{t(
|
||||||
|
'Teams can earn rewards if they meet the goals set in the on-chain trading competitions. Track your earned rewards here, and see which teams are top of the leaderboard this month.'
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
|
||||||
{teamsLoading ? (
|
<div className="flex">
|
||||||
<Loader size="small" />
|
{teamsLoading ? (
|
||||||
) : (
|
<Loader size="small" />
|
||||||
<CompetitionsLeaderboard data={take(teamsData, 10)} />
|
) : (
|
||||||
)}
|
<CompetitionsLeaderboard data={take(teamsData, 10)} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,11 +10,7 @@ import {
|
|||||||
VegaIcon,
|
VegaIcon,
|
||||||
VegaIconNames,
|
VegaIconNames,
|
||||||
} from '@vegaprotocol/ui-toolkit';
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
import {
|
import { TransferStatus, type Asset } from '@vegaprotocol/types';
|
||||||
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';
|
||||||
@@ -44,11 +40,6 @@ import {
|
|||||||
areTeamGames,
|
areTeamGames,
|
||||||
} from '../../lib/hooks/use-games';
|
} from '../../lib/hooks/use-games';
|
||||||
import { useEpochInfoQuery } from '../../lib/hooks/__generated__/Epoch';
|
import { useEpochInfoQuery } from '../../lib/hooks/__generated__/Epoch';
|
||||||
import {
|
|
||||||
type EnrichedTransfer,
|
|
||||||
isScopedToTeams,
|
|
||||||
useGameCards,
|
|
||||||
} from '../../lib/hooks/use-game-cards';
|
|
||||||
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
|
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
|
||||||
import {
|
import {
|
||||||
ActiveRewardCard,
|
ActiveRewardCard,
|
||||||
@@ -56,6 +47,11 @@ import {
|
|||||||
} from '../../components/rewards-container/active-rewards';
|
} from '../../components/rewards-container/active-rewards';
|
||||||
import { type MarketMap, useMarketsMapProvider } from '@vegaprotocol/markets';
|
import { type MarketMap, useMarketsMapProvider } from '@vegaprotocol/markets';
|
||||||
import format from 'date-fns/format';
|
import format from 'date-fns/format';
|
||||||
|
import {
|
||||||
|
type EnrichedRewardTransfer,
|
||||||
|
isScopedToTeams,
|
||||||
|
useRewards,
|
||||||
|
} from '../../lib/hooks/use-rewards';
|
||||||
|
|
||||||
export const CompetitionsTeam = () => {
|
export const CompetitionsTeam = () => {
|
||||||
const t = useT();
|
const t = useT();
|
||||||
@@ -78,10 +74,9 @@ 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 } = useRewards({
|
||||||
const { data: transfersData, loading: transfersLoading } = useGameCards({
|
|
||||||
currentEpoch: Number(epochData?.epoch.id),
|
|
||||||
onlyActive: false,
|
onlyActive: false,
|
||||||
|
scopeToTeams: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data: markets } = useMarketsMapProvider();
|
const { data: markets } = useMarketsMapProvider();
|
||||||
@@ -112,7 +107,7 @@ const TeamPageContainer = ({ teamId }: { teamId: string | undefined }) => {
|
|||||||
games={areTeamGames(games) ? games : undefined}
|
games={areTeamGames(games) ? games : undefined}
|
||||||
gamesLoading={gamesLoading}
|
gamesLoading={gamesLoading}
|
||||||
transfers={transfersData}
|
transfers={transfersData}
|
||||||
transfersLoading={epochLoading || transfersLoading}
|
transfersLoading={transfersLoading}
|
||||||
allMarkets={markets || undefined}
|
allMarkets={markets || undefined}
|
||||||
refetch={refetch}
|
refetch={refetch}
|
||||||
/>
|
/>
|
||||||
@@ -137,7 +132,7 @@ const TeamPage = ({
|
|||||||
members?: Member[];
|
members?: Member[];
|
||||||
games?: TeamGame[];
|
games?: TeamGame[];
|
||||||
gamesLoading?: boolean;
|
gamesLoading?: boolean;
|
||||||
transfers?: EnrichedTransfer[];
|
transfers?: EnrichedRewardTransfer[];
|
||||||
transfersLoading?: boolean;
|
transfersLoading?: boolean;
|
||||||
allMarkets?: MarketMap;
|
allMarkets?: MarketMap;
|
||||||
refetch: () => void;
|
refetch: () => void;
|
||||||
@@ -211,7 +206,7 @@ const Games = ({
|
|||||||
}: {
|
}: {
|
||||||
games?: TeamGame[];
|
games?: TeamGame[];
|
||||||
gamesLoading?: boolean;
|
gamesLoading?: boolean;
|
||||||
transfers?: EnrichedTransfer[];
|
transfers?: EnrichedRewardTransfer[];
|
||||||
transfersLoading?: boolean;
|
transfersLoading?: boolean;
|
||||||
allMarkets?: MarketMap;
|
allMarkets?: MarketMap;
|
||||||
}) => {
|
}) => {
|
||||||
@@ -451,7 +446,7 @@ const GameTypeCell = ({
|
|||||||
transfer,
|
transfer,
|
||||||
allMarkets,
|
allMarkets,
|
||||||
}: {
|
}: {
|
||||||
transfer?: EnrichedTransfer;
|
transfer?: EnrichedRewardTransfer;
|
||||||
allMarkets?: MarketMap;
|
allMarkets?: MarketMap;
|
||||||
}) => {
|
}) => {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
@@ -474,7 +469,7 @@ const GameTypeCell = ({
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className="border-b border-dashed border-vega-clight-200 dark:border-vega-cdark-200 text-left md:truncate md:max-w-[25vw]"
|
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} />
|
<DispatchMetricInfo reward={transfer} />
|
||||||
</button>
|
</button>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@@ -490,7 +485,7 @@ const ActiveRewardCardDialog = ({
|
|||||||
open: boolean;
|
open: boolean;
|
||||||
onChange: (isOpen: boolean) => void;
|
onChange: (isOpen: boolean) => void;
|
||||||
trigger?: HTMLElement | null;
|
trigger?: HTMLElement | null;
|
||||||
transfer: EnrichedTransfer;
|
transfer: EnrichedRewardTransfer;
|
||||||
allMarkets?: MarketMap;
|
allMarkets?: MarketMap;
|
||||||
}) => {
|
}) => {
|
||||||
const t = useT();
|
const t = useT();
|
||||||
@@ -516,8 +511,6 @@ const ActiveRewardCardDialog = ({
|
|||||||
<ActiveRewardCard
|
<ActiveRewardCard
|
||||||
transferNode={transfer}
|
transferNode={transfer}
|
||||||
currentEpoch={Number(data?.epoch.id)}
|
currentEpoch={Number(data?.epoch.id)}
|
||||||
kind={transfer.transfer.kind as RecurringTransfer}
|
|
||||||
allMarkets={allMarkets}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-1/4">
|
<div className="w-1/4">
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ export const TradePanels = ({ market, pinnedAsset }: TradePanelsProps) => {
|
|||||||
{[
|
{[
|
||||||
'chart',
|
'chart',
|
||||||
'orderbook',
|
'orderbook',
|
||||||
|
'depth',
|
||||||
'trades',
|
'trades',
|
||||||
'liquidity',
|
'liquidity',
|
||||||
'fundingPayments',
|
'fundingPayments',
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { Splash } from '@vegaprotocol/ui-toolkit';
|
|
||||||
import { formatNumber } from '@vegaprotocol/utils';
|
import { formatNumber } from '@vegaprotocol/utils';
|
||||||
import { type useTeams } from '../../lib/hooks/use-teams';
|
import { type useTeams } from '../../lib/hooks/use-teams';
|
||||||
import { useT } from '../../lib/use-t';
|
import { useT } from '../../lib/use-t';
|
||||||
@@ -18,7 +17,11 @@ export const CompetitionsLeaderboard = ({
|
|||||||
const num = (n?: number | string) => (!n ? '-' : formatNumber(n, 0));
|
const num = (n?: number | string) => (!n ? '-' : formatNumber(n, 0));
|
||||||
|
|
||||||
if (!data || data.length === 0) {
|
if (!data || data.length === 0) {
|
||||||
return <Splash>{t('Could not find any teams')}</Splash>;
|
return (
|
||||||
|
<p className="text-sm">
|
||||||
|
{t('Currently no active teams on the network.')}
|
||||||
|
</p>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,28 +1,26 @@
|
|||||||
import { ActiveRewardCard } from '../rewards-container/active-rewards';
|
import { ActiveRewardCard } from '../rewards-container/active-rewards';
|
||||||
import { useT } from '../../lib/use-t';
|
import { useT } from '../../lib/use-t';
|
||||||
import { type EnrichedTransfer } from '../../lib/hooks/use-game-cards';
|
import { type EnrichedRewardTransfer } from '../../lib/hooks/use-rewards';
|
||||||
import { useMarketsMapProvider } from '@vegaprotocol/markets';
|
|
||||||
|
|
||||||
export const GamesContainer = ({
|
export const GamesContainer = ({
|
||||||
data,
|
data,
|
||||||
currentEpoch,
|
currentEpoch,
|
||||||
}: {
|
}: {
|
||||||
data: EnrichedTransfer[];
|
data: EnrichedRewardTransfer[];
|
||||||
currentEpoch: number;
|
currentEpoch: number;
|
||||||
}) => {
|
}) => {
|
||||||
const t = useT();
|
const t = useT();
|
||||||
const { data: markets } = useMarketsMapProvider();
|
|
||||||
|
|
||||||
if (!data || data.length === 0) {
|
if (!data || data.length === 0) {
|
||||||
return (
|
return (
|
||||||
<p className="mb-6 text-muted">
|
<p className="text-sm">
|
||||||
{t('There are currently no games available.')}
|
{t('Currently no active games on the network.')}
|
||||||
</p>
|
</p>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mb-12 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
{data.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;
|
||||||
@@ -37,8 +35,6 @@ export const GamesContainer = ({
|
|||||||
key={i}
|
key={i}
|
||||||
transferNode={game}
|
transferNode={game}
|
||||||
currentEpoch={currentEpoch}
|
currentEpoch={currentEpoch}
|
||||||
kind={transfer.kind}
|
|
||||||
allMarkets={markets || undefined}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -21,18 +21,13 @@ const useFeesTableColumnDefs = (): ColDef[] => {
|
|||||||
pinned: 'left',
|
pinned: 'left',
|
||||||
width: 150,
|
width: 150,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
field: 'liquidityFee',
|
|
||||||
valueFormatter: ({ value }: { value: number }) => value + '%',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
field: 'feeAfterDiscount',
|
field: 'feeAfterDiscount',
|
||||||
headerName: t('Total fee after discount'),
|
headerName: t('Total fee after discount'),
|
||||||
valueFormatter: ({ value }: { value: number }) => value + '%',
|
valueFormatter: ({ value }: { value: number }) => value + '%',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'totalFee',
|
field: 'liquidityFee',
|
||||||
headerName: t('Total fee before discount'),
|
|
||||||
valueFormatter: ({ value }: { value: number }) => value + '%',
|
valueFormatter: ({ value }: { value: number }) => value + '%',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -43,6 +38,11 @@ const useFeesTableColumnDefs = (): ColDef[] => {
|
|||||||
field: 'makerFee',
|
field: 'makerFee',
|
||||||
valueFormatter: ({ value }: { value: number }) => value + '%',
|
valueFormatter: ({ value }: { value: number }) => value + '%',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
field: 'totalFee',
|
||||||
|
headerName: t('Total fee before discount'),
|
||||||
|
valueFormatter: ({ value }: { value: number }) => value + '%',
|
||||||
|
},
|
||||||
] as ColDef[],
|
] as ColDef[],
|
||||||
[t]
|
[t]
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import {
|
|||||||
lpAggregatedDataProvider,
|
lpAggregatedDataProvider,
|
||||||
type Filter,
|
type Filter,
|
||||||
LiquidityTable,
|
LiquidityTable,
|
||||||
liquidityProvisionsDataProvider,
|
|
||||||
} from '@vegaprotocol/liquidity';
|
} from '@vegaprotocol/liquidity';
|
||||||
import { getAsset, useMarket } from '@vegaprotocol/markets';
|
import { getAsset, useMarket } from '@vegaprotocol/markets';
|
||||||
import {
|
import {
|
||||||
@@ -71,7 +70,7 @@ export const LiquidityContainer = ({
|
|||||||
|
|
||||||
const useReloadLiquidityData = (marketId: string | undefined) => {
|
const useReloadLiquidityData = (marketId: string | undefined) => {
|
||||||
const { reload } = useDataProvider({
|
const { reload } = useDataProvider({
|
||||||
dataProvider: liquidityProvisionsDataProvider,
|
dataProvider: lpAggregatedDataProvider,
|
||||||
variables: { marketId: marketId || '' },
|
variables: { marketId: marketId || '' },
|
||||||
update: () => true,
|
update: () => true,
|
||||||
skip: !marketId,
|
skip: !marketId,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
useMaliciousOracle,
|
useMaliciousOracle,
|
||||||
} from '@vegaprotocol/markets';
|
} from '@vegaprotocol/markets';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { type MarketViewProposalFieldsFragment } from '@vegaprotocol/proposals';
|
import { type ProposalFragment } from '@vegaprotocol/proposals';
|
||||||
import { MarketSuspendedBanner } from './market-suspended-banner';
|
import { MarketSuspendedBanner } from './market-suspended-banner';
|
||||||
import { MarketUpdateBanner } from './market-update-banner';
|
import { MarketUpdateBanner } from './market-update-banner';
|
||||||
import { MarketUpdateStateBanner } from './market-update-state-banner';
|
import { MarketUpdateStateBanner } from './market-update-state-banner';
|
||||||
@@ -22,17 +22,17 @@ import {
|
|||||||
|
|
||||||
type UpdateMarketBanner = {
|
type UpdateMarketBanner = {
|
||||||
kind: 'UpdateMarket';
|
kind: 'UpdateMarket';
|
||||||
proposals: MarketViewProposalFieldsFragment[];
|
proposals: ProposalFragment[];
|
||||||
};
|
};
|
||||||
|
|
||||||
type UpdateMarketStateBanner = {
|
type UpdateMarketStateBanner = {
|
||||||
kind: 'UpdateMarketState';
|
kind: 'UpdateMarketState';
|
||||||
proposals: MarketViewProposalFieldsFragment[];
|
proposals: ProposalFragment[];
|
||||||
};
|
};
|
||||||
|
|
||||||
type NewMarketBanner = {
|
type NewMarketBanner = {
|
||||||
kind: 'NewMarket'; // aka a proposal of NewMarket which succeeds the current market
|
kind: 'NewMarket'; // aka a proposal of NewMarket which succeeds the current market
|
||||||
proposals: MarketViewProposalFieldsFragment[];
|
proposals: ProposalFragment[];
|
||||||
};
|
};
|
||||||
|
|
||||||
type SettledBanner = {
|
type SettledBanner = {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Fragment } from 'react';
|
import { Fragment } from 'react';
|
||||||
import { type MarketViewProposalFieldsFragment } from '@vegaprotocol/proposals';
|
import { type ProposalFragment } from '@vegaprotocol/proposals';
|
||||||
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||||
import { DApp, TOKEN_PROPOSAL, useLinks } from '@vegaprotocol/environment';
|
import { DApp, TOKEN_PROPOSAL, useLinks } from '@vegaprotocol/environment';
|
||||||
import { useT } from '../../lib/use-t';
|
import { useT } from '../../lib/use-t';
|
||||||
@@ -7,7 +7,7 @@ import { useT } from '../../lib/use-t';
|
|||||||
export const MarketSuccessorProposalBanner = ({
|
export const MarketSuccessorProposalBanner = ({
|
||||||
proposals,
|
proposals,
|
||||||
}: {
|
}: {
|
||||||
proposals: MarketViewProposalFieldsFragment[];
|
proposals: ProposalFragment[];
|
||||||
}) => {
|
}) => {
|
||||||
const t = useT();
|
const t = useT();
|
||||||
const tokenLink = useLinks(DApp.Governance);
|
const tokenLink = useLinks(DApp.Governance);
|
||||||
@@ -28,7 +28,7 @@ export const MarketSuccessorProposalBanner = ({
|
|||||||
}
|
}
|
||||||
)}{' '}
|
)}{' '}
|
||||||
{proposals.map((item, i) => {
|
{proposals.map((item, i) => {
|
||||||
if (item.terms.change.__typename !== 'NewMarket') {
|
if (item.terms?.change.__typename !== 'NewMarket') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import { ProposalState } from '@vegaprotocol/types';
|
import { ProposalState } from '@vegaprotocol/types';
|
||||||
import { MarketUpdateBanner } from './market-update-banner';
|
import { MarketUpdateBanner } from './market-update-banner';
|
||||||
import { type MarketViewProposalFieldsFragment } from '@vegaprotocol/proposals';
|
import { type ProposalFragment } from '@vegaprotocol/proposals';
|
||||||
|
|
||||||
describe('MarketUpdateBanner', () => {
|
describe('MarketUpdateBanner', () => {
|
||||||
const change = {
|
const change = {
|
||||||
@@ -34,9 +34,7 @@ describe('MarketUpdateBanner', () => {
|
|||||||
|
|
||||||
it('renders content for a single open proposal', () => {
|
it('renders content for a single open proposal', () => {
|
||||||
render(
|
render(
|
||||||
<MarketUpdateBanner
|
<MarketUpdateBanner proposals={[openProposal as ProposalFragment]} />
|
||||||
proposals={[openProposal as MarketViewProposalFieldsFragment]}
|
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
@@ -50,9 +48,7 @@ describe('MarketUpdateBanner', () => {
|
|||||||
|
|
||||||
it('renders content for a single passed proposal', () => {
|
it('renders content for a single passed proposal', () => {
|
||||||
render(
|
render(
|
||||||
<MarketUpdateBanner
|
<MarketUpdateBanner proposals={[passedProposal as ProposalFragment]} />
|
||||||
proposals={[passedProposal as MarketViewProposalFieldsFragment]}
|
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
@@ -65,10 +61,7 @@ describe('MarketUpdateBanner', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('renders content for multiple passed proposals', () => {
|
it('renders content for multiple passed proposals', () => {
|
||||||
const proposals = [
|
const proposals = [openProposal, openProposal] as ProposalFragment[];
|
||||||
openProposal,
|
|
||||||
openProposal,
|
|
||||||
] as MarketViewProposalFieldsFragment[];
|
|
||||||
|
|
||||||
render(<MarketUpdateBanner proposals={proposals} />);
|
render(<MarketUpdateBanner proposals={proposals} />);
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
TOKEN_PROPOSALS,
|
TOKEN_PROPOSALS,
|
||||||
useLinks,
|
useLinks,
|
||||||
} from '@vegaprotocol/environment';
|
} from '@vegaprotocol/environment';
|
||||||
import { type MarketViewProposalFieldsFragment } from '@vegaprotocol/proposals';
|
import { type ProposalFragment } from '@vegaprotocol/proposals';
|
||||||
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||||
import { ProposalState } from '@vegaprotocol/types';
|
import { ProposalState } from '@vegaprotocol/types';
|
||||||
import { useT } from '../../lib/use-t';
|
import { useT } from '../../lib/use-t';
|
||||||
@@ -14,17 +14,17 @@ import { useT } from '../../lib/use-t';
|
|||||||
export const MarketUpdateBanner = ({
|
export const MarketUpdateBanner = ({
|
||||||
proposals,
|
proposals,
|
||||||
}: {
|
}: {
|
||||||
proposals: MarketViewProposalFieldsFragment[];
|
proposals: ProposalFragment[];
|
||||||
}) => {
|
}) => {
|
||||||
const governanceLink = useLinks(DApp.Governance);
|
const governanceLink = useLinks(DApp.Governance);
|
||||||
const t = useT();
|
const t = useT();
|
||||||
const openProposals = sortBy(
|
const openProposals = sortBy(
|
||||||
proposals.filter((p) => p.state === ProposalState.STATE_OPEN),
|
proposals.filter((p) => p.state === ProposalState.STATE_OPEN),
|
||||||
(p) => p.terms.enactmentDatetime
|
(p) => p.terms?.enactmentDatetime
|
||||||
);
|
);
|
||||||
const passedProposals = sortBy(
|
const passedProposals = sortBy(
|
||||||
proposals.filter((p) => p.state === ProposalState.STATE_PASSED),
|
proposals.filter((p) => p.state === ProposalState.STATE_PASSED),
|
||||||
(p) => p.terms.enactmentDatetime
|
(p) => p.terms?.enactmentDatetime
|
||||||
);
|
);
|
||||||
|
|
||||||
let content = null;
|
let content = null;
|
||||||
@@ -49,7 +49,7 @@ export const MarketUpdateBanner = ({
|
|||||||
content = (
|
content = (
|
||||||
<p>
|
<p>
|
||||||
{t('Proposal set to change market on {{date}}.', {
|
{t('Proposal set to change market on {{date}}.', {
|
||||||
date: format(new Date(proposal.terms.enactmentDatetime), 'dd MMMM'),
|
date: format(new Date(proposal.terms?.enactmentDatetime), 'dd MMMM'),
|
||||||
})}
|
})}
|
||||||
<ExternalLink href={proposalLink}>{t('View proposal')}</ExternalLink>,
|
<ExternalLink href={proposalLink}>{t('View proposal')}</ExternalLink>,
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { type ReactNode } from 'react';
|
import { type ReactNode } from 'react';
|
||||||
import sortBy from 'lodash/sortBy';
|
import sortBy from 'lodash/sortBy';
|
||||||
import { format, formatDuration, intervalToDuration } from 'date-fns';
|
import { format, formatDuration, intervalToDuration } from 'date-fns';
|
||||||
import { type MarketViewProposalFieldsFragment } from '@vegaprotocol/proposals';
|
import { type ProposalFragment } from '@vegaprotocol/proposals';
|
||||||
import { ProposalState } from '@vegaprotocol/types';
|
import { MarketUpdateType, ProposalState } from '@vegaprotocol/types';
|
||||||
import {
|
import {
|
||||||
DApp,
|
DApp,
|
||||||
TOKEN_PROPOSAL,
|
TOKEN_PROPOSAL,
|
||||||
@@ -19,18 +19,29 @@ export const MarketUpdateStateBanner = ({
|
|||||||
proposals,
|
proposals,
|
||||||
}: {
|
}: {
|
||||||
market: Market;
|
market: Market;
|
||||||
proposals: MarketViewProposalFieldsFragment[];
|
proposals: ProposalFragment[];
|
||||||
}) => {
|
}) => {
|
||||||
const t = useT();
|
const t = useT();
|
||||||
const governanceLink = useLinks(DApp.Governance);
|
const governanceLink = useLinks(DApp.Governance);
|
||||||
|
|
||||||
|
const openTradingProposals = sortBy(
|
||||||
|
proposals.filter(
|
||||||
|
(p) =>
|
||||||
|
p.terms &&
|
||||||
|
p.terms.change.__typename === 'UpdateMarketState' &&
|
||||||
|
p.terms.change.updateType ===
|
||||||
|
MarketUpdateType.MARKET_STATE_UPDATE_TYPE_RESUME
|
||||||
|
),
|
||||||
|
(p) => p.terms?.enactmentDatetime
|
||||||
|
);
|
||||||
|
|
||||||
const openProposals = sortBy(
|
const openProposals = sortBy(
|
||||||
proposals.filter((p) => p.state === ProposalState.STATE_OPEN),
|
proposals.filter((p) => p.state === ProposalState.STATE_OPEN),
|
||||||
(p) => p.terms.enactmentDatetime
|
(p) => p.terms?.enactmentDatetime
|
||||||
);
|
);
|
||||||
const passedProposals = sortBy(
|
const passedProposals = sortBy(
|
||||||
proposals.filter((p) => p.state === ProposalState.STATE_PASSED),
|
proposals.filter((p) => p.state === ProposalState.STATE_PASSED),
|
||||||
(p) => p.terms.enactmentDatetime
|
(p) => p.terms?.enactmentDatetime
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!passedProposals.length && !openProposals.length) {
|
if (!passedProposals.length && !openProposals.length) {
|
||||||
@@ -45,12 +56,41 @@ export const MarketUpdateStateBanner = ({
|
|||||||
? governanceLink(TOKEN_PROPOSAL.replace(':id', openProposals[0]?.id))
|
? governanceLink(TOKEN_PROPOSAL.replace(':id', openProposals[0]?.id))
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
|
const openTradingProposalsLink =
|
||||||
|
openTradingProposals[0]?.__typename === 'Proposal' &&
|
||||||
|
openTradingProposals[0]?.id
|
||||||
|
? governanceLink(
|
||||||
|
TOKEN_PROPOSAL.replace(':id', openTradingProposals[0].id)
|
||||||
|
)
|
||||||
|
: openTradingProposals[0]?.__typename === 'ProposalDetail' &&
|
||||||
|
openTradingProposals[0]?.batchId
|
||||||
|
? governanceLink(
|
||||||
|
TOKEN_PROPOSAL.replace(':id', openTradingProposals[0].batchId)
|
||||||
|
)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
const proposalsLink =
|
const proposalsLink =
|
||||||
openProposals.length > 1 ? governanceLink(TOKEN_PROPOSALS) : undefined;
|
openProposals.length > 1 ? governanceLink(TOKEN_PROPOSALS) : undefined;
|
||||||
|
|
||||||
let content: ReactNode;
|
let content: ReactNode;
|
||||||
|
|
||||||
if (passedProposals.length) {
|
if (openTradingProposals.length >= 1) {
|
||||||
|
content = (
|
||||||
|
<>
|
||||||
|
<p className="mb-1">
|
||||||
|
{t(
|
||||||
|
'Trading on market {{name}} was suspended by governance. There are open proposals to resume trading on this market.',
|
||||||
|
{ name }
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<ExternalLink href={openTradingProposalsLink}>
|
||||||
|
{t('View proposals')}
|
||||||
|
</ExternalLink>
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
} else if (passedProposals.length) {
|
||||||
const { date, duration, price } = getMessageVariables(passedProposals[0]);
|
const { date, duration, price } = getMessageVariables(passedProposals[0]);
|
||||||
content = (
|
content = (
|
||||||
<>
|
<>
|
||||||
@@ -116,20 +156,23 @@ export const MarketUpdateStateBanner = ({
|
|||||||
return <div data-testid={`update-state-banner-${market.id}`}>{content}</div>;
|
return <div data-testid={`update-state-banner-${market.id}`}>{content}</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getMessageVariables = (proposal: MarketViewProposalFieldsFragment) => {
|
const getMessageVariables = (proposal: ProposalFragment) => {
|
||||||
const enactmentDatetime = new Date(proposal.terms.enactmentDatetime);
|
const enactmentDatetime =
|
||||||
const date = format(enactmentDatetime, 'dd MMMM');
|
proposal.terms && new Date(proposal.terms.enactmentDatetime);
|
||||||
const duration = formatDuration(
|
const date = enactmentDatetime && format(enactmentDatetime, 'dd MMMM');
|
||||||
intervalToDuration({
|
const duration =
|
||||||
start: new Date(),
|
enactmentDatetime &&
|
||||||
end: enactmentDatetime,
|
formatDuration(
|
||||||
}),
|
intervalToDuration({
|
||||||
{
|
start: new Date(),
|
||||||
format: ['days', 'hours'],
|
end: enactmentDatetime,
|
||||||
}
|
}),
|
||||||
);
|
{
|
||||||
|
format: ['days', 'hours'],
|
||||||
|
}
|
||||||
|
);
|
||||||
const price =
|
const price =
|
||||||
proposal.terms.change.__typename === 'UpdateMarketState'
|
proposal.terms?.change.__typename === 'UpdateMarketState'
|
||||||
? proposal.terms.change.price
|
? proposal.terms.change.price
|
||||||
: '';
|
: '';
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import {
|
import {
|
||||||
useMarketProposals,
|
useMarketProposals,
|
||||||
type MarketViewProposalFieldsFragment,
|
type ProposalFragment,
|
||||||
} from '@vegaprotocol/proposals';
|
} from '@vegaprotocol/proposals';
|
||||||
import { ProposalState, ProposalType } from '@vegaprotocol/types';
|
import { ProposalState, ProposalType } from '@vegaprotocol/types';
|
||||||
|
|
||||||
const isPending = (p: MarketViewProposalFieldsFragment) => {
|
const isPending = (p: ProposalFragment) => {
|
||||||
return [ProposalState.STATE_OPEN, ProposalState.STATE_PASSED].includes(
|
return [ProposalState.STATE_OPEN, ProposalState.STATE_PASSED].includes(
|
||||||
p.state
|
p.state
|
||||||
);
|
);
|
||||||
@@ -21,9 +21,10 @@ export const useUpdateMarketStateProposals = (
|
|||||||
|
|
||||||
const proposals = data
|
const proposals = data
|
||||||
? data.filter(isPending).filter((p) => {
|
? data.filter(isPending).filter((p) => {
|
||||||
const change = p.terms.change;
|
const change = p.terms?.change;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
change &&
|
||||||
change.__typename === 'UpdateMarketState' &&
|
change.__typename === 'UpdateMarketState' &&
|
||||||
change.market.id === marketId
|
change.market.id === marketId
|
||||||
) {
|
) {
|
||||||
@@ -48,8 +49,9 @@ export const useUpdateMarketProposals = (
|
|||||||
|
|
||||||
const proposals = data
|
const proposals = data
|
||||||
? data.filter(isPending).filter((p) => {
|
? data.filter(isPending).filter((p) => {
|
||||||
const change = p.terms.change;
|
const change = p.terms?.change;
|
||||||
if (
|
if (
|
||||||
|
change &&
|
||||||
change.__typename === 'UpdateMarket' &&
|
change.__typename === 'UpdateMarket' &&
|
||||||
change.marketId === marketId
|
change.marketId === marketId
|
||||||
) {
|
) {
|
||||||
@@ -73,8 +75,9 @@ export const useSuccessorMarketProposals = (
|
|||||||
|
|
||||||
const proposals = data
|
const proposals = data
|
||||||
? data.filter(isPending).filter((p) => {
|
? data.filter(isPending).filter((p) => {
|
||||||
const change = p.terms.change;
|
const change = p.terms?.change;
|
||||||
if (
|
if (
|
||||||
|
change &&
|
||||||
change.__typename === 'NewMarket' &&
|
change.__typename === 'NewMarket' &&
|
||||||
change.successorConfiguration?.parentMarketId === marketId
|
change.successorConfiguration?.parentMarketId === marketId
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { useT } from '../../lib/use-t';
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { MarketHeaderStats } from '../../client-pages/market/market-header-stats';
|
import { MarketHeaderStats } from '../../client-pages/market/market-header-stats';
|
||||||
import { MarketMarkPrice } from '../market-mark-price';
|
import { MarketMarkPrice } from '../market-mark-price';
|
||||||
|
import { MarketBanner } from '../market-banner';
|
||||||
/**
|
/**
|
||||||
* This is only rendered for the mobile navigation
|
* This is only rendered for the mobile navigation
|
||||||
*/
|
*/
|
||||||
@@ -108,8 +109,11 @@ export const MobileMarketHeader = () => {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
{data && (
|
{data && (
|
||||||
<div className="px-3 py-6 text-sm grid grid-cols-2 items-center gap-x-4 gap-y-6">
|
<div>
|
||||||
<MarketHeaderStats market={data} />
|
<MarketBanner market={data} />
|
||||||
|
<div className="px-3 py-6 text-sm grid grid-cols-2 items-center gap-x-4 gap-y-6">
|
||||||
|
<MarketHeaderStats market={data} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</FullScreenPopover>
|
</FullScreenPopover>
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import {
|
import { ActiveRewardCard, applyFilter } from './active-rewards';
|
||||||
ActiveRewardCard,
|
|
||||||
applyFilter,
|
|
||||||
isActiveReward,
|
|
||||||
} from './active-rewards';
|
|
||||||
import {
|
import {
|
||||||
AccountType,
|
AccountType,
|
||||||
AssetStatus,
|
AssetStatus,
|
||||||
@@ -11,54 +7,13 @@ import {
|
|||||||
DistributionStrategy,
|
DistributionStrategy,
|
||||||
EntityScope,
|
EntityScope,
|
||||||
IndividualScope,
|
IndividualScope,
|
||||||
type RecurringTransfer,
|
|
||||||
type TransferNode,
|
|
||||||
TransferStatus,
|
TransferStatus,
|
||||||
type Transfer,
|
type Transfer,
|
||||||
} from '@vegaprotocol/types';
|
} from '@vegaprotocol/types';
|
||||||
|
import { type EnrichedRewardTransfer } from '../../lib/hooks/use-rewards';
|
||||||
jest.mock('./__generated__/Rewards', () => ({
|
|
||||||
useMarketForRewardsQuery: () => ({
|
|
||||||
data: undefined,
|
|
||||||
}),
|
|
||||||
}));
|
|
||||||
|
|
||||||
jest.mock('@vegaprotocol/assets', () => ({
|
|
||||||
useAssetDataProvider: () => {
|
|
||||||
return {
|
|
||||||
data: {
|
|
||||||
assetId: 'asset-1',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('ActiveRewards', () => {
|
describe('ActiveRewards', () => {
|
||||||
const mockRecurringTransfer: RecurringTransfer = {
|
const reward: EnrichedRewardTransfer = {
|
||||||
__typename: 'RecurringTransfer',
|
|
||||||
startEpoch: 115332,
|
|
||||||
endEpoch: 115432,
|
|
||||||
factor: '1',
|
|
||||||
dispatchStrategy: {
|
|
||||||
__typename: 'DispatchStrategy',
|
|
||||||
dispatchMetric: DispatchMetric.DISPATCH_METRIC_LP_FEES_RECEIVED,
|
|
||||||
dispatchMetricAssetId:
|
|
||||||
'c9fe6fc24fce121b2cc72680543a886055abb560043fda394ba5376203b7527d',
|
|
||||||
marketIdsInScope: null,
|
|
||||||
entityScope: EntityScope.ENTITY_SCOPE_INDIVIDUALS,
|
|
||||||
individualScope: IndividualScope.INDIVIDUAL_SCOPE_ALL,
|
|
||||||
teamScope: null,
|
|
||||||
nTopPerformers: '',
|
|
||||||
stakingRequirement: '',
|
|
||||||
notionalTimeWeightedAveragePositionRequirement: '',
|
|
||||||
windowLength: 1,
|
|
||||||
lockPeriod: 0,
|
|
||||||
distributionStrategy: DistributionStrategy.DISTRIBUTION_STRATEGY_PRO_RATA,
|
|
||||||
rankTable: null,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const mockTransferNode: TransferNode = {
|
|
||||||
__typename: 'TransferNode',
|
__typename: 'TransferNode',
|
||||||
transfer: {
|
transfer: {
|
||||||
__typename: 'Transfer',
|
__typename: 'Transfer',
|
||||||
@@ -86,21 +41,37 @@ describe('ActiveRewards', () => {
|
|||||||
reference: 'reward',
|
reference: 'reward',
|
||||||
status: TransferStatus.STATUS_PENDING,
|
status: TransferStatus.STATUS_PENDING,
|
||||||
timestamp: '2023-12-18T13:05:35.948706Z',
|
timestamp: '2023-12-18T13:05:35.948706Z',
|
||||||
kind: mockRecurringTransfer,
|
kind: {
|
||||||
|
__typename: 'RecurringTransfer',
|
||||||
|
startEpoch: 115332,
|
||||||
|
endEpoch: 115432,
|
||||||
|
factor: '1',
|
||||||
|
dispatchStrategy: {
|
||||||
|
__typename: 'DispatchStrategy',
|
||||||
|
dispatchMetric: DispatchMetric.DISPATCH_METRIC_LP_FEES_RECEIVED,
|
||||||
|
dispatchMetricAssetId:
|
||||||
|
'c9fe6fc24fce121b2cc72680543a886055abb560043fda394ba5376203b7527d',
|
||||||
|
marketIdsInScope: null,
|
||||||
|
entityScope: EntityScope.ENTITY_SCOPE_INDIVIDUALS,
|
||||||
|
individualScope: IndividualScope.INDIVIDUAL_SCOPE_ALL,
|
||||||
|
teamScope: null,
|
||||||
|
nTopPerformers: '',
|
||||||
|
stakingRequirement: '',
|
||||||
|
notionalTimeWeightedAveragePositionRequirement: '',
|
||||||
|
windowLength: 1,
|
||||||
|
lockPeriod: 0,
|
||||||
|
distributionStrategy:
|
||||||
|
DistributionStrategy.DISTRIBUTION_STRATEGY_PRO_RATA,
|
||||||
|
rankTable: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
reason: null,
|
reason: null,
|
||||||
},
|
},
|
||||||
fees: [],
|
fees: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
it('renders with valid props', () => {
|
it('renders with valid props', () => {
|
||||||
render(
|
render(<ActiveRewardCard transferNode={reward} currentEpoch={115432} />);
|
||||||
<ActiveRewardCard
|
|
||||||
transferNode={mockTransferNode}
|
|
||||||
currentEpoch={1}
|
|
||||||
kind={mockRecurringTransfer}
|
|
||||||
allMarkets={{}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
screen.getByText(/Liquidity provision fees received/i)
|
screen.getByText(/Liquidity provision fees received/i)
|
||||||
@@ -108,41 +79,11 @@ describe('ActiveRewards', () => {
|
|||||||
expect(screen.getByText('Individual scope')).toBeInTheDocument();
|
expect(screen.getByText('Individual scope')).toBeInTheDocument();
|
||||||
expect(screen.getByText('Average position')).toBeInTheDocument();
|
expect(screen.getByText('Average position')).toBeInTheDocument();
|
||||||
expect(screen.getByText('Ends in')).toBeInTheDocument();
|
expect(screen.getByText('Ends in')).toBeInTheDocument();
|
||||||
expect(screen.getByText('115431 epochs')).toBeInTheDocument();
|
expect(screen.getByText('1 epoch')).toBeInTheDocument();
|
||||||
expect(screen.getByText('Assessed over')).toBeInTheDocument();
|
expect(screen.getByText('Assessed over')).toBeInTheDocument();
|
||||||
expect(screen.getByText('1 epoch')).toBeInTheDocument();
|
expect(screen.getByText('1 epoch')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('isActiveReward', () => {
|
|
||||||
it('returns true for valid active reward', () => {
|
|
||||||
const node = {
|
|
||||||
transfer: {
|
|
||||||
kind: {
|
|
||||||
__typename: 'RecurringTransfer',
|
|
||||||
dispatchStrategy: {},
|
|
||||||
endEpoch: 10,
|
|
||||||
},
|
|
||||||
status: TransferStatus.STATUS_PENDING,
|
|
||||||
},
|
|
||||||
} as TransferNode;
|
|
||||||
expect(isActiveReward(node, 5)).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns false for invalid active reward', () => {
|
|
||||||
const node = {
|
|
||||||
transfer: {
|
|
||||||
kind: {
|
|
||||||
__typename: 'RecurringTransfer',
|
|
||||||
dispatchStrategy: {},
|
|
||||||
endEpoch: 10,
|
|
||||||
},
|
|
||||||
status: TransferStatus.STATUS_PENDING,
|
|
||||||
},
|
|
||||||
} as TransferNode;
|
|
||||||
expect(isActiveReward(node, 15)).toBeFalsy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('applyFilter', () => {
|
describe('applyFilter', () => {
|
||||||
it('returns true when filter matches dispatch metric label', () => {
|
it('returns true when filter matches dispatch metric label', () => {
|
||||||
const transfer = {
|
const transfer = {
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
import { useActiveRewardsQuery } from './__generated__/Rewards';
|
|
||||||
import { useT } from '../../lib/use-t';
|
import { useT } from '../../lib/use-t';
|
||||||
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
|
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import {
|
import {
|
||||||
type IconName,
|
|
||||||
type VegaIconSize,
|
type VegaIconSize,
|
||||||
Icon,
|
|
||||||
Intent,
|
|
||||||
Tooltip,
|
Tooltip,
|
||||||
VegaIcon,
|
VegaIcon,
|
||||||
VegaIconNames,
|
VegaIconNames,
|
||||||
@@ -14,18 +10,12 @@ import {
|
|||||||
TinyScroll,
|
TinyScroll,
|
||||||
truncateMiddle,
|
truncateMiddle,
|
||||||
} from '@vegaprotocol/ui-toolkit';
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
import { IconNames } from '@blueprintjs/icons';
|
|
||||||
import {
|
import {
|
||||||
type Maybe,
|
|
||||||
type Transfer,
|
|
||||||
type TransferNode,
|
type TransferNode,
|
||||||
type RecurringTransfer,
|
|
||||||
DistributionStrategyDescriptionMapping,
|
DistributionStrategyDescriptionMapping,
|
||||||
DistributionStrategyMapping,
|
DistributionStrategyMapping,
|
||||||
EntityScope,
|
EntityScope,
|
||||||
EntityScopeMapping,
|
EntityScopeMapping,
|
||||||
TransferStatus,
|
|
||||||
TransferStatusMapping,
|
|
||||||
DispatchMetric,
|
DispatchMetric,
|
||||||
DispatchMetricDescription,
|
DispatchMetricDescription,
|
||||||
DispatchMetricLabels,
|
DispatchMetricLabels,
|
||||||
@@ -34,45 +24,36 @@ import {
|
|||||||
type DispatchStrategy,
|
type DispatchStrategy,
|
||||||
IndividualScopeMapping,
|
IndividualScopeMapping,
|
||||||
IndividualScopeDescriptionMapping,
|
IndividualScopeDescriptionMapping,
|
||||||
|
type Asset,
|
||||||
} from '@vegaprotocol/types';
|
} from '@vegaprotocol/types';
|
||||||
import { Card } from '../card/card';
|
import { Card } from '../card/card';
|
||||||
import { useMemo, useState } from 'react';
|
import { type ReactNode, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
type AssetFieldsFragment,
|
type AssetFieldsFragment,
|
||||||
useAssetsMapProvider,
|
type BasicAssetDetails,
|
||||||
} from '@vegaprotocol/assets';
|
} from '@vegaprotocol/assets';
|
||||||
|
import { type MarketFieldsFragment } from '@vegaprotocol/markets';
|
||||||
import {
|
import {
|
||||||
type MarketFieldsFragment,
|
type EnrichedRewardTransfer,
|
||||||
useMarketsMapProvider,
|
useRewards,
|
||||||
getAsset,
|
} from '../../lib/hooks/use-rewards';
|
||||||
} from '@vegaprotocol/markets';
|
import compact from 'lodash/compact';
|
||||||
|
|
||||||
|
enum CardColour {
|
||||||
|
BLUE = 'BLUE',
|
||||||
|
GREEN = 'GREEN',
|
||||||
|
GREY = 'GREY',
|
||||||
|
ORANGE = 'ORANGE',
|
||||||
|
PINK = 'PINK',
|
||||||
|
PURPLE = 'PURPLE',
|
||||||
|
WHITE = 'WHITE',
|
||||||
|
YELLOW = 'YELLOW',
|
||||||
|
}
|
||||||
|
|
||||||
export type Filter = {
|
export type Filter = {
|
||||||
searchTerm: string;
|
searchTerm: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isActiveReward = (node: TransferNode, currentEpoch: number) => {
|
|
||||||
const { transfer } = node;
|
|
||||||
if (transfer.kind.__typename !== 'RecurringTransfer') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const { dispatchStrategy } = transfer.kind;
|
|
||||||
|
|
||||||
if (!dispatchStrategy) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (transfer.kind.endEpoch && transfer.kind.endEpoch < currentEpoch) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (transfer.status !== TransferStatus.STATUS_PENDING) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const applyFilter = (
|
export const applyFilter = (
|
||||||
node: TransferNode & {
|
node: TransferNode & {
|
||||||
asset?: AssetFieldsFragment | null;
|
asset?: AssetFieldsFragment | null;
|
||||||
@@ -95,7 +76,10 @@ export const applyFilter = (
|
|||||||
transfer.asset?.symbol
|
transfer.asset?.symbol
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.includes(filter.searchTerm.toLowerCase()) ||
|
.includes(filter.searchTerm.toLowerCase()) ||
|
||||||
EntityScopeLabelMapping[transfer.kind.dispatchStrategy.entityScope]
|
(
|
||||||
|
EntityScopeLabelMapping[transfer.kind.dispatchStrategy.entityScope] ||
|
||||||
|
'Unspecified'
|
||||||
|
)
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.includes(filter.searchTerm.toLowerCase()) ||
|
.includes(filter.searchTerm.toLowerCase()) ||
|
||||||
node.asset?.name
|
node.asset?.name
|
||||||
@@ -114,42 +98,15 @@ export const applyFilter = (
|
|||||||
|
|
||||||
export const ActiveRewards = ({ currentEpoch }: { currentEpoch: number }) => {
|
export const ActiveRewards = ({ currentEpoch }: { currentEpoch: number }) => {
|
||||||
const t = useT();
|
const t = useT();
|
||||||
const { data: activeRewardsData } = useActiveRewardsQuery({
|
const { data } = useRewards({
|
||||||
variables: {
|
onlyActive: true,
|
||||||
isReward: true,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const [filter, setFilter] = useState<Filter>({
|
const [filter, setFilter] = useState<Filter>({
|
||||||
searchTerm: '',
|
searchTerm: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data: assets } = useAssetsMapProvider();
|
if (!data || !data.length) return null;
|
||||||
const { data: markets } = useMarketsMapProvider();
|
|
||||||
|
|
||||||
const enrichedTransfers = activeRewardsData?.transfersConnection?.edges
|
|
||||||
?.map((e) => e?.node as TransferNode)
|
|
||||||
.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;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
@@ -157,7 +114,8 @@ export const ActiveRewards = ({ currentEpoch }: { currentEpoch: number }) => {
|
|||||||
className="lg:col-span-full"
|
className="lg:col-span-full"
|
||||||
data-testid="active-rewards-card"
|
data-testid="active-rewards-card"
|
||||||
>
|
>
|
||||||
{enrichedTransfers.length > 1 && (
|
{/** CARDS FILTER */}
|
||||||
|
{data.length > 1 && (
|
||||||
<TradingInput
|
<TradingInput
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setFilter((curr) => ({ ...curr, searchTerm: e.target.value }))
|
setFilter((curr) => ({ ...curr, searchTerm: e.target.value }))
|
||||||
@@ -172,142 +130,32 @@ export const ActiveRewards = ({ currentEpoch }: { currentEpoch: number }) => {
|
|||||||
prependElement={<VegaIcon name={VegaIconNames.SEARCH} />}
|
prependElement={<VegaIcon name={VegaIconNames.SEARCH} />}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{/** CARDS */}
|
||||||
<TinyScroll className="grid gap-x-8 gap-y-10 h-fit grid-cols-[repeat(auto-fill,_minmax(230px,_1fr))] md:grid-cols-[repeat(auto-fill,_minmax(230px,_1fr))] lg:grid-cols-[repeat(auto-fill,_minmax(320px,_1fr))] xl:grid-cols-[repeat(auto-fill,_minmax(335px,_1fr))] max-h-[40rem] overflow-auto pr-2">
|
<TinyScroll className="grid gap-x-8 gap-y-10 h-fit grid-cols-[repeat(auto-fill,_minmax(230px,_1fr))] md:grid-cols-[repeat(auto-fill,_minmax(230px,_1fr))] lg:grid-cols-[repeat(auto-fill,_minmax(320px,_1fr))] xl:grid-cols-[repeat(auto-fill,_minmax(335px,_1fr))] max-h-[40rem] overflow-auto pr-2">
|
||||||
{enrichedTransfers
|
{data
|
||||||
.filter((n) => applyFilter(n, filter))
|
.filter((n) => applyFilter(n, filter))
|
||||||
.map((node, i) => {
|
.map((node, i) => (
|
||||||
const { transfer } = node;
|
<ActiveRewardCard
|
||||||
if (
|
key={i}
|
||||||
transfer.kind.__typename !== 'RecurringTransfer' ||
|
transferNode={node}
|
||||||
!transfer.kind.dispatchStrategy?.dispatchMetric
|
currentEpoch={currentEpoch}
|
||||||
) {
|
/>
|
||||||
return null;
|
))}
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
node && (
|
|
||||||
<ActiveRewardCard
|
|
||||||
key={i}
|
|
||||||
transferNode={node}
|
|
||||||
kind={transfer.kind}
|
|
||||||
currentEpoch={currentEpoch}
|
|
||||||
allMarkets={markets || {}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</TinyScroll>
|
</TinyScroll>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// This was built to be a status indicator for the rewards based on the transfer status
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
const StatusIndicator = ({
|
|
||||||
status,
|
|
||||||
reason,
|
|
||||||
}: {
|
|
||||||
status: TransferStatus;
|
|
||||||
reason?: Maybe<string> | undefined;
|
|
||||||
}) => {
|
|
||||||
const t = useT();
|
|
||||||
const getIconIntent = (status: string) => {
|
|
||||||
switch (status) {
|
|
||||||
case TransferStatus.STATUS_DONE:
|
|
||||||
return { icon: IconNames.TICK_CIRCLE, intent: Intent.Success };
|
|
||||||
case TransferStatus.STATUS_REJECTED:
|
|
||||||
return { icon: IconNames.ERROR, intent: Intent.Danger };
|
|
||||||
default:
|
|
||||||
return { icon: IconNames.HELP, intent: Intent.Primary };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const { icon, intent } = getIconIntent(status);
|
|
||||||
return (
|
|
||||||
<Tooltip
|
|
||||||
description={
|
|
||||||
<span>
|
|
||||||
{t('Transfer status: {{status}} {{reason}}', {
|
|
||||||
status: TransferStatusMapping[status],
|
|
||||||
reason: reason ? `(${reason})` : '',
|
|
||||||
})}
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
className={classNames(
|
|
||||||
{
|
|
||||||
'text-gray-700 dark:text-gray-300': intent === Intent.None,
|
|
||||||
'text-vega-blue': intent === Intent.Primary,
|
|
||||||
'text-vega-green dark:text-vega-green': intent === Intent.Success,
|
|
||||||
'dark:text-yellow text-yellow-600': intent === Intent.Warning,
|
|
||||||
'text-vega-red': intent === Intent.Danger,
|
|
||||||
},
|
|
||||||
'flex items-start p-1 align-text-bottom'
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Icon size={3} name={icon as IconName} />
|
|
||||||
</span>
|
|
||||||
</Tooltip>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
type ActiveRewardCardProps = {
|
type ActiveRewardCardProps = {
|
||||||
transferNode: TransferNode & {
|
transferNode: EnrichedRewardTransfer;
|
||||||
asset?: AssetFieldsFragment | null;
|
|
||||||
markets?: (MarketFieldsFragment | null)[];
|
|
||||||
};
|
|
||||||
currentEpoch: number;
|
currentEpoch: number;
|
||||||
kind: RecurringTransfer;
|
|
||||||
allMarkets?: Record<string, MarketFieldsFragment | null>;
|
|
||||||
};
|
};
|
||||||
export const ActiveRewardCard = ({
|
export const ActiveRewardCard = ({
|
||||||
transferNode,
|
transferNode,
|
||||||
currentEpoch,
|
currentEpoch,
|
||||||
kind,
|
|
||||||
allMarkets,
|
|
||||||
}: ActiveRewardCardProps) => {
|
}: ActiveRewardCardProps) => {
|
||||||
const t = useT();
|
// don't display the cards that are scoped to not trading markets
|
||||||
|
const marketSettled = transferNode.markets?.filter(
|
||||||
const { transfer } = transferNode;
|
|
||||||
const { dispatchStrategy } = kind;
|
|
||||||
|
|
||||||
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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<span>{firstMarketData?.tradableInstrument?.instrument?.name || ''}</span>
|
|
||||||
);
|
|
||||||
}, [firstMarketData, marketIdsInScope, allMarkets]);
|
|
||||||
|
|
||||||
const dispatchAsset = transferNode.asset;
|
|
||||||
|
|
||||||
if (!dispatchStrategy) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gray out/hide the cards that are related to not trading markets
|
|
||||||
const marketSettled = transferNode.markets?.some(
|
|
||||||
(m) =>
|
(m) =>
|
||||||
m?.state &&
|
m?.state &&
|
||||||
[
|
[
|
||||||
@@ -318,97 +166,140 @@ export const ActiveRewardCard = ({
|
|||||||
].includes(m.state)
|
].includes(m.state)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (marketSettled) {
|
// hide the card if all of the markets are being marked as e.g. settled
|
||||||
|
if (
|
||||||
|
marketSettled?.length === transferNode.markets?.length &&
|
||||||
|
Boolean(transferNode.markets && transferNode.markets.length > 0)
|
||||||
|
) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const assetInActiveMarket =
|
let colour =
|
||||||
allMarkets &&
|
DispatchMetricColourMap[
|
||||||
Object.values(allMarkets).some((m: MarketFieldsFragment | null) => {
|
transferNode.transfer.kind.dispatchStrategy.dispatchMetric
|
||||||
if (m && getAsset(m).id === dispatchStrategy.dispatchMetricAssetId) {
|
];
|
||||||
return m?.state && MarketState.STATE_ACTIVE === m.state;
|
|
||||||
|
// grey out of any of the markets is suspended or
|
||||||
|
// if the asset is not currently traded on any of the active markets
|
||||||
|
const marketSuspended =
|
||||||
|
transferNode.markets?.filter(
|
||||||
|
(m) =>
|
||||||
|
m?.state === MarketState.STATE_SUSPENDED ||
|
||||||
|
m?.state === MarketState.STATE_SUSPENDED_VIA_GOVERNANCE
|
||||||
|
).length === transferNode.markets?.length &&
|
||||||
|
Boolean(transferNode.markets && transferNode.markets.length > 0);
|
||||||
|
|
||||||
|
if (marketSuspended || !transferNode.isAssetTraded) {
|
||||||
|
colour = CardColour.GREY;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RewardCard
|
||||||
|
colour={colour}
|
||||||
|
rewardAmount={addDecimalsFormatNumber(
|
||||||
|
transferNode.transfer.amount,
|
||||||
|
transferNode.transfer.asset?.decimals || 0,
|
||||||
|
6
|
||||||
|
)}
|
||||||
|
rewardAsset={transferNode.dispatchAsset}
|
||||||
|
transferAsset={transferNode.transfer.asset || undefined}
|
||||||
|
endsIn={
|
||||||
|
transferNode.transfer.kind.endEpoch != null
|
||||||
|
? transferNode.transfer.kind.endEpoch - currentEpoch
|
||||||
|
: undefined
|
||||||
}
|
}
|
||||||
return false;
|
dispatchStrategy={transferNode.transfer.kind.dispatchStrategy}
|
||||||
});
|
dispatchMetricInfo={<DispatchMetricInfo reward={transferNode} />}
|
||||||
|
/>
|
||||||
const marketSuspended = transferNode.markets?.some(
|
|
||||||
(m) =>
|
|
||||||
m?.state === MarketState.STATE_SUSPENDED ||
|
|
||||||
m?.state === MarketState.STATE_SUSPENDED_VIA_GOVERNANCE
|
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
// Gray out the cards that are related to suspended markets
|
const RewardCard = ({
|
||||||
// Or settlement assets in markets that are not active and eligible for rewards
|
colour,
|
||||||
const { gradientClassName, mainClassName } =
|
rewardAmount,
|
||||||
marketSuspended || !assetInActiveMarket
|
rewardAsset,
|
||||||
? {
|
transferAsset,
|
||||||
gradientClassName: 'from-vega-cdark-500 to-vega-clight-400',
|
vegaAsset,
|
||||||
mainClassName: 'from-vega-cdark-400 dark:from-vega-cdark-600 to-20%',
|
dispatchStrategy,
|
||||||
}
|
endsIn,
|
||||||
: getGradientClasses(dispatchStrategy.dispatchMetric);
|
dispatchMetricInfo,
|
||||||
|
}: {
|
||||||
const entityScope = dispatchStrategy.entityScope;
|
colour: CardColour;
|
||||||
|
rewardAmount: string;
|
||||||
|
/** The asset linked to the dispatch strategy via `dispatchMetricAssetId` property. */
|
||||||
|
rewardAsset?: BasicAssetDetails;
|
||||||
|
/** The VEGA asset details, required to format the min staking amount. */
|
||||||
|
transferAsset?: Asset | undefined;
|
||||||
|
/** The VEGA asset details, required to format the min staking amount. */
|
||||||
|
vegaAsset?: BasicAssetDetails;
|
||||||
|
/** The transfer's dispatch strategy. */
|
||||||
|
dispatchStrategy: DispatchStrategy;
|
||||||
|
/** The number of epochs until the transfer stops. */
|
||||||
|
endsIn?: number;
|
||||||
|
dispatchMetricInfo?: ReactNode;
|
||||||
|
}) => {
|
||||||
|
const t = useT();
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'bg-gradient-to-r col-span-full p-0.5 lg:col-auto h-full',
|
'bg-gradient-to-r col-span-full p-0.5 lg:col-auto h-full',
|
||||||
'rounded-lg',
|
'rounded-lg',
|
||||||
gradientClassName
|
CardColourStyles[colour].gradientClassName
|
||||||
)}
|
)}
|
||||||
data-testid="active-rewards-card"
|
data-testid="active-rewards-card"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
mainClassName,
|
CardColourStyles[colour].mainClassName,
|
||||||
'bg-gradient-to-b bg-vega-clight-800 dark:bg-vega-cdark-800 h-full w-full rounded p-4 flex flex-col gap-4'
|
'bg-gradient-to-b bg-vega-clight-800 dark:bg-vega-cdark-800 h-full w-full rounded-md p-4 flex flex-col gap-4'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="flex justify-between gap-4">
|
<div className="flex justify-between gap-4">
|
||||||
|
{/** ENTITY SCOPE */}
|
||||||
<div className="flex flex-col gap-2 items-center text-center">
|
<div className="flex flex-col gap-2 items-center text-center">
|
||||||
<EntityIcon transfer={transfer} />
|
<EntityIcon entityScope={dispatchStrategy.entityScope} />
|
||||||
{entityScope && (
|
{dispatchStrategy.entityScope && (
|
||||||
<span className="text-muted text-xs" data-testid="entity-scope">
|
<span className="text-muted text-xs" data-testid="entity-scope">
|
||||||
{EntityScopeLabelMapping[entityScope] || t('Unspecified')}
|
{EntityScopeLabelMapping[dispatchStrategy.entityScope] ||
|
||||||
|
t('Unspecified')}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/** AMOUNT AND DISTRIBUTION STRATEGY */}
|
||||||
<div className="flex flex-col gap-2 items-center text-center">
|
<div className="flex flex-col gap-2 items-center text-center">
|
||||||
|
{/** AMOUNT */}
|
||||||
<h3 className="flex flex-col gap-1 text-2xl shrink-1 text-center">
|
<h3 className="flex flex-col gap-1 text-2xl shrink-1 text-center">
|
||||||
<span className="font-glitch" data-testid="reward-value">
|
<span className="font-glitch" data-testid="reward-value">
|
||||||
{addDecimalsFormatNumber(
|
{rewardAmount}
|
||||||
transferNode.transfer.amount,
|
|
||||||
transferNode.transfer.asset?.decimals || 0,
|
|
||||||
6
|
|
||||||
)}
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span className="font-alpha">
|
<span className="font-alpha" data-testid="reward-asset">
|
||||||
{transferNode.transfer.asset?.symbol}
|
{transferAsset?.symbol || ''}
|
||||||
</span>
|
</span>
|
||||||
</h3>
|
</h3>
|
||||||
{
|
|
||||||
<Tooltip
|
{/** DISTRIBUTION STRATEGY */}
|
||||||
description={t(
|
<Tooltip
|
||||||
DistributionStrategyDescriptionMapping[
|
description={t(
|
||||||
|
DistributionStrategyDescriptionMapping[
|
||||||
|
dispatchStrategy.distributionStrategy
|
||||||
|
]
|
||||||
|
)}
|
||||||
|
underline={true}
|
||||||
|
>
|
||||||
|
<span className="text-xs" data-testid="distribution-strategy">
|
||||||
|
{
|
||||||
|
DistributionStrategyMapping[
|
||||||
dispatchStrategy.distributionStrategy
|
dispatchStrategy.distributionStrategy
|
||||||
]
|
]
|
||||||
)}
|
}
|
||||||
underline={true}
|
</span>
|
||||||
>
|
</Tooltip>
|
||||||
<span className="text-xs" data-testid="distribution-strategy">
|
|
||||||
{
|
|
||||||
DistributionStrategyMapping[
|
|
||||||
dispatchStrategy.distributionStrategy
|
|
||||||
]
|
|
||||||
}
|
|
||||||
</span>
|
|
||||||
</Tooltip>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/** DISTRIBUTION DELAY */}
|
||||||
<div className="flex flex-col gap-2 items-center text-center">
|
<div className="flex flex-col gap-2 items-center text-center">
|
||||||
<CardIcon
|
<CardIcon
|
||||||
iconName={VegaIconNames.LOCK}
|
iconName={VegaIconNames.LOCK}
|
||||||
@@ -421,63 +312,60 @@ export const ActiveRewardCard = ({
|
|||||||
data-testid="locked-for"
|
data-testid="locked-for"
|
||||||
>
|
>
|
||||||
{t('numberEpochs', '{{count}} epochs', {
|
{t('numberEpochs', '{{count}} epochs', {
|
||||||
count: kind.dispatchStrategy?.lockPeriod,
|
count: dispatchStrategy.lockPeriod,
|
||||||
})}
|
})}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span className="border-[0.5px] border-gray-700" />
|
<span className="border-[0.5px] border-gray-700" />
|
||||||
<span data-testid="dispatch-metric-info">
|
{/** DISPATCH METRIC */}
|
||||||
{DispatchMetricLabels[dispatchStrategy.dispatchMetric]} •{' '}
|
{dispatchMetricInfo ? (
|
||||||
<Tooltip
|
dispatchMetricInfo
|
||||||
underline={marketSuspended}
|
) : (
|
||||||
description={
|
<span data-testid="dispatch-metric-info">
|
||||||
(marketSuspended || !assetInActiveMarket) &&
|
{DispatchMetricLabels[dispatchStrategy.dispatchMetric]}
|
||||||
(specificMarkets
|
</span>
|
||||||
? t('Eligible market(s) currently suspended')
|
)}
|
||||||
: !assetInActiveMarket
|
|
||||||
? t('Currently no markets eligible for reward')
|
|
||||||
: '')
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<span>{specificMarkets || dispatchAsset?.name}</span>
|
|
||||||
</Tooltip>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<div className="flex items-center gap-8 flex-wrap">
|
<div className="flex items-center gap-8 flex-wrap">
|
||||||
{kind.endEpoch && (
|
{/** ENDS IN */}
|
||||||
|
{endsIn != null && (
|
||||||
<span className="flex flex-col">
|
<span className="flex flex-col">
|
||||||
<span className="text-muted text-xs">{t('Ends in')} </span>
|
<span className="text-muted text-xs">{t('Ends in')} </span>
|
||||||
<span data-testid="ends-in">
|
<span data-testid="ends-in" data-endsin={endsIn}>
|
||||||
{t('numberEpochs', '{{count}} epochs', {
|
{endsIn >= 0
|
||||||
count: kind.endEpoch - currentEpoch,
|
? t('numberEpochs', '{{count}} epochs', {
|
||||||
})}
|
count: endsIn,
|
||||||
|
})
|
||||||
|
: t('Ended')}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{
|
{/** WINDOW LENGTH */}
|
||||||
<span className="flex flex-col">
|
<span className="flex flex-col">
|
||||||
<span className="text-muted text-xs">{t('Assessed over')}</span>
|
<span className="text-muted text-xs">{t('Assessed over')}</span>
|
||||||
<span data-testid="assessed-over">
|
<span data-testid="assessed-over">
|
||||||
{t('numberEpochs', '{{count}} epochs', {
|
{t('numberEpochs', '{{count}} epochs', {
|
||||||
count: dispatchStrategy.windowLength,
|
count: dispatchStrategy.windowLength,
|
||||||
})}
|
})}
|
||||||
</span>
|
|
||||||
</span>
|
</span>
|
||||||
}
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
{/** DISPATCH METRIC DESCRIPTION */}
|
||||||
{dispatchStrategy?.dispatchMetric && (
|
{dispatchStrategy?.dispatchMetric && (
|
||||||
<span className="text-muted text-sm h-[3rem]">
|
<span className="text-muted text-sm h-[3rem]">
|
||||||
{t(DispatchMetricDescription[dispatchStrategy?.dispatchMetric])}
|
{t(DispatchMetricDescription[dispatchStrategy?.dispatchMetric])}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
<span className="border-[0.5px] border-gray-700" />
|
<span className="border-[0.5px] border-gray-700" />
|
||||||
{kind.dispatchStrategy && (
|
{/** REQUIREMENTS */}
|
||||||
|
{dispatchStrategy && (
|
||||||
<RewardRequirements
|
<RewardRequirements
|
||||||
dispatchStrategy={kind.dispatchStrategy}
|
dispatchStrategy={dispatchStrategy}
|
||||||
assetDecimalPlaces={transfer.asset?.decimals}
|
rewardAsset={rewardAsset}
|
||||||
|
vegaAsset={vegaAsset}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -487,77 +375,67 @@ export const ActiveRewardCard = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const DispatchMetricInfo = ({
|
export const DispatchMetricInfo = ({
|
||||||
transferNode,
|
reward,
|
||||||
allMarkets,
|
|
||||||
}: {
|
}: {
|
||||||
transferNode: ActiveRewardCardProps['transferNode'];
|
reward: EnrichedRewardTransfer;
|
||||||
allMarkets?: ActiveRewardCardProps['allMarkets'];
|
|
||||||
}) => {
|
}) => {
|
||||||
const dispatchStrategy =
|
const t = useT();
|
||||||
transferNode.transfer.kind.__typename === 'RecurringTransfer'
|
const dispatchStrategy = reward.transfer.kind.dispatchStrategy;
|
||||||
? transferNode.transfer.kind.dispatchStrategy
|
const marketNames = compact(
|
||||||
: null;
|
reward.markets?.map((m) => m.tradableInstrument.instrument.name)
|
||||||
|
);
|
||||||
|
|
||||||
const dispatchAsset = transferNode.transfer.asset;
|
let additionalDispatchMetricInfo = null;
|
||||||
|
|
||||||
const marketIdsInScope = dispatchStrategy?.marketIdsInScope;
|
// if asset found then display asset symbol
|
||||||
const firstMarketData = transferNode.markets?.[0];
|
if (reward.dispatchAsset) {
|
||||||
const specificMarkets = useMemo(() => {
|
additionalDispatchMetricInfo = <span>{reward.dispatchAsset.symbol}</span>;
|
||||||
if (
|
}
|
||||||
!firstMarketData ||
|
// but if scoped to only one market then display market name
|
||||||
!marketIdsInScope ||
|
if (marketNames.length === 1) {
|
||||||
marketIdsInScope.length === 0
|
additionalDispatchMetricInfo = <span>{marketNames[0]}</span>;
|
||||||
) {
|
}
|
||||||
return null;
|
// or if scoped to many markets then indicate it's scoped to "specific markets"
|
||||||
}
|
if (marketNames.length > 1) {
|
||||||
if (marketIdsInScope.length > 1) {
|
additionalDispatchMetricInfo = (
|
||||||
const marketNames =
|
<Tooltip description={marketNames}>
|
||||||
allMarkets &&
|
<span>{t('Specific markets')}</span>
|
||||||
marketIdsInScope
|
</Tooltip>
|
||||||
.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 (
|
return (
|
||||||
<span data-testid="dispatch-metric-info">
|
<span data-testid="dispatch-metric-info">
|
||||||
{DispatchMetricLabels[dispatchStrategy.dispatchMetric]} •{' '}
|
{DispatchMetricLabels[dispatchStrategy.dispatchMetric]}
|
||||||
<span>{specificMarkets || dispatchAsset?.name}</span>
|
{additionalDispatchMetricInfo != null && (
|
||||||
|
<> • {additionalDispatchMetricInfo}</>
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const RewardRequirements = ({
|
const RewardRequirements = ({
|
||||||
dispatchStrategy,
|
dispatchStrategy,
|
||||||
assetDecimalPlaces = 0,
|
rewardAsset,
|
||||||
|
vegaAsset,
|
||||||
}: {
|
}: {
|
||||||
dispatchStrategy: DispatchStrategy;
|
dispatchStrategy: DispatchStrategy;
|
||||||
assetDecimalPlaces: number | undefined;
|
rewardAsset?: BasicAssetDetails;
|
||||||
|
vegaAsset?: BasicAssetDetails;
|
||||||
}) => {
|
}) => {
|
||||||
const t = useT();
|
const t = useT();
|
||||||
|
|
||||||
|
const entityLabel = EntityScopeLabelMapping[dispatchStrategy.entityScope];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<dl className="flex justify-between flex-wrap items-center gap-3 text-xs">
|
<dl className="flex justify-between flex-wrap items-center gap-3 text-xs">
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<dt className="flex items-center gap-1 text-muted">
|
<dt className="flex items-center gap-1 text-muted">
|
||||||
{t('{{entity}} scope', {
|
{entityLabel
|
||||||
entity: EntityScopeLabelMapping[dispatchStrategy.entityScope],
|
? t('{{entity}} scope', {
|
||||||
})}
|
entity: entityLabel,
|
||||||
|
})
|
||||||
|
: t('Scope')}
|
||||||
</dt>
|
</dt>
|
||||||
<dd className="flex items-center gap-1" data-testid="scope">
|
<dd className="flex items-center gap-1" data-testid="scope">
|
||||||
<RewardEntityScope dispatchStrategy={dispatchStrategy} />
|
<RewardEntityScope dispatchStrategy={dispatchStrategy} />
|
||||||
@@ -574,7 +452,7 @@ const RewardRequirements = ({
|
|||||||
>
|
>
|
||||||
{addDecimalsFormatNumber(
|
{addDecimalsFormatNumber(
|
||||||
dispatchStrategy?.stakingRequirement || 0,
|
dispatchStrategy?.stakingRequirement || 0,
|
||||||
assetDecimalPlaces
|
vegaAsset?.decimals || 18
|
||||||
)}
|
)}
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
@@ -587,7 +465,7 @@ const RewardRequirements = ({
|
|||||||
{addDecimalsFormatNumber(
|
{addDecimalsFormatNumber(
|
||||||
dispatchStrategy?.notionalTimeWeightedAveragePositionRequirement ||
|
dispatchStrategy?.notionalTimeWeightedAveragePositionRequirement ||
|
||||||
0,
|
0,
|
||||||
assetDecimalPlaces
|
rewardAsset?.decimals || 0
|
||||||
)}
|
)}
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
@@ -645,44 +523,65 @@ const RewardEntityScope = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return t('Unspecified');
|
||||||
};
|
};
|
||||||
|
|
||||||
const getGradientClasses = (d: DispatchMetric | undefined) => {
|
const CardColourStyles: Record<
|
||||||
switch (d) {
|
CardColour,
|
||||||
case DispatchMetric.DISPATCH_METRIC_AVERAGE_POSITION:
|
{ gradientClassName: string; mainClassName: string }
|
||||||
return {
|
> = {
|
||||||
gradientClassName: 'from-vega-pink-500 to-vega-purple-400',
|
[CardColour.BLUE]: {
|
||||||
mainClassName: 'from-vega-pink-400 dark:from-vega-pink-600 to-20%',
|
gradientClassName: 'from-vega-blue-500 to-vega-green-400',
|
||||||
};
|
mainClassName: 'from-vega-blue-400 dark:from-vega-blue-600 to-20%',
|
||||||
case DispatchMetric.DISPATCH_METRIC_LP_FEES_RECEIVED:
|
},
|
||||||
return {
|
[CardColour.GREEN]: {
|
||||||
gradientClassName: 'from-vega-green-500 to-vega-yellow-500',
|
gradientClassName: 'from-vega-green-500 to-vega-yellow-500',
|
||||||
mainClassName: 'from-vega-green-400 dark:from-vega-green-600 to-20%',
|
mainClassName: 'from-vega-green-400 dark:from-vega-green-600 to-20%',
|
||||||
};
|
},
|
||||||
case DispatchMetric.DISPATCH_METRIC_MAKER_FEES_PAID:
|
[CardColour.GREY]: {
|
||||||
return {
|
gradientClassName: 'from-vega-cdark-500 to-vega-clight-200',
|
||||||
gradientClassName: 'from-vega-orange-500 to-vega-pink-400',
|
mainClassName: 'from-vega-cdark-400 dark:from-vega-cdark-600 to-20%',
|
||||||
mainClassName: 'from-vega-orange-400 dark:from-vega-orange-600 to-20%',
|
},
|
||||||
};
|
[CardColour.ORANGE]: {
|
||||||
case DispatchMetric.DISPATCH_METRIC_MARKET_VALUE:
|
gradientClassName: 'from-vega-orange-500 to-vega-pink-400',
|
||||||
case DispatchMetric.DISPATCH_METRIC_RELATIVE_RETURN:
|
mainClassName: 'from-vega-orange-400 dark:from-vega-orange-600 to-20%',
|
||||||
return {
|
},
|
||||||
gradientClassName: 'from-vega-purple-500 to-vega-blue-400',
|
[CardColour.PINK]: {
|
||||||
mainClassName: 'from-vega-purple-400 dark:from-vega-purple-600 to-20%',
|
gradientClassName: 'from-vega-pink-500 to-vega-purple-400',
|
||||||
};
|
mainClassName: 'from-vega-pink-400 dark:from-vega-pink-600 to-20%',
|
||||||
case DispatchMetric.DISPATCH_METRIC_RETURN_VOLATILITY:
|
},
|
||||||
return {
|
[CardColour.PURPLE]: {
|
||||||
gradientClassName: 'from-vega-blue-500 to-vega-green-400',
|
gradientClassName: 'from-vega-purple-500 to-vega-blue-400',
|
||||||
mainClassName: 'from-vega-blue-400 dark:from-vega-blue-600 to-20%',
|
mainClassName: 'from-vega-purple-400 dark:from-vega-purple-600 to-20%',
|
||||||
};
|
},
|
||||||
case DispatchMetric.DISPATCH_METRIC_VALIDATOR_RANKING:
|
[CardColour.WHITE]: {
|
||||||
default:
|
gradientClassName:
|
||||||
return {
|
'from-vega-clight-600 dark:from-vega-clight-900 to-vega-yellow-500 dark:to-vega-yellow-400',
|
||||||
gradientClassName: 'from-vega-pink-500 to-vega-purple-400',
|
mainClassName: 'from-white dark:from-vega-clight-100 to-20%',
|
||||||
mainClassName: 'from-vega-pink-400 dark:from-vega-pink-600 to-20%',
|
},
|
||||||
};
|
[CardColour.YELLOW]: {
|
||||||
}
|
gradientClassName: 'from-vega-yellow-500 to-vega-orange-400',
|
||||||
|
mainClassName: 'from-vega-yellow-400 dark:from-vega-yellow-600 to-20%',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const DispatchMetricColourMap: Record<DispatchMetric, CardColour> = {
|
||||||
|
// Liquidity provision fees received
|
||||||
|
[DispatchMetric.DISPATCH_METRIC_LP_FEES_RECEIVED]: CardColour.BLUE,
|
||||||
|
// Price maker fees paid
|
||||||
|
[DispatchMetric.DISPATCH_METRIC_MAKER_FEES_PAID]: CardColour.PINK,
|
||||||
|
// Price maker fees earned
|
||||||
|
[DispatchMetric.DISPATCH_METRIC_MAKER_FEES_RECEIVED]: CardColour.GREEN,
|
||||||
|
// Total market value
|
||||||
|
[DispatchMetric.DISPATCH_METRIC_MARKET_VALUE]: CardColour.WHITE,
|
||||||
|
// Average position
|
||||||
|
[DispatchMetric.DISPATCH_METRIC_AVERAGE_POSITION]: CardColour.ORANGE,
|
||||||
|
// Relative return
|
||||||
|
[DispatchMetric.DISPATCH_METRIC_RELATIVE_RETURN]: CardColour.PURPLE,
|
||||||
|
// Return volatility
|
||||||
|
[DispatchMetric.DISPATCH_METRIC_RETURN_VOLATILITY]: CardColour.YELLOW,
|
||||||
|
// Validator ranking
|
||||||
|
[DispatchMetric.DISPATCH_METRIC_VALIDATOR_RANKING]: CardColour.WHITE,
|
||||||
};
|
};
|
||||||
|
|
||||||
const CardIcon = ({
|
const CardIcon = ({
|
||||||
@@ -703,36 +602,29 @@ const CardIcon = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const EntityScopeIconMap: Record<EntityScope, VegaIconNames> = {
|
||||||
|
[EntityScope.ENTITY_SCOPE_TEAMS]: VegaIconNames.TEAM,
|
||||||
|
[EntityScope.ENTITY_SCOPE_INDIVIDUALS]: VegaIconNames.MAN,
|
||||||
|
};
|
||||||
|
|
||||||
const EntityIcon = ({
|
const EntityIcon = ({
|
||||||
transfer,
|
entityScope,
|
||||||
size = 18,
|
size = 18,
|
||||||
}: {
|
}: {
|
||||||
transfer: Transfer;
|
entityScope: EntityScope;
|
||||||
size?: VegaIconSize;
|
size?: VegaIconSize;
|
||||||
}) => {
|
}) => {
|
||||||
if (transfer.kind.__typename !== 'RecurringTransfer') {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const entityScope = transfer.kind.dispatchStrategy?.entityScope;
|
|
||||||
const getIconName = () => {
|
|
||||||
switch (entityScope) {
|
|
||||||
case EntityScope.ENTITY_SCOPE_TEAMS:
|
|
||||||
return VegaIconNames.TEAM;
|
|
||||||
case EntityScope.ENTITY_SCOPE_INDIVIDUALS:
|
|
||||||
return VegaIconNames.MAN;
|
|
||||||
default:
|
|
||||||
return VegaIconNames.QUESTION_MARK;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const iconName = getIconName();
|
|
||||||
return (
|
return (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
description={
|
description={
|
||||||
<span>{entityScope ? EntityScopeMapping[entityScope] : ''}</span>
|
entityScope ? <span>{EntityScopeMapping[entityScope]}</span> : undefined
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<span className="flex items-center p-2 rounded-full border border-gray-600">
|
<span className="flex items-center p-2 rounded-full border border-gray-600">
|
||||||
{iconName && <VegaIcon name={iconName} size={size} />}
|
<VegaIcon
|
||||||
|
name={EntityScopeIconMap[entityScope] || VegaIconNames.QUESTION_MARK}
|
||||||
|
size={size}
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import {
|
|||||||
type RewardsPageQuery,
|
type RewardsPageQuery,
|
||||||
useRewardsPageQuery,
|
useRewardsPageQuery,
|
||||||
useRewardsEpochQuery,
|
useRewardsEpochQuery,
|
||||||
} from './__generated__/Rewards';
|
} from '../../lib/hooks/__generated__/Rewards';
|
||||||
import {
|
import {
|
||||||
TradingButton,
|
TradingButton,
|
||||||
VegaIcon,
|
VegaIcon,
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
useRewardsHistoryQuery,
|
useRewardsHistoryQuery,
|
||||||
type RewardsHistoryQuery,
|
type RewardsHistoryQuery,
|
||||||
} from './__generated__/Rewards';
|
} from '../../lib/hooks/__generated__/Rewards';
|
||||||
import { useRewardsRowData } from './use-reward-row-data';
|
import { useRewardsRowData } from './use-reward-row-data';
|
||||||
import { useT } from '../../lib/use-t';
|
import { useT } from '../../lib/use-t';
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import BigNumber from 'bignumber.js';
|
|||||||
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
||||||
import { type Asset } from '@vegaprotocol/assets';
|
import { type Asset } from '@vegaprotocol/assets';
|
||||||
import { type PartyRewardsConnection } from './rewards-history';
|
import { type PartyRewardsConnection } from './rewards-history';
|
||||||
import { type RewardsHistoryQuery } from './__generated__/Rewards';
|
import { type RewardsHistoryQuery } from '../../lib/hooks/__generated__/Rewards';
|
||||||
|
|
||||||
const REWARD_ACCOUNT_TYPES = [
|
const REWARD_ACCOUNT_TYPES = [
|
||||||
AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
CONSOLE_IMAGE_NAME=vegaprotocol/trading:latest
|
CONSOLE_IMAGE_NAME=vegaprotocol/trading:latest
|
||||||
VEGA_VERSION=v0.74.1
|
VEGA_VERSION=v0.74.6
|
||||||
LOCAL_SERVER=false
|
LOCAL_SERVER=false
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
CONSOLE_IMAGE_NAME=vegaprotocol/trading:develop
|
CONSOLE_IMAGE_NAME=vegaprotocol/trading:develop
|
||||||
VEGA_VERSION=v0.74.1
|
VEGA_VERSION=v0.74.6
|
||||||
LOCAL_SERVER=false
|
LOCAL_SERVER=false
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user