diff --git a/apps/token/src/i18n/translations/dev.json b/apps/token/src/i18n/translations/dev.json
index 6f6463837..2d434dae2 100644
--- a/apps/token/src/i18n/translations/dev.json
+++ b/apps/token/src/i18n/translations/dev.json
@@ -545,5 +545,9 @@
"proposalTerms": "Proposal terms",
"currentlySetTo": "Currently set to ",
"pass": "Pass",
- "fail": "Fail"
+ "fail": "Fail",
+ "rankingScore": "Ranking score",
+ "stakeScore": "Stake score",
+ "performanceScore": "Performance score",
+ "votingPower": "Voting score"
}
diff --git a/apps/token/src/routes/staking/__generated__/Nodes.ts b/apps/token/src/routes/staking/__generated__/Nodes.ts
index e700cc6a0..7380837d4 100644
--- a/apps/token/src/routes/staking/__generated__/Nodes.ts
+++ b/apps/token/src/routes/staking/__generated__/Nodes.ts
@@ -25,6 +25,26 @@ export interface Nodes_nodes_epochData {
online: number;
}
+export interface Nodes_nodes_rankingScore {
+ __typename: "RankingScore";
+ /**
+ * The ranking score of the validator
+ */
+ rankingScore: string;
+ /**
+ * The stake based score of the validator (no anti-whaling)
+ */
+ stakeScore: string;
+ /**
+ * The performance score of the validator
+ */
+ performanceScore: string;
+ /**
+ * The tendermint voting power of the validator (uint32)
+ */
+ votingPower: string;
+}
+
export interface Nodes_nodes {
__typename: "Node";
/**
@@ -66,6 +86,10 @@ export interface Nodes_nodes {
pendingStakeFormatted: string;
epochData: Nodes_nodes_epochData | null;
status: NodeStatus;
+ /**
+ * Ranking scores and status for the validator for the current epoch
+ */
+ rankingScore: Nodes_nodes_rankingScore;
}
export interface Nodes_nodeData {
diff --git a/apps/token/src/routes/staking/__generated__/Staking.ts b/apps/token/src/routes/staking/__generated__/Staking.ts
index eaf20ace7..b4460ea44 100644
--- a/apps/token/src/routes/staking/__generated__/Staking.ts
+++ b/apps/token/src/routes/staking/__generated__/Staking.ts
@@ -100,6 +100,26 @@ export interface Staking_nodes_epochData {
online: number;
}
+export interface Staking_nodes_rankingScore {
+ __typename: "RankingScore";
+ /**
+ * The ranking score of the validator
+ */
+ rankingScore: string;
+ /**
+ * The stake based score of the validator (no anti-whaling)
+ */
+ stakeScore: string;
+ /**
+ * The performance score of the validator
+ */
+ performanceScore: string;
+ /**
+ * The tendermint voting power of the validator (uint32)
+ */
+ votingPower: string;
+}
+
export interface Staking_nodes {
__typename: "Node";
/**
@@ -145,6 +165,10 @@ export interface Staking_nodes {
pendingStakeFormatted: string;
epochData: Staking_nodes_epochData | null;
status: NodeStatus;
+ /**
+ * Ranking scores and status for the validator for the current epoch
+ */
+ rankingScore: Staking_nodes_rankingScore;
}
export interface Staking_nodeData {
diff --git a/apps/token/src/routes/staking/node-list.spec.tsx b/apps/token/src/routes/staking/node-list.spec.tsx
new file mode 100644
index 000000000..9b2849cc9
--- /dev/null
+++ b/apps/token/src/routes/staking/node-list.spec.tsx
@@ -0,0 +1,204 @@
+import { render, screen, waitFor, within } from '@testing-library/react';
+import { NodeList, NODES_QUERY } from './node-list';
+import { MockedProvider } from '@apollo/client/testing';
+import { MemoryRouter } from 'react-router-dom';
+import { addDecimal } from '@vegaprotocol/react-helpers';
+import type { Nodes_nodes } from './__generated__/Nodes';
+
+jest.mock('../../components/epoch-countdown', () => ({
+ EpochCountdown: () =>
,
+}));
+
+const nodeFactory = (overrides?: Partial) => ({
+ id: 'ccc022b7e63a4d0a6d3a193c3940c88574060e58a184964c994998d86835a1b4',
+ name: 'Skynet',
+ pubkey: '6abc23391a9f888ab240415bf63d6844b03fc360be822f4a1d2cd832d87b2917',
+ infoUrl: 'https://en.wikipedia.org/wiki/Skynet_(Terminator)',
+ location: '',
+ stakedByOperator: '3000000000000000000000',
+ stakedByDelegates: '11182454495731682635157',
+ stakedTotal: '14182454495731682635157',
+ pendingStake: '0',
+ stakedByOperatorFormatted: addDecimal(
+ overrides?.stakedByOperator || '3000000000000000000000',
+ 18
+ ),
+ stakedByDelegatesFormatted: addDecimal(
+ overrides?.stakedByDelegates || '11182454495731682635157',
+ 18
+ ),
+ stakedTotalFormatted: addDecimal(
+ overrides?.stakedTotal || '14182454495731682635157',
+ 18
+ ),
+ pendingStakeFormatted: addDecimal(overrides?.pendingStake || '0', 18),
+ epochData: null,
+ status: 'Validator',
+ rankingScore: {
+ rankingScore: '0.67845061012234727427532760837568',
+ stakeScore: '0.3392701644525644',
+ performanceScore: '0.9998677767864936',
+ votingPower: '2407',
+ __typename: 'RankingScore',
+ },
+ __typename: 'Node',
+ ...overrides,
+});
+
+const MOCK_NODES = {
+ nodes: [
+ nodeFactory(),
+ nodeFactory({
+ id: '966438c6bffac737cfb08173ffcb3f393c4692b099ad80cb45a82e2dc0a8cf99',
+ name: 'T-800 Terminator',
+ pubkey:
+ 'ccc3b8362c25b09d20df8ea407b0a476d6b24a0e72bc063d0033c8841652ddd4',
+ infoUrl: 'https://en.wikipedia.org/wiki/Terminator_(character)',
+ stakedByOperator: '3000000000000000000000',
+ stakedByDelegates: '6618711883996159534058',
+ stakedTotal: '9618711883996159534058',
+ rankingScore: {
+ rankingScore: '0.4601942440481428',
+ stakeScore: '0.2300971220240714',
+ performanceScore: '1',
+ votingPower: '2408',
+ __typename: 'RankingScore',
+ },
+ }),
+ nodeFactory({
+ id: '12c81b738e8051152e1afe44376ec37bca9216466e6d44cdd772194bad0ada81',
+ name: 'NCC-1701-E',
+ pubkey:
+ '0931a8fd8cc935458f470e435a05414387cea6f329d648be894fcd44bd517a2b',
+ infoUrl: 'https://en.wikipedia.org/wiki/USS_Enterprise_(NCC-1701-E)',
+ stakedByOperator: '3000000000000000000000',
+ stakedByDelegates: '1041343338923442976709',
+ stakedTotal: '4041343338923442976709',
+ pendingStake: '0',
+ rankingScore: {
+ rankingScore: '0.1932810100133910357676209647912',
+ stakeScore: '0.0966762995515676',
+ performanceScore: '0.999629748500531',
+ votingPower: '1163',
+ __typename: 'RankingScore',
+ },
+ }),
+ ],
+ nodeData: {
+ stakedTotal: '27842509718651285145924',
+ stakedTotalFormatted: addDecimal('27842509718651285145924', 18),
+ totalNodes: 3,
+ inactiveNodes: 0,
+ validatingNodes: 3,
+ uptime: 1560.266845703125,
+ __typename: 'NodeData',
+ },
+};
+
+const renderNodeList = () => {
+ return render(
+
+
+
+
+
+ );
+};
+
+beforeAll(() => {
+ jest.useFakeTimers();
+ jest.setSystemTime(0);
+});
+
+afterAll(() => {
+ jest.useRealTimers();
+});
+
+describe('Nodes list', () => {
+ it('should render epoch info', async () => {
+ renderNodeList();
+ await waitFor(() => {
+ expect(screen.getByText(MOCK_NODES.nodes[0].name)).toBeInTheDocument();
+ });
+ expect(screen.getByTestId('epoch-info')).toBeInTheDocument();
+ });
+
+ it('should a lit of all nodes', async () => {
+ renderNodeList();
+
+ await waitFor(() => {
+ expect(screen.getByText(MOCK_NODES.nodes[0].name)).toBeInTheDocument();
+ });
+ const items = screen.queryAllByTestId('node-list-item');
+ expect(items).toHaveLength(3);
+ for (const item of MOCK_NODES.nodes) {
+ expect(screen.getByText(item.name)).toBeInTheDocument();
+ }
+ });
+
+ it('should list the total stake and rewards of each node', async () => {
+ renderNodeList();
+
+ await waitFor(() => {
+ expect(screen.getByText(MOCK_NODES.nodes[0].name)).toBeInTheDocument();
+ });
+ const items = screen.queryAllByTestId('node-list-item');
+ const item = within(items[0]);
+ const rows = item.getAllByRole('row');
+
+ const expectedValues = [
+ ['Total stake', '14,182.45 (50.94%)'],
+ ['Ranking score', '0.6785'],
+ ['Stake score', '0.3393'],
+ ['Performance score', '0.9999'],
+ ['Voting score', '2,407.0000'],
+ ];
+
+ for (const [i, r] of rows.entries()) {
+ const row = within(r);
+ const cell = row.getByRole('cell');
+ const header = row.getByRole('rowheader');
+ expect(header).toHaveTextContent(expectedValues[i][0]);
+ expect(cell).toHaveTextContent(expectedValues[i][1]);
+ }
+ });
+});
diff --git a/apps/token/src/routes/staking/node-list.tsx b/apps/token/src/routes/staking/node-list.tsx
index 45efa7f55..321b3b553 100644
--- a/apps/token/src/routes/staking/node-list.tsx
+++ b/apps/token/src/routes/staking/node-list.tsx
@@ -8,7 +8,7 @@ import { EpochCountdown } from '../../components/epoch-countdown';
import { BigNumber } from '../../lib/bignumber';
import { formatNumber } from '../../lib/format-number';
import { truncateMiddle } from '../../lib/truncate-middle';
-import type { Nodes } from './__generated__/Nodes';
+import type { Nodes, Nodes_nodes_rankingScore } from './__generated__/Nodes';
import type { Staking_epoch, Staking_party } from './__generated__/Staking';
export const NODES_QUERY = gql`
@@ -33,6 +33,13 @@ export const NODES_QUERY = gql`
online
}
status
+ rankingScore {
+ rankingScore
+ stakeScore
+ performanceScore
+ votingPower
+ stakeScore
+ }
}
nodeData {
stakedTotal
@@ -54,7 +61,10 @@ const NodeListTr = ({ children }: { children: React.ReactNode }) => (
);
const NodeListTh = ({ children }: { children: React.ReactNode }) => (
-
+ |
{children}
|
);
@@ -113,6 +123,7 @@ export const NodeList = ({ epoch, party }: NodeListProps) => {
userStake,
userStakePercentage,
epoch,
+ scores: node.rankingScore,
};
});
@@ -160,6 +171,7 @@ export interface NodeListItemProps {
stakedTotalPercentage: string;
userStake: BigNumber;
userStakePercentage: string;
+ scores: Nodes_nodes_rankingScore;
}
export const NodeListItem = ({
@@ -169,6 +181,7 @@ export const NodeListItem = ({
stakedTotalPercentage,
userStake,
userStakePercentage,
+ scores,
}: NodeListItemProps) => {
const { t } = useTranslation();
@@ -186,24 +199,36 @@ export const NodeListItem = ({
{truncateMiddle(id)}
>
)}
-
+
{t('Total stake')}
- {formatNumber(stakedOnNode, 2)}
- {stakedTotalPercentage}
-
-
- {t('Your stake')}
- {formatNumber(userStake, 2)}
- {userStakePercentage}
+
+ {formatNumber(stakedOnNode, 2)} ({stakedTotalPercentage})
+
+ {scores
+ ? Object.entries(scores)
+ .filter(([key]) => key !== '__typename')
+ .map(([key, value]) => (
+
+ {t(key)}
+
+ {formatNumber(new BigNumber(value), 4)}
+
+
+ ))
+ : null}
diff --git a/apps/token/src/routes/staking/staking-nodes-container.tsx b/apps/token/src/routes/staking/staking-nodes-container.tsx
index a6e0cfbd0..4986c6cf8 100644
--- a/apps/token/src/routes/staking/staking-nodes-container.tsx
+++ b/apps/token/src/routes/staking/staking-nodes-container.tsx
@@ -53,6 +53,13 @@ export const STAKING_QUERY = gql`
online
}
status
+ rankingScore {
+ rankingScore
+ stakeScore
+ performanceScore
+ votingPower
+ stakeScore
+ }
}
nodeData {
stakedTotal
diff --git a/libs/wallet/src/order-hooks/__generated__/OrderEvent.ts b/libs/wallet/src/order-hooks/__generated__/OrderEvent.ts
index 8ce11083c..a2d305009 100644
--- a/libs/wallet/src/order-hooks/__generated__/OrderEvent.ts
+++ b/libs/wallet/src/order-hooks/__generated__/OrderEvent.ts
@@ -22,14 +22,14 @@ export interface OrderEvent_busEvents_event_Order_market {
/**
* decimalPlaces indicates the number of decimal places that an integer must be shifted by in order to get a correct
* number denominated in the currency of the Market. (uint64)
- *
+ *
* Examples:
* Currency Balance decimalPlaces Real Balance
* GBP 100 0 GBP 100
* GBP 100 2 GBP 1.00
* GBP 100 4 GBP 0.01
* GBP 1 4 GBP 0.0001 ( 0.01p )
- *
+ *
* GBX (pence) 100 0 GBP 1.00 (100p )
* GBX (pence) 100 2 GBP 0.01 ( 1p )
* GBX (pence) 100 4 GBP 0.0001 ( 0.01p )