Compare commits
110
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e5d8996b1a | ||
|
|
ba882d3c25 | ||
|
|
bddc1ac7dd | ||
|
|
38d13085fb | ||
|
|
c0f4278b81 | ||
|
|
b2777043b4 | ||
|
|
4d19b55096 | ||
|
|
7ea7362a7d | ||
|
|
bbfe42ddb1 | ||
|
|
b163af3e8a | ||
|
|
e6b3ff456d | ||
|
|
00dbb7dd60 | ||
|
|
82df401611 | ||
|
|
89e2033556 | ||
|
|
2d821700bd | ||
|
|
28b4593a1d | ||
|
|
358c734e31 | ||
|
|
3dd7496a9a | ||
|
|
80d576d484 | ||
|
|
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 | ||
|
|
1f08e8225a | ||
|
|
5fff6ba3f7 | ||
|
|
6cacc46a74 | ||
|
|
98ff4f3c04 | ||
|
|
8268acac6d | ||
|
|
4ad1a47ded | ||
|
|
fea97aac19 | ||
|
|
f652292e02 | ||
|
|
9195bf8c91 | ||
|
|
cd481512f3 | ||
|
|
8fe6f3f5f2 | ||
|
|
1c2389dee5 | ||
|
|
1e6a2debfc | ||
|
|
fc2773d748 | ||
|
|
b2860121e5 | ||
|
|
db5e5ee782 | ||
|
|
0d3bcf05a1 | ||
|
|
c7dd5e846a | ||
|
|
61e5941751 | ||
|
|
0d850bd8b9 | ||
|
|
8db286fa93 | ||
|
|
44189591fc | ||
|
|
3ed2ec88d7 | ||
|
|
a21feea699 | ||
|
|
41fd14dd00 | ||
|
|
c5a27dc6a2 | ||
|
|
0a3b1cadba | ||
|
|
b953de953a | ||
|
|
5ddcb613e2 | ||
|
|
76c07992d3 | ||
|
|
e5635cae61 | ||
|
|
46e2965fa2 | ||
|
|
b01c67ced5 | ||
|
|
636b1f98db | ||
|
|
c31a927526 | ||
|
|
5803d6e890 | ||
|
|
cb0dd17839 | ||
|
|
496b0b5a90 | ||
|
|
532ad3a4b9 | ||
|
|
3a8b40d7a5 | ||
|
|
be38813a33 | ||
|
|
bf70dc33ec | ||
|
|
d6084e75a0 | ||
|
|
75e7cea32a | ||
|
|
b4e98e285e | ||
|
|
f62e29c67f | ||
|
|
94e7ad489f | ||
|
|
657942d995 | ||
|
|
a529b9b031 | ||
|
|
0f311611ba | ||
|
|
02bd031bee | ||
|
|
bd97651dd3 | ||
|
|
516b3e5b93 | ||
|
|
42a98b6a35 | ||
|
|
2002731c52 | ||
|
|
a49139f127 | ||
|
|
e216b23472 | ||
|
|
e52ae97233 | ||
|
|
1780f6fa7f |
@@ -10,7 +10,7 @@ on:
|
||||
inputs:
|
||||
console-test-branch:
|
||||
type: choice
|
||||
description: 'main: v0.73.5, develop: v0.73.5'
|
||||
description: 'main: v0.73.13, develop: v0.74.0'
|
||||
options:
|
||||
- main
|
||||
- develop
|
||||
@@ -205,7 +205,7 @@ jobs:
|
||||
# run tests
|
||||
#----------------------------------------------
|
||||
- name: Run tests
|
||||
run: CONSOLE_IMAGE_NAME=ci/trading:local poetry run pytest -v --numprocesses 1 --dist loadfile --durations=45
|
||||
run: CONSOLE_IMAGE_NAME=ci/trading:local poetry run pytest -v --numprocesses 4 --dist loadfile --durations=45
|
||||
working-directory: apps/trading/e2e
|
||||
#----------------------------------------------
|
||||
# upload traces
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
name: Cypress Console tests -- live environment
|
||||
|
||||
# This workflow runs using provided url
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
url:
|
||||
description: 'Url'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
cypress-run:
|
||||
name: Run Cypress Trading tests -- live environment
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Use Node.js 20
|
||||
id: Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
|
||||
- name: Run Cypress tests
|
||||
uses: cypress-io/github-action@v4
|
||||
with:
|
||||
browser: chrome
|
||||
record: true
|
||||
project: ./apps/trading-e2e
|
||||
config: baseUrl=${{ github.event.inputs.url }}
|
||||
env: grepTags=@live
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -12,7 +12,6 @@ on:
|
||||
options:
|
||||
- explorer-e2e
|
||||
- governance-e2e
|
||||
- trading-e2e
|
||||
tags:
|
||||
description: 'Test tags to run'
|
||||
required: true
|
||||
|
||||
@@ -10,5 +10,5 @@ jobs:
|
||||
uses: ./.github/workflows/cypress-run.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
projects: '["explorer-e2e","governance-e2e","trading-e2e"]'
|
||||
projects: '["explorer-e2e","governance-e2e"]'
|
||||
tags: '@smoke @regression @slow'
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
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
|
||||
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
|
||||
|
||||
@@ -4,7 +4,7 @@ The front-end monorepo provides a toolkit for building apps that interact with V
|
||||
|
||||
This repository is managed using [Nx](https://nx.dev).
|
||||
|
||||
# 🔎 Applications in this repo
|
||||
## 🔎 Applications in this repo
|
||||
|
||||
### [Block explorer](./apps/explorer)
|
||||
|
||||
@@ -30,7 +30,7 @@ Hosting for static content being shared across apps, for example fonts.
|
||||
|
||||
The utility dApp for validators wishing to add or remove themselves as a signer of the multisig contract.
|
||||
|
||||
# 🧱 Libraries in this repo
|
||||
## 🧱 Libraries in this repo
|
||||
|
||||
### [UI toolkit](./libs/ui-toolkit)
|
||||
|
||||
@@ -53,7 +53,7 @@ A utility library for connecting to the Ethereum network and interacting with Ve
|
||||
|
||||
Generic react helpers that can be used across multiple applications, along with other utilities.
|
||||
|
||||
# 💻 Develop
|
||||
## 💻 Develop
|
||||
|
||||
### Set up
|
||||
|
||||
@@ -103,7 +103,7 @@ In CI linting, formatting and also run. These checks can be seen in the [CI work
|
||||
|
||||
Visit the [Nx Documentation](https://nx.dev/getting-started/intro) to learn more.
|
||||
|
||||
# 🐋 Hosting a console
|
||||
## 🐋 Hosting a console
|
||||
|
||||
To host a console there are two possible build scenarios for running the frontends: nx performed **outside** or **inside** docker build. For specific build instructions follow [build instructions](#build-instructions).
|
||||
|
||||
@@ -226,6 +226,6 @@ Note: The script is only needed if capsule was built for first time or fresh. To
|
||||
vega wallet service run -n DV --load-tokens --tokens-passphrase-file passphrase --no-version-check --automatic-consent --home ~/.vegacapsule/testnet/wallet
|
||||
```
|
||||
|
||||
# 📑 License
|
||||
## 📑 License
|
||||
|
||||
[MIT](./LICENSE)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { getNewAssetTxBody } from '../support/governance.functions';
|
||||
|
||||
context('Proposal page', { tags: '@smoke' }, function () {
|
||||
describe('Verify elements on page', function () {
|
||||
describe.skip('Verify elements on page', function () {
|
||||
const proposalHeading = 'proposals-heading';
|
||||
const dateTimeRegex =
|
||||
/(\d{1,2})\/(\d{1,2})\/(\d{4}), (\d{1,2}):(\d{1,2}):(\d{1,2})/gm;
|
||||
@@ -24,10 +24,6 @@ context('Proposal page', { tags: '@smoke' }, function () {
|
||||
cy.get_element_by_col_id('title').should('have.text', proposalTitle);
|
||||
cy.get_element_by_col_id('type').should('have.text', 'NewMarket');
|
||||
cy.get_element_by_col_id('state').should('have.text', 'Enacted');
|
||||
cy.getByTestId('vote-progress').should('be.visible');
|
||||
cy.getByTestId('vote-progress-bar-for')
|
||||
.invoke('attr', 'style')
|
||||
.should('eq', 'width: 100%;');
|
||||
cy.get('[col-id="cDate"]')
|
||||
.invoke('text')
|
||||
.should('match', dateTimeRegex);
|
||||
@@ -73,10 +69,6 @@ context('Proposal page', { tags: '@smoke' }, function () {
|
||||
'have.text',
|
||||
'Waiting for Node Vote'
|
||||
);
|
||||
cy.getByTestId('vote-progress').should('be.visible');
|
||||
cy.getByTestId('vote-progress-bar-against')
|
||||
.invoke('attr', 'style')
|
||||
.should('eq', 'width: 100%;');
|
||||
cy.get('[col-id="cDate"]')
|
||||
.invoke('text')
|
||||
.should('match', dateTimeRegex);
|
||||
|
||||
@@ -7,6 +7,7 @@ export type AssetBalanceProps = {
|
||||
price: string;
|
||||
showAssetLink?: boolean;
|
||||
showAssetSymbol?: boolean;
|
||||
rounded?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -18,12 +19,17 @@ const AssetBalance = ({
|
||||
price,
|
||||
showAssetLink = true,
|
||||
showAssetSymbol = false,
|
||||
rounded = false,
|
||||
}: AssetBalanceProps) => {
|
||||
const { data: asset, loading } = useAssetDataProvider(assetId);
|
||||
|
||||
const label =
|
||||
!loading && asset && asset.decimals
|
||||
? addDecimalsFixedFormatNumber(price, asset.decimals)
|
||||
? addDecimalsFixedFormatNumber(
|
||||
price,
|
||||
asset.decimals,
|
||||
rounded ? 0 : undefined
|
||||
)
|
||||
: price;
|
||||
|
||||
return (
|
||||
|
||||
@@ -41,6 +41,7 @@ export const Header = () => {
|
||||
Routes.ASSETS,
|
||||
Routes.MARKETS,
|
||||
Routes.GOVERNANCE,
|
||||
Routes.TREASURY,
|
||||
Routes.NETWORK_PARAMETERS,
|
||||
Routes.GENESIS,
|
||||
].map((n) => pages.find((r) => r.path === n))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export type HashProps = {
|
||||
export type HashProps = React.HTMLProps<HTMLSpanElement> & {
|
||||
text: string;
|
||||
truncate?: boolean;
|
||||
};
|
||||
|
||||
@@ -2,4 +2,5 @@ export { default as BlockLink } from './block-link/block-link';
|
||||
export { default as PartyLink } from './party-link/party-link';
|
||||
export { default as NodeLink } from './node-link/node-link';
|
||||
export { default as MarketLink } from './market-link/market-link';
|
||||
export { default as NetworkParameterLink } from './network-parameter-link/network-parameter-link';
|
||||
export * from './asset-link/asset-link';
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
import React from 'react';
|
||||
import { Routes } from '../../../routes/route-names';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import type { ComponentProps } from 'react';
|
||||
import Hash from '../hash';
|
||||
|
||||
export type NetworkParameterLinkProps = Partial<ComponentProps<typeof Link>> & {
|
||||
parameter: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Links a given network parameter to the relevant page and anchor on the page
|
||||
*/
|
||||
const NetworkParameterLink = ({
|
||||
parameter,
|
||||
...props
|
||||
}: NetworkParameterLinkProps) => {
|
||||
return (
|
||||
<Link
|
||||
className="underline"
|
||||
{...props}
|
||||
to={`/${Routes.NETWORK_PARAMETERS}#${parameter}`}
|
||||
>
|
||||
<Hash text={parameter} />
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
export default NetworkParameterLink;
|
||||
@@ -32,9 +32,17 @@ export function getNameForParty(id: string, data?: ExplorerNodeNamesQuery) {
|
||||
export type PartyLinkProps = Partial<ComponentProps<typeof Link>> & {
|
||||
id: string;
|
||||
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 name = useMemo(() => getNameForParty(id, data), [data, 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) {
|
||||
return (
|
||||
<span className="font-mono" data-testid="network">
|
||||
{t('Network')}
|
||||
{networkLabel}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
@@ -70,7 +78,11 @@ const PartyLink = ({ id, truncate = false, ...props }: PartyLinkProps) => {
|
||||
{useName ? (
|
||||
name
|
||||
) : (
|
||||
<Hash text={truncate ? truncateMiddle(id, 4, 4) : id} />
|
||||
<Hash
|
||||
text={
|
||||
truncate ? truncateMiddle(id, truncateLength, truncateLength) : id
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</Link>
|
||||
</span>
|
||||
|
||||
@@ -26,7 +26,7 @@ const ProposalLink = ({ id, text }: ProposalLinkProps) => {
|
||||
>;
|
||||
|
||||
const base = ENV.dataSources.governanceUrl;
|
||||
const label = proposal?.rationale.title || id;
|
||||
const label = proposal?.rationale?.title || id;
|
||||
|
||||
return (
|
||||
<ExternalLink href={`${base}/proposals/${id}`}>
|
||||
|
||||
@@ -175,6 +175,7 @@ describe('Amend order details', () => {
|
||||
|
||||
const res = renderExistingAmend('123', 1, amend);
|
||||
expect(await res.findByText('New size')).toBeInTheDocument();
|
||||
expect(await res.findByText('Size ±')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Renders Reference if provided', async () => {
|
||||
|
||||
@@ -82,7 +82,7 @@ const AmendOrderDetails = ({ id, version, amend }: AmendOrderDetailsProps) => {
|
||||
{amend.sizeDelta && amend.sizeDelta !== '0' ? (
|
||||
<div className="mb-12 md:mb-0">
|
||||
<h2 className="text-dark mb-4 text-2xl font-bold">
|
||||
{t('New size')}
|
||||
{t('Size ±')}
|
||||
</h2>
|
||||
<h5
|
||||
className={`mb-0 text-lg font-medium capitalize text-gray-500 ${getSideDeltaColour(
|
||||
@@ -93,6 +93,16 @@ const AmendOrderDetails = ({ id, version, amend }: AmendOrderDetailsProps) => {
|
||||
</h5>
|
||||
</div>
|
||||
) : 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' ? (
|
||||
<div className="">
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { type ProposalListFieldsFragment } from '@vegaprotocol/proposals';
|
||||
import { VoteProgress } from '@vegaprotocol/proposals';
|
||||
import { type AgGridReact } from 'ag-grid-react';
|
||||
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
import { AgGrid } from '@vegaprotocol/datagrid';
|
||||
@@ -12,12 +11,7 @@ import { type ColDef } from 'ag-grid-community';
|
||||
import type { RowClickedEvent } from 'ag-grid-community';
|
||||
import { getDateTimeFormat } from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import {
|
||||
NetworkParams,
|
||||
useNetworkParams,
|
||||
} from '@vegaprotocol/network-parameters';
|
||||
import { ProposalStateMapping } from '@vegaprotocol/types';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { DApp, TOKEN_PROPOSAL, useLinks } from '@vegaprotocol/environment';
|
||||
import { BREAKPOINT_MD } from '../../config/breakpoints';
|
||||
import { JsonViewerDialog } from '../dialogs/json-viewer-dialog';
|
||||
@@ -31,15 +25,7 @@ type ProposalsTableProps = {
|
||||
data: ProposalListFieldsFragment[] | null;
|
||||
};
|
||||
export const ProposalsTable = ({ data }: ProposalsTableProps) => {
|
||||
const { params } = useNetworkParams([
|
||||
NetworkParams.governance_proposal_market_requiredMajority,
|
||||
]);
|
||||
const tokenLink = useLinks(DApp.Governance);
|
||||
const requiredMajorityPercentage = useMemo(() => {
|
||||
const requiredMajority =
|
||||
params?.governance_proposal_market_requiredMajority ?? 1;
|
||||
return new BigNumber(requiredMajority).times(100);
|
||||
}, [params?.governance_proposal_market_requiredMajority]);
|
||||
|
||||
const gridRef = useRef<AgGridReact>(null);
|
||||
useLayoutEffect(() => {
|
||||
@@ -90,33 +76,6 @@ export const ProposalsTable = ({ data }: ProposalsTableProps) => {
|
||||
return value ? ProposalStateMapping[value] : '-';
|
||||
},
|
||||
},
|
||||
{
|
||||
colId: 'voting',
|
||||
maxWidth: 100,
|
||||
hide: window.innerWidth <= BREAKPOINT_MD,
|
||||
headerName: t('Voting'),
|
||||
cellRenderer: ({
|
||||
data,
|
||||
}: VegaICellRendererParams<ProposalListFieldsFragment>) => {
|
||||
if (data) {
|
||||
const yesTokens = new BigNumber(data.votes.yes.totalTokens);
|
||||
const noTokens = new BigNumber(data.votes.no.totalTokens);
|
||||
const totalTokensVoted = yesTokens.plus(noTokens);
|
||||
const yesPercentage = totalTokensVoted.isZero()
|
||||
? new BigNumber(0)
|
||||
: yesTokens.multipliedBy(100).dividedBy(totalTokensVoted);
|
||||
return (
|
||||
<div className="flex h-full items-center justify-center pt-2 uppercase">
|
||||
<VoteProgress
|
||||
threshold={requiredMajorityPercentage}
|
||||
progress={yesPercentage}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return '-';
|
||||
},
|
||||
},
|
||||
{
|
||||
colId: 'cDate',
|
||||
maxWidth: 150,
|
||||
@@ -184,7 +143,7 @@ export const ProposalsTable = ({ data }: ProposalsTableProps) => {
|
||||
},
|
||||
},
|
||||
],
|
||||
[requiredMajorityPercentage, tokenLink]
|
||||
[tokenLink]
|
||||
);
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -5,5 +5,10 @@ query ExplorerProposalStatus($id: ID!) {
|
||||
state
|
||||
rejectionReason
|
||||
}
|
||||
... on BatchProposal {
|
||||
id
|
||||
state
|
||||
rejectionReason
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-1
@@ -8,7 +8,7 @@ export type ExplorerProposalStatusQueryVariables = Types.Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type ExplorerProposalStatusQuery = { __typename?: 'Query', proposal?: { __typename?: 'BatchProposal' } | { __typename?: 'Proposal', id?: string | null, state: Types.ProposalState, rejectionReason?: Types.ProposalRejectionReason | null } | null };
|
||||
export type ExplorerProposalStatusQuery = { __typename?: 'Query', proposal?: { __typename?: 'BatchProposal', id?: string | null, state: Types.ProposalState, rejectionReason?: Types.ProposalRejectionReason | null } | { __typename?: 'Proposal', id?: string | null, state: Types.ProposalState, rejectionReason?: Types.ProposalRejectionReason | null } | null };
|
||||
|
||||
|
||||
export const ExplorerProposalStatusDocument = gql`
|
||||
@@ -19,6 +19,11 @@ export const ExplorerProposalStatusDocument = gql`
|
||||
state
|
||||
rejectionReason
|
||||
}
|
||||
... on BatchProposal {
|
||||
id
|
||||
state
|
||||
rejectionReason
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -0,0 +1,257 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { BatchItem } from './batch-item';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import type { components } from '../../../../../types/explorer';
|
||||
type Item = components['schemas']['vegaBatchProposalTermsChange'];
|
||||
|
||||
describe('BatchItem', () => {
|
||||
it('Renders "Unknown proposal type" by default', () => {
|
||||
const item = {};
|
||||
render(<BatchItem item={item} />);
|
||||
expect(screen.getByText('Unknown proposal type')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Renders "Unknown proposal type" for unknown items', () => {
|
||||
const item = {
|
||||
newLochNessMonster: {
|
||||
location: 'Loch Ness',
|
||||
},
|
||||
} as unknown as Item;
|
||||
render(<BatchItem item={item} />);
|
||||
expect(screen.getByText('Unknown proposal type')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Renders "New spot market"', () => {
|
||||
const item = {
|
||||
newSpotMarket: {},
|
||||
};
|
||||
render(<BatchItem item={item} />);
|
||||
expect(screen.getByText('New spot market')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Renders "Cancel transfer"', () => {
|
||||
const item = {
|
||||
cancelTransfer: {
|
||||
changes: {
|
||||
transferId: 'transfer123',
|
||||
},
|
||||
},
|
||||
};
|
||||
render(<BatchItem item={item} />);
|
||||
expect(screen.getByText('Cancel transfer')).toBeInTheDocument();
|
||||
expect(screen.getByText('transf')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Renders "Cancel transfer" without an id', () => {
|
||||
const item = {
|
||||
cancelTransfer: {
|
||||
changes: {},
|
||||
},
|
||||
};
|
||||
render(<BatchItem item={item} />);
|
||||
expect(screen.getByText('Cancel transfer')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Renders "New freeform"', () => {
|
||||
const item = {
|
||||
newFreeform: {},
|
||||
};
|
||||
render(<BatchItem item={item} />);
|
||||
expect(screen.getByText('New freeform proposal')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Renders "New market"', () => {
|
||||
const item = {
|
||||
newMarket: {},
|
||||
};
|
||||
render(<BatchItem item={item} />);
|
||||
expect(screen.getByText('New market')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Renders "New transfer"', () => {
|
||||
const item = {
|
||||
newTransfer: {},
|
||||
};
|
||||
render(<BatchItem item={item} />);
|
||||
expect(screen.getByText('New transfer')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Renders "Update asset" with assetId', () => {
|
||||
const item = {
|
||||
updateAsset: {
|
||||
assetId: 'asset123',
|
||||
},
|
||||
};
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<MockedProvider>
|
||||
<BatchItem item={item} />
|
||||
</MockedProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
expect(screen.getByText('Update asset')).toBeInTheDocument();
|
||||
expect(screen.getByText('asset123')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Renders "Update asset" even if assetId is not set', () => {
|
||||
const item = {
|
||||
updateAsset: {
|
||||
assetId: undefined,
|
||||
},
|
||||
};
|
||||
render(<BatchItem item={item} />);
|
||||
expect(screen.getByText('Update asset')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Renders "Update market state" with marketId', () => {
|
||||
const item = {
|
||||
updateMarketState: {
|
||||
changes: {
|
||||
marketId: 'market123',
|
||||
},
|
||||
},
|
||||
};
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<MockedProvider>
|
||||
<BatchItem item={item} />
|
||||
</MockedProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
expect(screen.getByText('Update market state')).toBeInTheDocument();
|
||||
expect(screen.getByText('market123')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Renders "Update market state" even if marketId is not set', () => {
|
||||
const item = {
|
||||
updateMarketState: {
|
||||
changes: {
|
||||
marketId: undefined,
|
||||
},
|
||||
},
|
||||
};
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<MockedProvider>
|
||||
<BatchItem item={item} />
|
||||
</MockedProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
expect(screen.getByText('Update market state')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Renders "Update network parameter" with parameter', () => {
|
||||
const item = {
|
||||
updateNetworkParameter: {
|
||||
changes: {
|
||||
key: 'parameter123',
|
||||
},
|
||||
},
|
||||
};
|
||||
render(
|
||||
<MockedProvider>
|
||||
<MemoryRouter>
|
||||
<BatchItem item={item} />
|
||||
</MemoryRouter>
|
||||
</MockedProvider>
|
||||
);
|
||||
expect(screen.getByText('Update network parameter')).toBeInTheDocument();
|
||||
expect(screen.getByText('parameter123')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Renders "Update network parameter" even if parameter is not set', () => {
|
||||
const item = {
|
||||
updateNetworkParameter: {
|
||||
changes: {
|
||||
key: undefined,
|
||||
},
|
||||
},
|
||||
};
|
||||
render(<BatchItem item={item} />);
|
||||
expect(screen.getByText('Update network parameter')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Renders "Update referral program"', () => {
|
||||
const item = {
|
||||
updateReferralProgram: {},
|
||||
};
|
||||
render(<BatchItem item={item} />);
|
||||
expect(screen.getByText('Update referral program')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Renders "Update spot market" with marketId', () => {
|
||||
const item = {
|
||||
updateSpotMarket: {
|
||||
marketId: 'market123',
|
||||
},
|
||||
};
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<MockedProvider>
|
||||
<BatchItem item={item} />
|
||||
</MockedProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
expect(screen.getByText('Update spot market')).toBeInTheDocument();
|
||||
expect(screen.getByText('market123')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Renders "Update spot market" even if marketId is not set', () => {
|
||||
const item = {
|
||||
updateSpotMarket: {
|
||||
marketId: undefined,
|
||||
},
|
||||
};
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<MockedProvider>
|
||||
<BatchItem item={item} />
|
||||
</MockedProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
expect(screen.getByText('Update spot market')).toBeInTheDocument();
|
||||
});
|
||||
it('Renders "Update market" with marketId', () => {
|
||||
const item = {
|
||||
updateMarket: {
|
||||
marketId: 'market123',
|
||||
},
|
||||
};
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<MockedProvider>
|
||||
<BatchItem item={item} />
|
||||
</MockedProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
expect(screen.getByText('Update market')).toBeInTheDocument();
|
||||
expect(screen.getByText('market123')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Renders "Update market" even if marketId is not set', () => {
|
||||
const item = {
|
||||
updateMarket: {
|
||||
marketId: undefined,
|
||||
},
|
||||
};
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<MockedProvider>
|
||||
<BatchItem item={item} />
|
||||
</MockedProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
expect(screen.getByText('Update market')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Renders "Update volume discount program"', () => {
|
||||
const item = {
|
||||
updateVolumeDiscountProgram: {},
|
||||
};
|
||||
render(<BatchItem item={item} />);
|
||||
expect(
|
||||
screen.getByText('Update volume discount program')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,87 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { AssetLink, MarketLink, NetworkParameterLink } from '../../../links';
|
||||
import type { components } from '../../../../../types/explorer';
|
||||
import Hash from '../../../links/hash';
|
||||
|
||||
type Item = components['schemas']['vegaBatchProposalTermsChange'];
|
||||
|
||||
export interface BatchItemProps {
|
||||
item: Item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Produces a one line summary for an item in a batch proposal. Could
|
||||
* easily be adapted to summarise individual proposals, but there is no
|
||||
* place for that yet.
|
||||
*
|
||||
* Details (like IDs) should be shown and linked if available, but handled
|
||||
* if not available. This is adequate as the ProposalSummary component contains
|
||||
* a JSON viewer for the full proposal.
|
||||
*/
|
||||
export const BatchItem = ({ item }: BatchItemProps) => {
|
||||
if (item.cancelTransfer) {
|
||||
const transferId = item?.cancelTransfer?.changes?.transferId || false;
|
||||
return (
|
||||
<span>
|
||||
{t('Cancel transfer')}
|
||||
{transferId && (
|
||||
<Hash className="ml-1" truncate={true} text={transferId} />
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
} else if (item.newFreeform) {
|
||||
return <span>{t('New freeform proposal')}</span>;
|
||||
} else if (item.newMarket) {
|
||||
return <span>{t('New market')}</span>;
|
||||
} else if (item.newSpotMarket) {
|
||||
return <span>{t('New spot market')}</span>;
|
||||
} else if (item.newTransfer) {
|
||||
return <span>{t('New transfer')}</span>;
|
||||
} else if (item.updateAsset) {
|
||||
const assetId = item?.updateAsset?.assetId || false;
|
||||
return (
|
||||
<span>
|
||||
{t('Update asset')}
|
||||
{assetId && <AssetLink className="ml-1" assetId={assetId} />}
|
||||
</span>
|
||||
);
|
||||
} else if (item.updateMarket) {
|
||||
const marketId = item?.updateMarket?.marketId || false;
|
||||
return (
|
||||
<span>
|
||||
{t('Update market')}{' '}
|
||||
{marketId && <MarketLink className="ml-1" id={marketId} />}
|
||||
</span>
|
||||
);
|
||||
} else if (item.updateMarketState) {
|
||||
const marketId = item?.updateMarketState?.changes?.marketId || false;
|
||||
return (
|
||||
<span>
|
||||
{t('Update market state')}
|
||||
{marketId && <MarketLink className="ml-1" id={marketId} />}
|
||||
</span>
|
||||
);
|
||||
} else if (item.updateNetworkParameter) {
|
||||
const param = item?.updateNetworkParameter?.changes?.key || false;
|
||||
return (
|
||||
<span>
|
||||
{t('Update network parameter')}
|
||||
{param && <NetworkParameterLink className="ml-1" parameter={param} />}
|
||||
</span>
|
||||
);
|
||||
} else if (item.updateReferralProgram) {
|
||||
return <span>{t('Update referral program')}</span>;
|
||||
} else if (item.updateSpotMarket) {
|
||||
const marketId = item?.updateSpotMarket?.marketId || '';
|
||||
return (
|
||||
<span>
|
||||
{t('Update spot market')}
|
||||
<MarketLink className="ml-1" id={marketId} />
|
||||
</span>
|
||||
);
|
||||
} else if (item.updateVolumeDiscountProgram) {
|
||||
return <span>{t('Update volume discount program')}</span>;
|
||||
}
|
||||
|
||||
return <span>{t('Unknown proposal type')}</span>;
|
||||
};
|
||||
@@ -1,6 +1,4 @@
|
||||
import type { ProposalTerms } from '../tx-proposal';
|
||||
import { useState } from 'react';
|
||||
import type { components } from '../../../../../types/explorer';
|
||||
import { JsonViewerDialog } from '../../../dialogs/json-viewer-dialog';
|
||||
import ProposalLink from '../../../links/proposal-link/proposal-link';
|
||||
import truncate from 'lodash/truncate';
|
||||
@@ -9,7 +7,12 @@ import ReactMarkdown from 'react-markdown';
|
||||
import { ProposalDate } from './proposal-date';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
|
||||
import type { ProposalTerms } from '../tx-proposal';
|
||||
import type { components } from '../../../../../types/explorer';
|
||||
import { BatchItem } from './batch-item';
|
||||
|
||||
type Rationale = components['schemas']['vegaProposalRationale'];
|
||||
type Batch = components['schemas']['v1BatchProposalSubmissionTerms']['changes'];
|
||||
|
||||
type ProposalTermsDialog = {
|
||||
open: boolean;
|
||||
@@ -21,6 +24,7 @@ interface ProposalSummaryProps {
|
||||
id: string;
|
||||
rationale?: Rationale;
|
||||
terms?: ProposalTerms;
|
||||
batch?: Batch;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -31,6 +35,7 @@ export const ProposalSummary = ({
|
||||
id,
|
||||
rationale,
|
||||
terms,
|
||||
batch,
|
||||
}: ProposalSummaryProps) => {
|
||||
const [dialog, setDialog] = useState<ProposalTermsDialog>({
|
||||
open: false,
|
||||
@@ -59,7 +64,9 @@ export const ProposalSummary = ({
|
||||
return (
|
||||
<div className="w-auto max-w-lg border-2 border-solid border-vega-light-100 dark:border-vega-dark-200 p-5">
|
||||
{id && <ProposalStatusIcon id={id} />}
|
||||
{rationale?.title && <h1 className="text-xl pb-1">{rationale.title}</h1>}
|
||||
{rationale?.title && (
|
||||
<h1 className="text-xl pb-1 break-all">{rationale.title}</h1>
|
||||
)}
|
||||
{rationale?.description && (
|
||||
<div className="pt-2 text-sm leading-tight">
|
||||
<ReactMarkdown
|
||||
@@ -72,6 +79,18 @@ export const ProposalSummary = ({
|
||||
</ReactMarkdown>
|
||||
</div>
|
||||
)}
|
||||
{batch && (
|
||||
<section className="pt-2 text-sm leading-tight my-3">
|
||||
<h2 className="text-lg pb-1">{t('Changes')}</h2>
|
||||
<ol>
|
||||
{batch.map((change, index) => (
|
||||
<li className="ml-4 list-decimal" key={`batch-${index}`}>
|
||||
<BatchItem item={change} />
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
</section>
|
||||
)}
|
||||
<div className="pt-5">
|
||||
<button className="underline max-md:hidden mr-5" onClick={openDialog}>
|
||||
{t('View terms')}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { TxDetailsShared } from '../shared/tx-details-shared';
|
||||
import { TableWithTbody } from '../../../table';
|
||||
import type { components } from '../../../../../types/explorer';
|
||||
|
||||
import type { BlockExplorerTransactionResult } from '../../../../routes/types/block-explorer-response';
|
||||
import type { TendermintBlocksResponse } from '../../../../routes/blocks/tendermint-blocks-response';
|
||||
import { TableCell, TableRow } from '../../../table';
|
||||
|
||||
type Update = components['schemas']['v1UpdatePartyProfile'];
|
||||
|
||||
interface TxDetailsUpdatePartyProfileProps {
|
||||
txData: BlockExplorerTransactionResult | undefined;
|
||||
pubKey: string | undefined;
|
||||
blockData: TendermintBlocksResponse | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Party profiles can be an alias and arbitrary key/values pairs.
|
||||
* This component displays the alias, if any, but not the metadata. When there is
|
||||
* some wider usage, we can decide how to render it. For now, it's available in the
|
||||
* full TX details.
|
||||
*/
|
||||
export const TxDetailsUpdatePartyProfile = ({
|
||||
txData,
|
||||
pubKey,
|
||||
blockData,
|
||||
}: TxDetailsUpdatePartyProfileProps) => {
|
||||
if (!txData?.command.updatePartyProfile) {
|
||||
return <>{t('Awaiting Block Explorer transaction details')}</>;
|
||||
}
|
||||
|
||||
const update: Update = txData.command.updatePartyProfile;
|
||||
|
||||
return (
|
||||
<TableWithTbody className="mb-8" allowWrap={true}>
|
||||
<TxDetailsShared txData={txData} pubKey={pubKey} blockData={blockData} />
|
||||
{update.alias && (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('New alias')}</TableCell>
|
||||
<TableCell>{update.alias}</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableWithTbody>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
query ExplorerTransferStatus($id: ID!) {
|
||||
transfer(id: $id) {
|
||||
transfer {
|
||||
reference
|
||||
timestamp
|
||||
status
|
||||
reason
|
||||
fromAccountType
|
||||
from
|
||||
to
|
||||
toAccountType
|
||||
asset {
|
||||
id
|
||||
}
|
||||
amount
|
||||
}
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
import * as Types from '@vegaprotocol/types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type ExplorerTransferStatusQueryVariables = Types.Exact<{
|
||||
id: Types.Scalars['ID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type ExplorerTransferStatusQuery = { __typename?: 'Query', transfer?: { __typename?: 'TransferNode', transfer: { __typename?: 'Transfer', reference?: string | null, timestamp: any, status: Types.TransferStatus, reason?: string | null, fromAccountType: Types.AccountType, from: string, to: string, toAccountType: Types.AccountType, amount: string, asset?: { __typename?: 'Asset', id: string } | null } } | null };
|
||||
|
||||
|
||||
export const ExplorerTransferStatusDocument = gql`
|
||||
query ExplorerTransferStatus($id: ID!) {
|
||||
transfer(id: $id) {
|
||||
transfer {
|
||||
reference
|
||||
timestamp
|
||||
status
|
||||
reason
|
||||
fromAccountType
|
||||
from
|
||||
to
|
||||
toAccountType
|
||||
asset {
|
||||
id
|
||||
}
|
||||
amount
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useExplorerTransferStatusQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useExplorerTransferStatusQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useExplorerTransferStatusQuery` 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 } = useExplorerTransferStatusQuery({
|
||||
* variables: {
|
||||
* id: // value for 'id'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useExplorerTransferStatusQuery(baseOptions: Apollo.QueryHookOptions<ExplorerTransferStatusQuery, ExplorerTransferStatusQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<ExplorerTransferStatusQuery, ExplorerTransferStatusQueryVariables>(ExplorerTransferStatusDocument, options);
|
||||
}
|
||||
export function useExplorerTransferStatusLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ExplorerTransferStatusQuery, ExplorerTransferStatusQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<ExplorerTransferStatusQuery, ExplorerTransferStatusQueryVariables>(ExplorerTransferStatusDocument, options);
|
||||
}
|
||||
export type ExplorerTransferStatusQueryHookResult = ReturnType<typeof useExplorerTransferStatusQuery>;
|
||||
export type ExplorerTransferStatusLazyQueryHookResult = ReturnType<typeof useExplorerTransferStatusLazyQuery>;
|
||||
export type ExplorerTransferStatusQueryResult = Apollo.QueryResult<ExplorerTransferStatusQuery, ExplorerTransferStatusQueryVariables>;
|
||||
+2
-2
@@ -111,7 +111,7 @@ export function TransferParticipants({
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 9"
|
||||
className="fill-vega-light-100 dark:fill-black"
|
||||
className="fill-white dark:fill-black"
|
||||
>
|
||||
<path d="M0,0L8,9l8,-9Z" />
|
||||
</svg>
|
||||
@@ -120,7 +120,7 @@ export function TransferParticipants({
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 9"
|
||||
className="fill-vega-light-100 dark:fill-vega-dark-200"
|
||||
className="fill-vega-light-200 dark:fill-vega-dark-200"
|
||||
>
|
||||
<path d="M0,0L8,9l8,-9Z" />
|
||||
</svg>
|
||||
|
||||
+178
-52
@@ -1,97 +1,223 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { AssetLink, MarketLink } from '../../../../links';
|
||||
import { headerClasses, wrapperClasses } from '../transfer-details';
|
||||
import type { components } from '../../../../../../types/explorer';
|
||||
import type { Recurring } from '../transfer-details';
|
||||
import { DispatchMetricLabels } from '@vegaprotocol/types';
|
||||
|
||||
import {
|
||||
DispatchMetricLabels,
|
||||
DistributionStrategy,
|
||||
} from '@vegaprotocol/types';
|
||||
import { VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
|
||||
import { formatNumber } from '@vegaprotocol/utils';
|
||||
export type Metric = components['schemas']['vegaDispatchMetric'];
|
||||
export type Strategy = components['schemas']['vegaDispatchStrategy'];
|
||||
|
||||
export const wrapperClasses = 'border pv-2 w-full flex-auto basis-full';
|
||||
export const headerClasses =
|
||||
'bg-solid bg-vega-light-150 dark:bg-vega-dark-150 text-center text-xl py-2 font-alpha calt';
|
||||
|
||||
const metricLabels: Record<Metric, string> = {
|
||||
DISPATCH_METRIC_UNSPECIFIED: 'Unknown metric',
|
||||
...DispatchMetricLabels,
|
||||
};
|
||||
|
||||
// Maps the two (non-null) values of entityScope to the icon that represents it
|
||||
const entityScopeIcons: Record<
|
||||
string,
|
||||
typeof VegaIconNames[keyof typeof VegaIconNames]
|
||||
> = {
|
||||
ENTITY_SCOPE_INDIVIDUALS: VegaIconNames.MAN,
|
||||
ENTITY_SCOPE_TEAMS: VegaIconNames.TEAM,
|
||||
};
|
||||
|
||||
const distributionStrategyLabel: Record<DistributionStrategy, string> = {
|
||||
[DistributionStrategy.DISTRIBUTION_STRATEGY_PRO_RATA]: 'Pro Rata',
|
||||
[DistributionStrategy.DISTRIBUTION_STRATEGY_RANK]: 'Ranked',
|
||||
};
|
||||
|
||||
interface TransferRewardsProps {
|
||||
recurring: Recurring;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renderer for a transfer. These can vary quite
|
||||
* widely, essentially every field can be null.
|
||||
* Renders recurring transfers/game details in a way that is, perhaps, easy to understand
|
||||
*
|
||||
* @param transfer A recurring transfer object
|
||||
*/
|
||||
export function TransferRewards({ recurring }: TransferRewardsProps) {
|
||||
const metric =
|
||||
recurring?.dispatchStrategy?.metric || 'DISPATCH_METRIC_UNSPECIFIED';
|
||||
|
||||
if (!recurring || !recurring.dispatchStrategy) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Destructure to make things a bit more readable
|
||||
const {
|
||||
entityScope,
|
||||
individualScope,
|
||||
teamScope,
|
||||
distributionStrategy,
|
||||
lockPeriod,
|
||||
markets,
|
||||
stakingRequirement,
|
||||
windowLength,
|
||||
notionalTimeWeightedAveragePositionRequirement,
|
||||
rankTable,
|
||||
nTopPerformers,
|
||||
} = recurring.dispatchStrategy;
|
||||
|
||||
return (
|
||||
<div className={wrapperClasses}>
|
||||
<h2 className={headerClasses}>{t('Reward metrics')}</h2>
|
||||
<ul className="relative block rounded-lg py-6 text-center p-6">
|
||||
{recurring.dispatchStrategy.assetForMetric ? (
|
||||
<h2 className={headerClasses}>{getRewardTitle(entityScope)}</h2>
|
||||
<ul className="relative block rounded-lg py-6 text-left p-6">
|
||||
{entityScope && entityScopeIcons[entityScope] ? (
|
||||
<li>
|
||||
<strong>{t('Asset')}</strong>:{' '}
|
||||
<AssetLink assetId={recurring.dispatchStrategy.assetForMetric} />
|
||||
<strong>{t('Scope')}</strong>:{' '}
|
||||
<VegaIcon name={entityScopeIcons[entityScope]} />
|
||||
|
||||
{individualScope ? individualScopeLabels[individualScope] : null}
|
||||
{getScopeLabel(entityScope, teamScope)}
|
||||
</li>
|
||||
) : null}
|
||||
<li>
|
||||
<strong>{t('Metric')}</strong>: {metricLabels[metric]}
|
||||
</li>
|
||||
{recurring.dispatchStrategy.markets &&
|
||||
recurring.dispatchStrategy.markets.length > 0 ? (
|
||||
{recurring.dispatchStrategy &&
|
||||
recurring.dispatchStrategy.assetForMetric && (
|
||||
<li>
|
||||
<strong>{t('Asset for metric')}</strong>:{' '}
|
||||
<AssetLink assetId={recurring.dispatchStrategy.assetForMetric} />
|
||||
</li>
|
||||
)}
|
||||
{recurring.dispatchStrategy.metric &&
|
||||
metricLabels[recurring.dispatchStrategy.metric] && (
|
||||
<li>
|
||||
<strong>{t('Metric')}</strong>:{' '}
|
||||
{metricLabels[recurring.dispatchStrategy.metric]}
|
||||
</li>
|
||||
)}
|
||||
{lockPeriod && (
|
||||
<li>
|
||||
<strong>{t('Reward lock')}</strong>:
|
||||
{recurring.dispatchStrategy.lockPeriod}{' '}
|
||||
{recurring.dispatchStrategy.lockPeriod === '1'
|
||||
? t('epoch')
|
||||
: t('epochs')}
|
||||
</li>
|
||||
)}
|
||||
|
||||
{markets && markets.length > 0 ? (
|
||||
<li>
|
||||
<strong>{t('Markets in scope')}</strong>:
|
||||
<ul>
|
||||
{recurring.dispatchStrategy.markets.map((m) => (
|
||||
<li key={m}>
|
||||
<ul className="inline-block ml-1">
|
||||
{markets.map((m) => (
|
||||
<li key={m} className="inline-block mr-2">
|
||||
<MarketLink id={m} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</li>
|
||||
) : null}
|
||||
<li>
|
||||
<strong>{t('Factor')}</strong>: {recurring.factor}
|
||||
</li>
|
||||
|
||||
{stakingRequirement && stakingRequirement !== '0' ? (
|
||||
<li>
|
||||
<strong>{t('Staking requirement')}</strong>: {stakingRequirement}
|
||||
</li>
|
||||
) : null}
|
||||
|
||||
{windowLength && windowLength !== '0' ? (
|
||||
<li>
|
||||
<strong>{t('Window length')}</strong>:{' '}
|
||||
{recurring.dispatchStrategy.windowLength}{' '}
|
||||
{recurring.dispatchStrategy.windowLength === '1'
|
||||
? t('epoch')
|
||||
: t('epochs')}
|
||||
</li>
|
||||
) : null}
|
||||
|
||||
{notionalTimeWeightedAveragePositionRequirement &&
|
||||
notionalTimeWeightedAveragePositionRequirement !== '' ? (
|
||||
<li>
|
||||
<strong>{t('Notional TWAP')}</strong>:{' '}
|
||||
{notionalTimeWeightedAveragePositionRequirement}
|
||||
</li>
|
||||
) : null}
|
||||
|
||||
{nTopPerformers && (
|
||||
<li>
|
||||
<strong>{t('Elligible team members:')}</strong> top{' '}
|
||||
{`${formatNumber(Number(nTopPerformers) * 100, 0)}%`}
|
||||
</li>
|
||||
)}
|
||||
|
||||
{distributionStrategy &&
|
||||
distributionStrategy !== 'DISTRIBUTION_STRATEGY_UNSPECIFIED' && (
|
||||
<li>
|
||||
<strong>{t('Distribution strategy')}</strong>:{' '}
|
||||
{distributionStrategyLabel[distributionStrategy]}
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
<div className="px-6 pt-1 pb-5">
|
||||
{rankTable && rankTable.length > 0 ? (
|
||||
<table className="border-collapse border border-gray-400 ">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="border border-gray-300 bg-gray-300 px-3">
|
||||
<strong>{t('Start rank')}</strong>
|
||||
</th>
|
||||
<th className="border border-gray-300 bg-gray-300 px-3">
|
||||
<strong>{t('Share of reward pool')}</strong>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{rankTable.map((row, i) => {
|
||||
return (
|
||||
<tr key={`rank-${i}`}>
|
||||
<td className="border border-slate-300 text-center">
|
||||
{row.startRank}
|
||||
</td>
|
||||
<td className="border border-slate-300 text-center">
|
||||
{row.shareRatio}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface TransferRecurringStrategyProps {
|
||||
strategy: Strategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple renderer for a dispatch strategy in a recurring transfer
|
||||
*
|
||||
* @param strategy Dispatch strategy object
|
||||
*/
|
||||
export function TransferRecurringStrategy({
|
||||
strategy,
|
||||
}: TransferRecurringStrategyProps) {
|
||||
if (!strategy) {
|
||||
return null;
|
||||
export function getScopeLabel(
|
||||
scope: components['schemas']['vegaEntityScope'] | undefined,
|
||||
teamScope: readonly string[] | undefined
|
||||
): string {
|
||||
if (scope === 'ENTITY_SCOPE_TEAMS') {
|
||||
if (teamScope && teamScope.length !== 0) {
|
||||
return ` ${teamScope.length} teams`;
|
||||
} else {
|
||||
return t('All teams');
|
||||
}
|
||||
} else if (scope === 'ENTITY_SCOPE_INDIVIDUALS') {
|
||||
return t('Individuals');
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{strategy.assetForMetric ? (
|
||||
<li>
|
||||
<strong>{t('Asset for metric')}</strong>:{' '}
|
||||
<AssetLink assetId={strategy.assetForMetric} />
|
||||
</li>
|
||||
) : null}
|
||||
<li>
|
||||
<strong>{t('Metric')}</strong>: {strategy.metric}
|
||||
</li>
|
||||
</>
|
||||
);
|
||||
}
|
||||
export function getRewardTitle(
|
||||
scope?: components['schemas']['vegaEntityScope']
|
||||
) {
|
||||
if (scope === 'ENTITY_SCOPE_TEAMS') {
|
||||
return t('Game');
|
||||
}
|
||||
return t('Reward metrics');
|
||||
}
|
||||
|
||||
const individualScopeLabels: Record<
|
||||
components['schemas']['vegaIndividualScope'],
|
||||
string
|
||||
> = {
|
||||
// Unspecified and All are not rendered
|
||||
INDIVIDUAL_SCOPE_UNSPECIFIED: '',
|
||||
INDIVIDUAL_SCOPE_ALL: '',
|
||||
INDIVIDUAL_SCOPE_IN_TEAM: '(in team)',
|
||||
INDIVIDUAL_SCOPE_NOT_IN_TEAM: '(not in team)',
|
||||
};
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { headerClasses, wrapperClasses } from '../transfer-details';
|
||||
import { Icon, Loader } from '@vegaprotocol/ui-toolkit';
|
||||
import type { IconName } from '@vegaprotocol/ui-toolkit';
|
||||
import type { ApolloError } from '@apollo/client';
|
||||
import { TransferStatus, TransferStatusMapping } from '@vegaprotocol/types';
|
||||
import { IconNames } from '@blueprintjs/icons';
|
||||
|
||||
interface TransferStatusProps {
|
||||
status: TransferStatus | undefined;
|
||||
error: ApolloError | undefined;
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renderer for a transfer. These can vary quite
|
||||
* widely, essentially every field can be null.
|
||||
*
|
||||
* @param transfer A recurring transfer object
|
||||
*/
|
||||
export function TransferStatusView({ status, loading }: TransferStatusProps) {
|
||||
if (!status) {
|
||||
status = TransferStatus.STATUS_PENDING;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={wrapperClasses}>
|
||||
<h2 className={headerClasses}>{t('Status')}</h2>
|
||||
<div className="relative block rounded-lg py-6 text-center p-6">
|
||||
{loading ? (
|
||||
<div className="leading-10 mt-12">
|
||||
<Loader size={'small'} />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<p className="leading-10 my-2">
|
||||
<TransferStatusIcon status={status} />
|
||||
</p>
|
||||
<p className="leading-10 my-2">{TransferStatusMapping[status]}</p>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
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
|
||||
* @param status TransferStatus
|
||||
* @returns IconName
|
||||
*/
|
||||
export function getIconForStatus(status: TransferStatus): IconName {
|
||||
switch (status) {
|
||||
case TransferStatus.STATUS_PENDING:
|
||||
return IconNames.TIME;
|
||||
case TransferStatus.STATUS_DONE:
|
||||
return IconNames.TICK;
|
||||
case TransferStatus.STATUS_REJECTED:
|
||||
return IconNames.CROSS;
|
||||
case TransferStatus.STATUS_CANCELLED:
|
||||
return IconNames.CROSS;
|
||||
default:
|
||||
return IconNames.TIME;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple mapping from status to colour
|
||||
* @param status TransferStatus
|
||||
* @returns string Tailwind classname
|
||||
*/
|
||||
export function getColourForStatus(status: TransferStatus): string {
|
||||
switch (status) {
|
||||
case TransferStatus.STATUS_PENDING:
|
||||
return 'text-yellow-500';
|
||||
case TransferStatus.STATUS_DONE:
|
||||
return 'text-green-500';
|
||||
case TransferStatus.STATUS_REJECTED:
|
||||
return 'text-red-500';
|
||||
case TransferStatus.STATUS_CANCELLED:
|
||||
return 'text-red-600';
|
||||
default:
|
||||
return 'text-yellow-500';
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,15 @@ import type { components } from '../../../../../types/explorer';
|
||||
import { TransferRepeat } from './blocks/transfer-repeat';
|
||||
import { TransferRewards } from './blocks/transfer-rewards';
|
||||
import { TransferParticipants } from './blocks/transfer-participants';
|
||||
import { useExplorerTransferStatusQuery } from './__generated__/Transfer';
|
||||
import { TransferStatusView } from './blocks/transfer-status';
|
||||
import { TransferStatus } from '@vegaprotocol/types';
|
||||
|
||||
export type Recurring = components['schemas']['commandsv1RecurringTransfer'];
|
||||
export type Metric = components['schemas']['vegaDispatchMetric'];
|
||||
|
||||
export const wrapperClasses =
|
||||
'border border-vega-light-150 dark:border-vega-dark-200 rounded-md pv-2 mb-5 w-full sm:w-1/4 min-w-[200px] ';
|
||||
'border border-vega-light-150 dark:border-vega-dark-200 pv-2 w-full sm:w-1/3 basis-1/3';
|
||||
export const headerClasses =
|
||||
'bg-solid bg-vega-light-150 dark:bg-vega-dark-150 border-vega-light-150 text-center text-xl py-2 font-alpha calt';
|
||||
|
||||
@@ -16,6 +19,7 @@ export type Transfer = components['schemas']['commandsv1Transfer'];
|
||||
interface TransferDetailsProps {
|
||||
transfer: Transfer;
|
||||
from: string;
|
||||
id: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -24,13 +28,24 @@ interface TransferDetailsProps {
|
||||
*
|
||||
* @param transfer A recurring transfer object
|
||||
*/
|
||||
export function TransferDetails({ transfer, from }: TransferDetailsProps) {
|
||||
export function TransferDetails({ transfer, from, id }: TransferDetailsProps) {
|
||||
const recurring = transfer.recurring;
|
||||
|
||||
// Currently all this is passed in to TransferStatus, but the extra details
|
||||
// may be useful in the future.
|
||||
const { data, error, loading } = useExplorerTransferStatusQuery({
|
||||
variables: { id },
|
||||
});
|
||||
|
||||
const status = error
|
||||
? TransferStatus.STATUS_REJECTED
|
||||
: data?.transfer?.transfer.status;
|
||||
|
||||
return (
|
||||
<div className="flex gap-5 flex-wrap">
|
||||
<div className="flex flex-wrap">
|
||||
<TransferParticipants from={from} transfer={transfer} />
|
||||
{recurring ? <TransferRepeat recurring={transfer.recurring} /> : null}
|
||||
<TransferStatusView status={status} error={error} loading={loading} />
|
||||
{recurring && recurring.dispatchStrategy ? (
|
||||
<TransferRewards recurring={transfer.recurring} />
|
||||
) : null}
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
import {
|
||||
getScopeLabel,
|
||||
getRewardTitle,
|
||||
TransferRewards,
|
||||
} from './blocks/transfer-rewards';
|
||||
import { render } from '@testing-library/react';
|
||||
import type { components } from '../../../../../types/explorer';
|
||||
import type { Recurring } from './transfer-details';
|
||||
import {
|
||||
DispatchMetric,
|
||||
DistributionStrategy,
|
||||
EntityScope,
|
||||
IndividualScope,
|
||||
} from '@vegaprotocol/types';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
|
||||
describe('getScopeLabel', () => {
|
||||
it('should return the correct label for ENTITY_SCOPE_TEAMS with teamScope', () => {
|
||||
const scope = 'ENTITY_SCOPE_TEAMS';
|
||||
const teamScope = ['team1', 'team2', 'team3'];
|
||||
const expectedLabel = ' 3 teams';
|
||||
|
||||
const result = getScopeLabel(scope, teamScope);
|
||||
|
||||
expect(result).toEqual(expectedLabel);
|
||||
});
|
||||
|
||||
it('should return the correct label for ENTITY_SCOPE_TEAMS without teamScope', () => {
|
||||
const scope = 'ENTITY_SCOPE_TEAMS';
|
||||
const teamScope = undefined;
|
||||
const expectedLabel = 'All teams';
|
||||
|
||||
const result = getScopeLabel(scope, teamScope);
|
||||
|
||||
expect(result).toEqual(expectedLabel);
|
||||
});
|
||||
|
||||
it('should return the correct label for ENTITY_SCOPE_INDIVIDUALS', () => {
|
||||
const scope = 'ENTITY_SCOPE_INDIVIDUALS';
|
||||
const teamScope = undefined;
|
||||
const expectedLabel = 'Individuals';
|
||||
|
||||
const result = getScopeLabel(scope, teamScope);
|
||||
|
||||
expect(result).toEqual(expectedLabel);
|
||||
});
|
||||
|
||||
it('should return an empty string for unknown scope', () => {
|
||||
const scope = 'UNKNOWN_SCOPE';
|
||||
const teamScope = undefined;
|
||||
const expectedLabel = '';
|
||||
|
||||
const result = getScopeLabel(
|
||||
scope as unknown as components['schemas']['vegaEntityScope'],
|
||||
teamScope
|
||||
);
|
||||
|
||||
expect(result).toEqual(expectedLabel);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getRewardTitle', () => {
|
||||
it('should return the correct title for ENTITY_SCOPE_TEAMS', () => {
|
||||
const scope = 'ENTITY_SCOPE_TEAMS';
|
||||
const expectedTitle = 'Game';
|
||||
|
||||
const result = getRewardTitle(scope);
|
||||
|
||||
expect(result).toEqual(expectedTitle);
|
||||
});
|
||||
|
||||
it('should return the correct title for other scopes', () => {
|
||||
const scope = 'ENTITY_SCOPE_INDIVIDUALS';
|
||||
const expectedTitle = 'Reward metrics';
|
||||
|
||||
const result = getRewardTitle(scope);
|
||||
|
||||
expect(result).toEqual(expectedTitle);
|
||||
});
|
||||
});
|
||||
|
||||
describe('TransferRewards', () => {
|
||||
it('should render nothing if recurring dispatchStrategy is not provided', () => {
|
||||
const { container } = render(
|
||||
<TransferRewards recurring={null as unknown as Recurring} />
|
||||
);
|
||||
expect(container.firstChild).toBeNull();
|
||||
});
|
||||
|
||||
it('should render nothing if recurring.dispatchStrategy is not provided', () => {
|
||||
const { container } = render(
|
||||
<TransferRewards recurring={{} as unknown as Recurring} />
|
||||
);
|
||||
expect(container.firstChild).toBeNull();
|
||||
});
|
||||
|
||||
it('should render the reward details correctly', () => {
|
||||
const recurring = {
|
||||
dispatchStrategy: {
|
||||
metric: DispatchMetric.DISPATCH_METRIC_AVERAGE_POSITION,
|
||||
assetForMetric: '123',
|
||||
entityScope: EntityScope.ENTITY_SCOPE_TEAMS,
|
||||
individualScope: IndividualScope.INDIVIDUAL_SCOPE_IN_TEAM,
|
||||
teamScope: [],
|
||||
distributionStrategy:
|
||||
DistributionStrategy.DISTRIBUTION_STRATEGY_PRO_RATA,
|
||||
lockPeriod: 'lockPeriod',
|
||||
markets: ['market1', 'market2'],
|
||||
stakingRequirement: '1',
|
||||
windowLength: 'windowLength',
|
||||
notionalTimeWeightedAveragePositionRequirement:
|
||||
'notionalTimeWeightedAveragePositionRequirement',
|
||||
rankTable: [
|
||||
{ startRank: 1, shareRatio: 0.2 },
|
||||
{ startRank: 2, shareRatio: 0.3 },
|
||||
],
|
||||
nTopPerformers: 'nTopPerformers',
|
||||
},
|
||||
};
|
||||
|
||||
const { getByText } = render(
|
||||
<MemoryRouter>
|
||||
<MockedProvider>
|
||||
<TransferRewards recurring={recurring} />
|
||||
</MockedProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
expect(getByText('Game')).toBeInTheDocument();
|
||||
expect(getByText('Scope')).toBeInTheDocument();
|
||||
expect(getByText('Asset for metric')).toBeInTheDocument();
|
||||
expect(getByText('Metric')).toBeInTheDocument();
|
||||
expect(getByText('Reward lock')).toBeInTheDocument();
|
||||
expect(getByText('Markets in scope')).toBeInTheDocument();
|
||||
expect(getByText('Staking requirement')).toBeInTheDocument();
|
||||
expect(getByText('Window length')).toBeInTheDocument();
|
||||
expect(getByText('Notional TWAP')).toBeInTheDocument();
|
||||
expect(getByText('Elligible team members:')).toBeInTheDocument();
|
||||
expect(getByText('Distribution strategy')).toBeInTheDocument();
|
||||
expect(getByText('Start rank')).toBeInTheDocument();
|
||||
expect(getByText('Share of reward pool')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should not render a rank table if recurring.dispatchStrategy.rankTable is not provided', () => {
|
||||
const recurring = {
|
||||
dispatchStrategy: {
|
||||
entityScope: EntityScope.ENTITY_SCOPE_INDIVIDUALS,
|
||||
individualScope: IndividualScope.INDIVIDUAL_SCOPE_ALL,
|
||||
teamScope: ['team1', 'team2', 'team3'],
|
||||
distributionStrategy:
|
||||
DistributionStrategy.DISTRIBUTION_STRATEGY_PRO_RATA,
|
||||
lockPeriod: 'lockPeriod',
|
||||
markets: ['market1', 'market2'],
|
||||
stakingRequirement: 'stakingRequirement',
|
||||
windowLength: 'windowLength',
|
||||
notionalTimeWeightedAveragePositionRequirement:
|
||||
'notionalTimeWeightedAveragePositionRequirement',
|
||||
nTopPerformers: 'nTopPerformers',
|
||||
},
|
||||
};
|
||||
|
||||
const { container } = render(
|
||||
<MemoryRouter>
|
||||
<MockedProvider>
|
||||
<TransferRewards recurring={recurring} />
|
||||
</MockedProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
expect(container.querySelector('table')).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,75 @@
|
||||
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
|
||||
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
|
||||
import { sharedHeaderProps, TxDetailsShared } from './shared/tx-details-shared';
|
||||
import { TableCell, TableRow, TableWithTbody } from '../../table';
|
||||
import type { components } from '../../../../types/explorer';
|
||||
import { txSignatureToDeterministicId } from '../lib/deterministic-ids';
|
||||
import { ProposalSummary } from './proposal/summary';
|
||||
import Hash from '../../links/hash';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
|
||||
export type Proposal = components['schemas']['v1BatchProposalSubmission'];
|
||||
export type ProposalTerms = components['schemas']['vegaProposalTerms'];
|
||||
|
||||
interface TxBatchProposalProps {
|
||||
txData: BlockExplorerTransactionResult | undefined;
|
||||
pubKey: string | undefined;
|
||||
blockData: TendermintBlocksResponse | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export const TxBatchProposal = ({
|
||||
txData,
|
||||
pubKey,
|
||||
blockData,
|
||||
}: TxBatchProposalProps) => {
|
||||
if (!txData || !txData.command.batchProposalSubmission) {
|
||||
return <>{t('Awaiting Block Explorer transaction details')}</>;
|
||||
}
|
||||
let deterministicId = '';
|
||||
|
||||
const proposal: Proposal = txData.command.batchProposalSubmission;
|
||||
const sig = txData?.signature?.value;
|
||||
if (sig) {
|
||||
deterministicId = txSignatureToDeterministicId(sig);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<TableWithTbody className="mb-8" allowWrap={true}>
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell {...sharedHeaderProps}>{t('Type')}</TableCell>
|
||||
<TableCell>{t('Batch proposal')}</TableCell>
|
||||
</TableRow>
|
||||
<TxDetailsShared
|
||||
txData={txData}
|
||||
pubKey={pubKey}
|
||||
blockData={blockData}
|
||||
hideTypeRow={true}
|
||||
/>
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Batch size')}</TableCell>
|
||||
<TableCell>
|
||||
{proposal.terms?.changes?.length || t('No changes')}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Proposal ID')}</TableCell>
|
||||
<TableCell>
|
||||
<Hash text={deterministicId} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableWithTbody>
|
||||
{proposal && (
|
||||
<ProposalSummary
|
||||
id={deterministicId}
|
||||
rationale={proposal?.rationale}
|
||||
terms={proposal.terms}
|
||||
batch={proposal.terms?.changes}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -32,6 +32,9 @@ import { TxDetailsCreateReferralSet } from './tx-create-referral-set';
|
||||
import { TxDetailsApplyReferralCode } from './tx-apply-referral-code';
|
||||
import { TxDetailsUpdateReferralSet } from './tx-update-referral-set';
|
||||
import { TxDetailsJoinTeam } from './tx-join-team';
|
||||
import { TxDetailsUpdateMarginMode } from './tx-update-margin-mode';
|
||||
import { TxBatchProposal } from './tx-batch-proposal';
|
||||
import { TxDetailsUpdatePartyProfile } from './proposal/tx-update-party-profile';
|
||||
|
||||
interface TxDetailsWrapperProps {
|
||||
txData: BlockExplorerTransactionResult | undefined;
|
||||
@@ -133,6 +136,12 @@ function getTransactionComponent(txData?: BlockExplorerTransactionResult) {
|
||||
return TxDetailsApplyReferralCode;
|
||||
case 'Join Team':
|
||||
return TxDetailsJoinTeam;
|
||||
case 'Update Margin Mode':
|
||||
return TxDetailsUpdateMarginMode;
|
||||
case 'Batch Proposal':
|
||||
return TxBatchProposal;
|
||||
case 'Update Party Profile':
|
||||
return TxDetailsUpdatePartyProfile;
|
||||
default:
|
||||
return TxDetailsGeneric;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ import { ProposalSignatureBundleNewAsset } from './proposal/signature-bundle-new
|
||||
import { ProposalSignatureBundleUpdateAsset } from './proposal/signature-bundle-update';
|
||||
import { MarketLink } from '../../links';
|
||||
import { formatNumber } from '@vegaprotocol/utils';
|
||||
import { TransferDetails } from './transfer/transfer-details';
|
||||
import { proposalToTransfer } from '../lib/proposal-to-transfer';
|
||||
|
||||
export type Proposal = components['schemas']['v1ProposalSubmission'];
|
||||
export type ProposalTerms = components['schemas']['vegaProposalTerms'];
|
||||
@@ -104,6 +106,12 @@ export const TxProposal = ({ txData, pubKey, blockData }: TxProposalProps) => {
|
||||
? ProposalSignatureBundleNewAsset
|
||||
: ProposalSignatureBundleUpdateAsset;
|
||||
|
||||
let transfer, from;
|
||||
if (proposal.terms?.newTransfer?.changes) {
|
||||
transfer = proposalToTransfer(proposal.terms?.newTransfer.changes);
|
||||
from = proposal.terms.newTransfer.changes.source;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<TableWithTbody className="mb-8" allowWrap={true}>
|
||||
@@ -149,14 +157,26 @@ export const TxProposal = ({ txData, pubKey, blockData }: TxProposalProps) => {
|
||||
</>
|
||||
) : null}
|
||||
</TableWithTbody>
|
||||
|
||||
<ProposalSummary
|
||||
id={deterministicId}
|
||||
rationale={proposal.rationale}
|
||||
terms={proposal?.terms}
|
||||
/>
|
||||
|
||||
{proposalRequiresSignatureBundle(proposal) && (
|
||||
<SignatureBundleComponent id={deterministicId} tx={tx} />
|
||||
)}
|
||||
|
||||
{transfer && (
|
||||
<div className="mt-8">
|
||||
<TransferDetails
|
||||
transfer={transfer}
|
||||
from={from || ''}
|
||||
id={deterministicId}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
SPECIAL_CASE_NETWORK_ID,
|
||||
} from '../../links/party-link/party-link';
|
||||
import { txSignatureToDeterministicId } from '../lib/deterministic-ids';
|
||||
import Hash from '../../links/hash';
|
||||
|
||||
type Transfer = components['schemas']['commandsv1Transfer'];
|
||||
|
||||
@@ -60,7 +61,7 @@ export const TxDetailsTransfer = ({
|
||||
}
|
||||
|
||||
const from = txData.submitter;
|
||||
|
||||
const id = txSignatureToDeterministicId(txData.signature.value);
|
||||
return (
|
||||
<>
|
||||
<TableWithTbody className="mb-8" allowWrap={true}>
|
||||
@@ -71,7 +72,7 @@ export const TxDetailsTransfer = ({
|
||||
<TableRow modifier="bordered" data-testid="id">
|
||||
<TableCell {...sharedHeaderProps}>{t('Transfer ID')}</TableCell>
|
||||
<TableCell>
|
||||
{txSignatureToDeterministicId(txData.signature.value)}
|
||||
<Hash text={id} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TxDetailsShared
|
||||
@@ -105,7 +106,7 @@ export const TxDetailsTransfer = ({
|
||||
</TableRow>
|
||||
) : null}
|
||||
</TableWithTbody>
|
||||
<TransferDetails from={from} transfer={transfer} />
|
||||
<TransferDetails from={from} transfer={transfer} id={id} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
|
||||
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
|
||||
import { TxDetailsShared } from './shared/tx-details-shared';
|
||||
import { TableCell, TableRow, TableWithTbody } from '../../table';
|
||||
import type { components } from '../../../../types/explorer';
|
||||
import { MarketLink } from '../../links';
|
||||
|
||||
interface TxDetailsUpdateMarginModeProps {
|
||||
txData: BlockExplorerTransactionResult | undefined;
|
||||
pubKey: string | undefined;
|
||||
blockData: TendermintBlocksResponse | undefined;
|
||||
}
|
||||
|
||||
type Mode = components['schemas']['UpdateMarginModeMode'];
|
||||
|
||||
const MarginModeLabels: Record<Mode, string> = {
|
||||
MODE_CROSS_MARGIN: t('Cross margin'),
|
||||
MODE_ISOLATED_MARGIN: t('Isolated margin'),
|
||||
MODE_UNSPECIFIED: t('Unspecified'),
|
||||
};
|
||||
|
||||
export const TxDetailsUpdateMarginMode = ({
|
||||
txData,
|
||||
pubKey,
|
||||
blockData,
|
||||
}: TxDetailsUpdateMarginModeProps) => {
|
||||
if (!txData || !txData.command.updateMarginMode) {
|
||||
return <>{t('Awaiting Block Explorer transaction details')}</>;
|
||||
}
|
||||
|
||||
const u: components['schemas']['v1UpdateMarginMode'] =
|
||||
txData.command.updateMarginMode;
|
||||
|
||||
return (
|
||||
<TableWithTbody className="mb-8" allowWrap={true}>
|
||||
<TxDetailsShared txData={txData} pubKey={pubKey} blockData={blockData} />
|
||||
{u.marketId && (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Market ID')}</TableCell>
|
||||
<TableCell>
|
||||
<MarketLink id={u.marketId} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
{u.mode && (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('New margin mode')}</TableCell>
|
||||
<TableCell>{MarginModeLabels[u.mode]}</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
{u.marginFactor && (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Margin factor')}</TableCell>
|
||||
<TableCell>{u.marginFactor}</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableWithTbody>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
import type { components } from '../../../../types/explorer';
|
||||
|
||||
type TransferProposal = components['schemas']['vegaNewTransferConfiguration'];
|
||||
type ActualTransfer = components['schemas']['commandsv1Transfer'];
|
||||
|
||||
/**
|
||||
* Converts a governance proposal for a transfer in to a transfer command that the
|
||||
* TransferDetails component can then render. The types are very similar, but do not
|
||||
* map precisely to each other due to some missing fields and some different field
|
||||
* names.
|
||||
*
|
||||
* @param proposal Governance proposal for a transfer
|
||||
* @returns transfer a Transfer object as if it had been submitted
|
||||
*/
|
||||
export function proposalToTransfer(proposal: TransferProposal): ActualTransfer {
|
||||
return {
|
||||
amount: proposal.amount,
|
||||
asset: proposal.asset,
|
||||
// On a transfer, 'from' is determined by the submitter, so there is no 'from' field
|
||||
// fromAccountType does exist and is just named differently on the proposal
|
||||
fromAccountType: proposal.sourceType,
|
||||
oneOff: proposal.oneOff,
|
||||
recurring: proposal.recurring,
|
||||
// There is no reference applied on governance initiated transfers
|
||||
reference: '',
|
||||
to: proposal.destination,
|
||||
toAccountType: proposal.destinationType,
|
||||
};
|
||||
}
|
||||
@@ -20,6 +20,7 @@ export type FilterOption =
|
||||
| 'Amend Order'
|
||||
| 'Apply Referral Code'
|
||||
| 'Batch Market Instructions'
|
||||
| 'Batch Proposal'
|
||||
| 'Cancel LiquidityProvision Order'
|
||||
| 'Cancel Order'
|
||||
| 'Cancel Transfer Funds'
|
||||
@@ -43,7 +44,9 @@ export type FilterOption =
|
||||
| 'Submit Order'
|
||||
| 'Transfer Funds'
|
||||
| 'Undelegate'
|
||||
| 'Update Party Profile'
|
||||
| 'Update Referral Set'
|
||||
| 'Update Margin Mode'
|
||||
| 'Validator Heartbeat'
|
||||
| 'Vote on Proposal'
|
||||
| 'Withdraw';
|
||||
@@ -59,17 +62,25 @@ export const filterOptions: Record<string, FilterOption[]> = {
|
||||
'Stop Orders Submission',
|
||||
'Stop Orders Cancellation',
|
||||
'Submit Order',
|
||||
'Update Margin Mode',
|
||||
],
|
||||
'Transfers and Withdrawals': [
|
||||
'Transfer Funds',
|
||||
'Cancel Transfer Funds',
|
||||
'Withdraw',
|
||||
],
|
||||
Governance: ['Delegate', 'Undelegate', 'Vote on Proposal', 'Proposal'],
|
||||
Governance: [
|
||||
'Batch Proposal',
|
||||
'Delegate',
|
||||
'Undelegate',
|
||||
'Vote on Proposal',
|
||||
'Proposal',
|
||||
],
|
||||
Referrals: [
|
||||
'Apply Referral Code',
|
||||
'Create Referral Set',
|
||||
'Join Team',
|
||||
'Update Party Profile',
|
||||
'Update Referral Set',
|
||||
],
|
||||
'External Data': ['Chain Event', 'Submit Oracle Data'],
|
||||
|
||||
@@ -12,4 +12,5 @@ export const Routes = {
|
||||
ORACLES: 'oracles',
|
||||
NETWORK_PARAMETERS: 'network-parameters',
|
||||
DISCLAIMER: 'disclaimer',
|
||||
TREASURY: 'treasury',
|
||||
};
|
||||
|
||||
@@ -30,6 +30,7 @@ import { PartyAccountsByAsset } from './parties/id/accounts';
|
||||
import { Disclaimer } from './pages/disclaimer';
|
||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
import RestrictedPage from './restricted';
|
||||
import { NetworkTreasury } from './treasury';
|
||||
|
||||
export type Navigable = {
|
||||
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
|
||||
? [
|
||||
{
|
||||
@@ -358,6 +370,7 @@ export const useRouterConfig = () => {
|
||||
...marketsRoutes,
|
||||
...networkParametersRoutes,
|
||||
...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>
|
||||
);
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"short_name": "Mainnet Stats",
|
||||
"name": "Vega Mainnet statistics",
|
||||
"short_name": "Explorer VEGA",
|
||||
"name": "Vega Protocol - Explorer",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
|
||||
@@ -2,7 +2,7 @@ export const proposalsData = {
|
||||
proposalsConnection: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
proposalNode: {
|
||||
id: 'e8ba9d268e12514644fd1fc7ff289292f4ce6489cc32cc73133aea52c04aef89',
|
||||
rationale: {
|
||||
title: 'Add asset Wrapped Ether',
|
||||
@@ -56,7 +56,7 @@ export const proposalsData = {
|
||||
__typename: 'ProposalEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
proposalNode: {
|
||||
id: 'd848fc7881f13d366df5f61ab139d5fcfa72bf838151bb51b54381870e357931',
|
||||
rationale: {
|
||||
title: 'Add asset Dai Stablecoin',
|
||||
@@ -110,60 +110,7 @@ export const proposalsData = {
|
||||
__typename: 'ProposalEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: 'ccbd651b4a1167fd73c4a0340ac759fa0a31ca487ad46a13254b741ad71947ed',
|
||||
rationale: {
|
||||
title: 'New DAI market',
|
||||
description: 'New DAI market',
|
||||
__typename: 'ProposalRationale',
|
||||
},
|
||||
reference: '0VFQusmmESdrP5GuL8naB6lxfoE3RPGaEeo7abdN',
|
||||
state: 'STATE_ENACTED',
|
||||
datetime: '2022-11-26T19:36:19.26034Z',
|
||||
rejectionReason: null,
|
||||
party: {
|
||||
id: '69464e35bcb8e8a2900ca0f87acaf252d50cf2ab2fc73694845a16b7c8a0dc6f',
|
||||
__typename: 'Party',
|
||||
},
|
||||
errorDetails: null,
|
||||
terms: {
|
||||
closingDatetime: '2022-11-26T19:36:42Z',
|
||||
enactmentDatetime: '2023-03-22T13:57:37Z',
|
||||
change: {
|
||||
instrument: {
|
||||
name: 'UNIDAI Monthly (Dec 2022)',
|
||||
code: 'UNIDAI.MF21',
|
||||
product: {
|
||||
settlementAsset: { symbol: 'tDAI', __typename: 'Asset' },
|
||||
__typename: 'FutureProduct',
|
||||
},
|
||||
__typename: 'InstrumentConfiguration',
|
||||
},
|
||||
__typename: 'NewMarket',
|
||||
},
|
||||
__typename: 'ProposalTerms',
|
||||
},
|
||||
votes: {
|
||||
yes: {
|
||||
totalTokens: '0',
|
||||
totalNumber: '0',
|
||||
totalEquityLikeShareWeight: '0',
|
||||
__typename: 'ProposalVoteSide',
|
||||
},
|
||||
no: {
|
||||
totalTokens: '0',
|
||||
totalNumber: '0',
|
||||
totalEquityLikeShareWeight: '0',
|
||||
__typename: 'ProposalVoteSide',
|
||||
},
|
||||
__typename: 'ProposalVotes',
|
||||
},
|
||||
__typename: 'Proposal',
|
||||
},
|
||||
__typename: 'ProposalEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
proposalNode: {
|
||||
id: 'bc70383f0e9515b15542cf4c63590cd2ca46b3363ba7c4a72af0e62112b3951b',
|
||||
rationale: {
|
||||
title: 'USDC-III',
|
||||
@@ -217,60 +164,7 @@ export const proposalsData = {
|
||||
__typename: 'ProposalEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: '9d9b2a9d0179d0e4ccb317f6c4a5db0b905d893190bfb5e5499985ef313281c8',
|
||||
rationale: {
|
||||
title: 'New BTC market',
|
||||
description: 'New BTC market',
|
||||
__typename: 'ProposalRationale',
|
||||
},
|
||||
reference: 'AXeRWS3TvLBFDgWOSHQpKFJf3NTbnWK6310q02fZ',
|
||||
state: 'STATE_ENACTED',
|
||||
datetime: '2022-11-26T19:36:19.26034Z',
|
||||
rejectionReason: null,
|
||||
party: {
|
||||
id: '69464e35bcb8e8a2900ca0f87acaf252d50cf2ab2fc73694845a16b7c8a0dc6f',
|
||||
__typename: 'Party',
|
||||
},
|
||||
errorDetails: null,
|
||||
terms: {
|
||||
closingDatetime: '2022-11-26T19:36:42Z',
|
||||
enactmentDatetime: '2023-03-22T13:57:37Z',
|
||||
change: {
|
||||
instrument: {
|
||||
name: 'ETHBTC Quarterly (Feb 2023)',
|
||||
code: 'ETHBTC.QM21',
|
||||
product: {
|
||||
settlementAsset: { symbol: 'tBTC', __typename: 'Asset' },
|
||||
__typename: 'FutureProduct',
|
||||
},
|
||||
__typename: 'InstrumentConfiguration',
|
||||
},
|
||||
__typename: 'NewMarket',
|
||||
},
|
||||
__typename: 'ProposalTerms',
|
||||
},
|
||||
votes: {
|
||||
yes: {
|
||||
totalTokens: '0',
|
||||
totalNumber: '0',
|
||||
totalEquityLikeShareWeight: '0',
|
||||
__typename: 'ProposalVoteSide',
|
||||
},
|
||||
no: {
|
||||
totalTokens: '0',
|
||||
totalNumber: '0',
|
||||
totalEquityLikeShareWeight: '0',
|
||||
__typename: 'ProposalVoteSide',
|
||||
},
|
||||
__typename: 'ProposalVotes',
|
||||
},
|
||||
__typename: 'Proposal',
|
||||
},
|
||||
__typename: 'ProposalEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
proposalNode: {
|
||||
id: '9c48796e7988769ededc2b2b02220b00e93f65f23e8141bf1fd23a6983d95943',
|
||||
rationale: {
|
||||
title: 'Update governance.proposal.asset.requiredMajority',
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
navigateTo,
|
||||
navigation,
|
||||
turnTelemetryOff,
|
||||
setRiskAccepted,
|
||||
} from '../../support/common.functions';
|
||||
import {
|
||||
clickOnValidatorFromList,
|
||||
@@ -57,6 +58,7 @@ context(
|
||||
// 1002-STKE-002, 1002-STKE-032
|
||||
before('visit staking tab and connect vega wallet', function () {
|
||||
cy.visit('/');
|
||||
setRiskAccepted();
|
||||
ethereumWalletConnect();
|
||||
cy.connectVegaWallet();
|
||||
vegaWalletSetSpecifiedApprovalAmount('1000');
|
||||
@@ -236,7 +238,7 @@ context(
|
||||
});
|
||||
|
||||
// 1002-STKE-041 1002-STKE-053
|
||||
it(
|
||||
it.skip(
|
||||
'Able to remove part of a stake against a validator',
|
||||
// @ts-ignore clash between jest and cypress
|
||||
{ tags: '@smoke' },
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
navigateTo,
|
||||
navigation,
|
||||
turnTelemetryOff,
|
||||
setRiskAccepted,
|
||||
} from '../../support/common.functions';
|
||||
import {
|
||||
stakingPageAssociateTokens,
|
||||
@@ -57,6 +58,7 @@ context(
|
||||
function () {
|
||||
cy.clearLocalStorage();
|
||||
turnTelemetryOff();
|
||||
setRiskAccepted();
|
||||
cy.mockChainId();
|
||||
cy.reload();
|
||||
waitForSpinner();
|
||||
|
||||
@@ -27,7 +27,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
||||
cy.getByTestId('app-announcement').should('not.exist');
|
||||
});
|
||||
|
||||
it('should show open or enacted proposals without proposal summary', function () {
|
||||
it.skip('should show open or enacted proposals without proposal summary', function () {
|
||||
cy.get('body').then(($body) => {
|
||||
if (!$body.find('[data-testid="proposals-list-item"]').length) {
|
||||
cy.createMarket();
|
||||
@@ -159,7 +159,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
||||
cy.getByTestId('node-url-custom').click({ force: true });
|
||||
cy.get('input').should('exist');
|
||||
cy.getByTestId('connect').should('be.disabled');
|
||||
cy.getByTestId('icon-cross').click();
|
||||
cy.getByTestId('dialog-close').click();
|
||||
});
|
||||
|
||||
it('should display eth data', function () {
|
||||
@@ -189,7 +189,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
||||
cy.viewport('iphone-xr');
|
||||
});
|
||||
|
||||
it('should have burger button', () => {
|
||||
it.skip('should have burger button', () => {
|
||||
cy.getByTestId('button-menu-drawer').should('be.visible').click();
|
||||
cy.getByTestId('menu-drawer').should('be.visible');
|
||||
});
|
||||
|
||||
@@ -34,16 +34,10 @@ context('View functionality with public key', { tags: '@smoke' }, function () {
|
||||
cy.connectPublicKey(vegaWalletPubKey);
|
||||
});
|
||||
|
||||
it('Able to connect public key using url', function () {
|
||||
cy.getByTestId('exit-view').click();
|
||||
cy.visit(`/?address=${vegaWalletPubKey}`);
|
||||
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();
|
||||
cy.getByTestId('currency-title', { timeout: 10000 })
|
||||
.should('have.length.at.least', 4)
|
||||
.should('have.length.at.least', 2)
|
||||
.and('contain.text', 'USDC (fake)');
|
||||
});
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
||||
import {
|
||||
navigation,
|
||||
setRiskAccepted,
|
||||
verifyPageHeader,
|
||||
verifyTabHighlighted,
|
||||
} from '../../support/common.functions';
|
||||
@@ -187,6 +188,7 @@ context('Validators Page - verify elements on page', function () {
|
||||
before('connect wallets and click on validator', function () {
|
||||
cy.mockChainId();
|
||||
cy.visit('/validators');
|
||||
setRiskAccepted();
|
||||
cy.connectVegaWallet();
|
||||
clickOnValidatorFromList(0);
|
||||
});
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { truncateByChars } from '@vegaprotocol/utils';
|
||||
import { waitForSpinner } from '../../support/common.functions';
|
||||
import {
|
||||
setRiskAccepted,
|
||||
waitForSpinner,
|
||||
} from '../../support/common.functions';
|
||||
import {
|
||||
vegaWalletFaucetAssetsWithoutCheck,
|
||||
vegaWalletTeardown,
|
||||
@@ -11,7 +14,6 @@ const connectButton = 'connect-vega-wallet';
|
||||
const getVegaLink = 'link';
|
||||
const dialog = '[role="dialog"]:visible';
|
||||
const dialogHeader = 'dialog-title';
|
||||
const walletDialogHeader = 'wallet-dialog-title';
|
||||
const connectorsList = 'connectors-list';
|
||||
const dialogCloseBtn = 'dialog-close';
|
||||
const accountNo = 'vega-account-truncated';
|
||||
@@ -34,6 +36,7 @@ context(
|
||||
() => {
|
||||
before('visit token home page', () => {
|
||||
cy.visit('/');
|
||||
setRiskAccepted();
|
||||
cy.get(walletContainer, { timeout: 60000 }).should('be.visible');
|
||||
});
|
||||
|
||||
@@ -63,17 +66,12 @@ context(
|
||||
|
||||
it('should have Connect Vega header visible', () => {
|
||||
cy.get(dialog).within(() => {
|
||||
cy.getByTestId(walletDialogHeader)
|
||||
cy.getByTestId(connectorsList)
|
||||
.should('be.visible')
|
||||
.and('have.text', 'Get a Vega wallet');
|
||||
});
|
||||
});
|
||||
|
||||
it('should have jsonRpc and hosted connection options visible on list', function () {
|
||||
cy.getByTestId(connectorsList).within(() => {
|
||||
cy.getByTestId('connector-jsonRpc')
|
||||
.should('be.visible')
|
||||
.and('have.text', 'Use the Desktop App/CLI');
|
||||
.and(
|
||||
'have.text',
|
||||
'Get the Vega WalletGet MetaMask>_Command Line WalletView as public key'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -88,7 +86,6 @@ context(
|
||||
before('connect vega wallet', function () {
|
||||
cy.mockChainId();
|
||||
cy.visit('/');
|
||||
cy.wait('@ChainId');
|
||||
cy.connectVegaWallet();
|
||||
vegaWalletTeardown();
|
||||
});
|
||||
|
||||
@@ -102,6 +102,12 @@ export function turnTelemetryOff() {
|
||||
);
|
||||
}
|
||||
|
||||
export function setRiskAccepted() {
|
||||
cy.window().then((win) =>
|
||||
win.localStorage.setItem('vega_wallet_risk_accepted', 'true')
|
||||
);
|
||||
}
|
||||
|
||||
export function dissociateFromSecondWalletKey() {
|
||||
const secondWalletKey = Cypress.env('vegaWalletPublicKey2Short');
|
||||
cy.getByTestId('vega-in-wallet')
|
||||
|
||||
@@ -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_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||
NX_DELEGATIONS_PAGINATION=50
|
||||
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_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_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
|
||||
CYPRESS_FAIRGROUND=false
|
||||
@@ -33,7 +32,7 @@ LC_ALL="en_US.UTF-8"
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_METAMASK_SNAPS=true
|
||||
NX_PRODUCT_PERPETUALS=false
|
||||
NX_UPDATE_MARKET_STATE=false
|
||||
NX_PRODUCT_PERPETUALS=true
|
||||
NX_UPDATE_MARKET_STATE=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_VEGA_WALLET_URL=http://localhost:1789
|
||||
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_VEGA_REST_URL=http://localhost:3008/api/v2/
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -8,14 +8,13 @@ NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
||||
NX_VEGA_EXPLORER_URL=#
|
||||
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_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_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
||||
|
||||
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_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_DOCS_URL=https://docs.vega.xyz/mainnet
|
||||
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_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
|
||||
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_VEGA_EXPLORER_URL=https://explorer.mainnet-mirror.vega.rocks
|
||||
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_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
|
||||
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_EXPLORER_URL=https://explorer.stagnet1.vega.rocks
|
||||
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_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||
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_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_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_DOCS_URL=https://docs.vega.xyz/testnet
|
||||
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_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||
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_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_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_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_WEBSOCKET_URL=wss://be.validators-testnet.vega.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { toBigNum } from '@vegaprotocol/utils';
|
||||
import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet, useEagerConnect } from '@vegaprotocol/wallet';
|
||||
import { useVegaWallet, useEagerConnect } from '@vegaprotocol/wallet-react';
|
||||
import { useFeatureFlags, useEnvironment } from '@vegaprotocol/environment';
|
||||
import { useWeb3React } from '@web3-react/core';
|
||||
import React, { Suspense } from 'react';
|
||||
@@ -15,20 +15,6 @@ import {
|
||||
} from './contexts/app-state/app-state-context';
|
||||
import { useContracts } from './contexts/contracts/contracts-context';
|
||||
import { useRefreshAssociatedBalances } from './hooks/use-refresh-associated-balances';
|
||||
import { useConnectors } from './lib/vega-connectors';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
|
||||
const useVegaWalletEagerConnect = () => {
|
||||
const connectors = useConnectors();
|
||||
const vegaConnecting = useEagerConnect(connectors);
|
||||
const { pubKey, connect } = useVegaWallet();
|
||||
const [searchParams] = useSearchParams();
|
||||
const [query] = React.useState(searchParams.get('address'));
|
||||
if (query && !pubKey) {
|
||||
connect(connectors.view);
|
||||
}
|
||||
return vegaConnecting;
|
||||
};
|
||||
|
||||
export const AppLoader = ({ children }: { children: React.ReactElement }) => {
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
@@ -40,7 +26,7 @@ export const AppLoader = ({ children }: { children: React.ReactElement }) => {
|
||||
const { token, staking, vesting } = useContracts();
|
||||
const setAssociatedBalances = useRefreshAssociatedBalances();
|
||||
const [balancesLoaded, setBalancesLoaded] = React.useState(false);
|
||||
const vegaConnecting = useVegaWalletEagerConnect();
|
||||
const vegaConnecting = useEagerConnect();
|
||||
|
||||
const loaded = balancesLoaded && !vegaConnecting;
|
||||
|
||||
|
||||
+34
-141
@@ -1,7 +1,6 @@
|
||||
import './i18n';
|
||||
|
||||
import React, { useEffect } from 'react';
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { BrowserRouter as Router, useLocation } from 'react-router-dom';
|
||||
import { AppLoader } from './app-loader';
|
||||
import { NetworkInfo } from '@vegaprotocol/network-info';
|
||||
@@ -26,7 +25,7 @@ import {
|
||||
} from '@vegaprotocol/web3';
|
||||
import { Web3Provider } from '@vegaprotocol/web3';
|
||||
import { VegaWalletDialogs } from './components/vega-wallet-dialogs';
|
||||
import { VegaWalletProvider, useChainId } from '@vegaprotocol/wallet';
|
||||
import { WalletProvider } from '@vegaprotocol/wallet-react';
|
||||
import {
|
||||
useVegaTransactionManager,
|
||||
useVegaTransactionUpdater,
|
||||
@@ -36,32 +35,30 @@ import { useEthereumConfig } from '@vegaprotocol/web3';
|
||||
import {
|
||||
useEnvironment,
|
||||
NetworkLoader,
|
||||
useInitializeEnv,
|
||||
NodeGuard,
|
||||
NodeSwitcherDialog,
|
||||
useNodeSwitcherStore,
|
||||
DocsLinks,
|
||||
NodeFailure,
|
||||
AppLoader as Loader,
|
||||
useInitializeEnv,
|
||||
} from '@vegaprotocol/environment';
|
||||
import { ENV } from './config';
|
||||
import type { InMemoryCacheConfig } from '@apollo/client';
|
||||
import { CreateWithdrawalDialog } from '@vegaprotocol/withdraws';
|
||||
import { SplashLoader } from './components/splash-loader';
|
||||
import { ToastsManager } from './toasts-manager';
|
||||
import {
|
||||
TelemetryDialog,
|
||||
TELEMETRY_ON,
|
||||
} from './components/telemetry-dialog/telemetry-dialog';
|
||||
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
||||
import { TelemetryDialog } from './components/telemetry-dialog/telemetry-dialog';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { isPartyNotFoundError } from './lib/party';
|
||||
import { useSentryInit } from './hooks/use-sentry-init';
|
||||
import { useVegaWalletConfig } from './hooks/use-vega-wallet-config';
|
||||
|
||||
const cache: InMemoryCacheConfig = {
|
||||
typePolicies: {
|
||||
Account: {
|
||||
keyFields: false,
|
||||
},
|
||||
Instrument: {
|
||||
keyFields: ['code'],
|
||||
},
|
||||
Delegation: {
|
||||
keyFields: false,
|
||||
// Only get full updates
|
||||
@@ -101,32 +98,12 @@ const Web3Container = ({
|
||||
/** Ethereum provider url */
|
||||
providerUrl: string;
|
||||
}) => {
|
||||
const InitializeHandlers = () => {
|
||||
useVegaTransactionManager();
|
||||
useVegaTransactionUpdater();
|
||||
useEthTransactionManager();
|
||||
useEthTransactionUpdater();
|
||||
useEthWithdrawApprovalsManager();
|
||||
return null;
|
||||
};
|
||||
|
||||
const [connectors, initializeConnectors] = useWeb3ConnectStore((store) => [
|
||||
store.connectors,
|
||||
store.initialize,
|
||||
]);
|
||||
const {
|
||||
ETHEREUM_PROVIDER_URL,
|
||||
ETH_LOCAL_PROVIDER_URL,
|
||||
ETH_WALLET_MNEMONIC,
|
||||
VEGA_ENV,
|
||||
VEGA_URL,
|
||||
VEGA_EXPLORER_URL,
|
||||
CHROME_EXTENSION_URL,
|
||||
MOZILLA_EXTENSION_URL,
|
||||
VEGA_WALLET_URL,
|
||||
} = useEnvironment();
|
||||
|
||||
const vegaChainId = useChainId(VEGA_URL);
|
||||
const { ETHEREUM_PROVIDER_URL, ETH_LOCAL_PROVIDER_URL, ETH_WALLET_MNEMONIC } =
|
||||
useEnvironment();
|
||||
|
||||
useEffect(() => {
|
||||
if (chainId) {
|
||||
@@ -147,50 +124,31 @@ const Web3Container = ({
|
||||
ETH_LOCAL_PROVIDER_URL,
|
||||
ETH_WALLET_MNEMONIC,
|
||||
]);
|
||||
const sideBar = React.useMemo(() => {
|
||||
return [<EthWallet />, <VegaWallet />];
|
||||
}, []);
|
||||
|
||||
if (connectors.length === 0) {
|
||||
const vegaWalletConfig = useVegaWalletConfig();
|
||||
|
||||
if (!vegaWalletConfig || connectors.length === 0) {
|
||||
// Prevent loading when the connectors are not initialized
|
||||
return <SplashLoader />;
|
||||
}
|
||||
|
||||
if (
|
||||
!VEGA_URL ||
|
||||
!VEGA_WALLET_URL ||
|
||||
!VEGA_EXPLORER_URL ||
|
||||
!DocsLinks ||
|
||||
!CHROME_EXTENSION_URL ||
|
||||
!MOZILLA_EXTENSION_URL ||
|
||||
!vegaChainId
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Web3Provider connectors={connectors}>
|
||||
<Web3Connector connectors={connectors} chainId={Number(chainId)}>
|
||||
<VegaWalletProvider
|
||||
config={{
|
||||
network: VEGA_ENV,
|
||||
vegaUrl: VEGA_URL,
|
||||
chainId: vegaChainId,
|
||||
vegaWalletServiceUrl: VEGA_WALLET_URL,
|
||||
links: {
|
||||
explorer: VEGA_EXPLORER_URL,
|
||||
concepts: DocsLinks?.VEGA_WALLET_CONCEPTS_URL,
|
||||
chromeExtensionUrl: CHROME_EXTENSION_URL,
|
||||
mozillaExtensionUrl: MOZILLA_EXTENSION_URL,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<WalletProvider config={vegaWalletConfig}>
|
||||
<ContractsProvider>
|
||||
<AppLoader>
|
||||
<BalanceManager>
|
||||
<>
|
||||
<AppLayout>
|
||||
<TemplateSidebar sidebar={sideBar}>
|
||||
<TemplateSidebar
|
||||
sidebar={
|
||||
<>
|
||||
<EthWallet />
|
||||
<VegaWallet />
|
||||
</>
|
||||
}
|
||||
>
|
||||
<AppRouter />
|
||||
</TemplateSidebar>
|
||||
<footer className="p-4 break-all border-t border-neutral-700">
|
||||
@@ -208,7 +166,7 @@ const Web3Container = ({
|
||||
</BalanceManager>
|
||||
</AppLoader>
|
||||
</ContractsProvider>
|
||||
</VegaWalletProvider>
|
||||
</WalletProvider>
|
||||
</Web3Connector>
|
||||
</Web3Provider>
|
||||
);
|
||||
@@ -228,20 +186,9 @@ const ScrollToTop = () => {
|
||||
return null;
|
||||
};
|
||||
|
||||
const removeQueryParams = (url: string) => {
|
||||
return url.split('?')[0];
|
||||
};
|
||||
|
||||
const AppContainer = () => {
|
||||
const { config, loading, error } = useEthereumConfig();
|
||||
const {
|
||||
VEGA_ENV,
|
||||
VEGA_URL,
|
||||
GIT_COMMIT_HASH,
|
||||
GIT_BRANCH,
|
||||
ETHEREUM_PROVIDER_URL,
|
||||
} = useEnvironment();
|
||||
const [telemetryOn] = useLocalStorage(TELEMETRY_ON);
|
||||
const { VEGA_URL, ETHEREUM_PROVIDER_URL } = useEnvironment();
|
||||
const { t } = useTranslation();
|
||||
const [nodeSwitcherOpen, setNodeSwitcher] = useNodeSwitcherStore((store) => [
|
||||
store.dialogOpen,
|
||||
@@ -251,70 +198,7 @@ const AppContainer = () => {
|
||||
// Hacky skip all the loading & web3 init for geo restricted users
|
||||
const isRestricted = document?.location?.pathname?.includes('/restricted');
|
||||
|
||||
useEffect(() => {
|
||||
if (ENV.dsn && telemetryOn === 'true') {
|
||||
Sentry.init({
|
||||
dsn: ENV.dsn,
|
||||
tracesSampleRate: 0.1,
|
||||
enabled: true,
|
||||
environment: VEGA_ENV,
|
||||
release: GIT_COMMIT_HASH,
|
||||
beforeSend(event, hint) {
|
||||
const error = hint?.originalException;
|
||||
const errorIsString = typeof error === 'string';
|
||||
const errorIsObject = error instanceof Error;
|
||||
const requestUrl = event.request?.url;
|
||||
const transaction = event.transaction;
|
||||
|
||||
if (
|
||||
(errorIsString && isPartyNotFoundError({ message: error })) ||
|
||||
(errorIsObject && isPartyNotFoundError(error))
|
||||
) {
|
||||
// This error is caused by a pubkey making an API request before
|
||||
// it has interacted with the chain. This isn't needed in Sentry.
|
||||
return null;
|
||||
}
|
||||
|
||||
const updatedRequest =
|
||||
requestUrl && requestUrl.includes('/claim?')
|
||||
? { ...event.request, url: removeQueryParams(requestUrl) }
|
||||
: event.request;
|
||||
|
||||
const updatedTransaction =
|
||||
transaction && transaction.includes('/claim?')
|
||||
? removeQueryParams(transaction)
|
||||
: transaction;
|
||||
|
||||
const updatedBreadcrumbs = event.breadcrumbs?.map((breadcrumb) => {
|
||||
if (
|
||||
breadcrumb.type === 'navigation' &&
|
||||
breadcrumb.data?.to?.includes('/claim?')
|
||||
) {
|
||||
return {
|
||||
...breadcrumb,
|
||||
data: {
|
||||
...breadcrumb.data,
|
||||
to: removeQueryParams(breadcrumb.data.to),
|
||||
},
|
||||
};
|
||||
}
|
||||
return breadcrumb;
|
||||
});
|
||||
|
||||
return {
|
||||
...event,
|
||||
request: updatedRequest,
|
||||
transaction: updatedTransaction,
|
||||
breadcrumbs: updatedBreadcrumbs ?? event.breadcrumbs,
|
||||
};
|
||||
},
|
||||
});
|
||||
Sentry.setTag('branch', GIT_BRANCH);
|
||||
Sentry.setTag('commit', GIT_COMMIT_HASH);
|
||||
} else {
|
||||
Sentry.close();
|
||||
}
|
||||
}, [GIT_COMMIT_HASH, GIT_BRANCH, VEGA_ENV, telemetryOn]);
|
||||
useSentryInit();
|
||||
|
||||
if (isRestricted) {
|
||||
return (
|
||||
@@ -356,6 +240,15 @@ const AppContainer = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const InitializeHandlers = () => {
|
||||
useVegaTransactionManager();
|
||||
useVegaTransactionUpdater();
|
||||
useEthTransactionManager();
|
||||
useEthTransactionUpdater();
|
||||
useEthWithdrawApprovalsManager();
|
||||
return null;
|
||||
};
|
||||
|
||||
function App() {
|
||||
useInitializeEnv();
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"short_name": "Mainnet Stats",
|
||||
"name": "Vega Mainnet statistics",
|
||||
"short_name": "Governance VEGA",
|
||||
"name": "Vega Protocol - Governance",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
|
||||
@@ -7,7 +7,7 @@ import { useGetAssociationBreakdown } from '../../hooks/use-get-association-brea
|
||||
import { useGetUserBalances } from '../../hooks/use-get-user-balances';
|
||||
import { useBalances } from '../../lib/balances/balances-store';
|
||||
import type { ReactElement } from 'react';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet-react';
|
||||
import { useListenForStakingEvents as useListenForAssociationEvents } from '../../hooks/use-listen-for-staking-events';
|
||||
import { useTranches } from '../../lib/tranches/tranches-store';
|
||||
import { useUserTrancheBalances } from '../../routes/redemption/hooks';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import classnames from 'classnames';
|
||||
import { Icon } from '@vegaprotocol/ui-toolkit';
|
||||
import { VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
|
||||
import type { Dispatch, SetStateAction, ReactNode } from 'react';
|
||||
|
||||
interface CollapsibleToggleProps {
|
||||
@@ -15,22 +15,19 @@ export const CollapsibleToggle = ({
|
||||
dataTestId,
|
||||
children,
|
||||
}: CollapsibleToggleProps) => {
|
||||
const classes = classnames(
|
||||
'mb-4 transition-transform ease-in-out duration-300',
|
||||
{
|
||||
'rotate-180': toggleState,
|
||||
}
|
||||
);
|
||||
const classes = classnames('transition-transform ease-in-out duration-300', {
|
||||
'rotate-180': toggleState,
|
||||
});
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={() => setToggleState(!toggleState)}
|
||||
data-testid={dataTestId}
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex items-baseline gap-3">
|
||||
{children}
|
||||
<div className={classes} data-testid="toggle-icon-wrapper">
|
||||
<Icon name="chevron-down" size={8} />
|
||||
<VegaIcon name={VegaIconNames.CHEVRON_DOWN} size={20} />
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
@@ -1,18 +1,13 @@
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useVegaWalletDialogStore } from '@vegaprotocol/wallet';
|
||||
import { useDialogStore } from '@vegaprotocol/wallet-react';
|
||||
|
||||
export const ConnectToVega = () => {
|
||||
const { t } = useTranslation();
|
||||
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
|
||||
openVegaWalletDialog: store.openVegaWalletDialog,
|
||||
}));
|
||||
const openVegaWalletDialog = useDialogStore((store) => store.open);
|
||||
return (
|
||||
<Button
|
||||
onClick={() => {
|
||||
openVegaWalletDialog();
|
||||
}}
|
||||
onClick={openVegaWalletDialog}
|
||||
data-testid="connect-to-vega-wallet-btn"
|
||||
variant="primary"
|
||||
>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import classNames from 'classnames';
|
||||
import { type ReactNode } from 'react';
|
||||
|
||||
interface HeadingProps {
|
||||
title?: string;
|
||||
title?: ReactNode;
|
||||
centerContent?: boolean;
|
||||
marginTop?: boolean;
|
||||
marginBottom?: boolean;
|
||||
|
||||
+3
-3
@@ -6,8 +6,8 @@ import {
|
||||
Intent,
|
||||
Icon,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet } from '../use-vega-wallet';
|
||||
import { useT } from '../use-t';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export interface VegaManageDialogProps {
|
||||
dialogOpen: boolean;
|
||||
@@ -18,7 +18,7 @@ export const VegaManageDialog = ({
|
||||
dialogOpen,
|
||||
setDialogOpen,
|
||||
}: VegaManageDialogProps) => {
|
||||
const t = useT();
|
||||
const { t } = useTranslation();
|
||||
const { pubKey, pubKeys, selectPubKey, disconnect } = useVegaWallet();
|
||||
return (
|
||||
<Dialog
|
||||
@@ -0,0 +1,44 @@
|
||||
import {
|
||||
useLinks,
|
||||
DApp,
|
||||
CONSOLE_REWARDS_PAGE,
|
||||
} from '@vegaprotocol/environment';
|
||||
import {
|
||||
ExternalLink,
|
||||
Intent,
|
||||
NotificationBanner,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { Trans } from 'react-i18next';
|
||||
import { useMatch } from 'react-router-dom';
|
||||
import Routes from '../../routes/routes';
|
||||
import { type ReactNode } from 'react';
|
||||
|
||||
const ConsoleRewardsLink = ({ children }: { children: ReactNode }) => {
|
||||
const consoleLink = useLinks(DApp.Console);
|
||||
return (
|
||||
<ExternalLink
|
||||
href={consoleLink(CONSOLE_REWARDS_PAGE)}
|
||||
className="underline inline-flex gap-1 items-center"
|
||||
title="Rewards in Console"
|
||||
>
|
||||
<span>{children}</span>
|
||||
<VegaIcon size={12} name={VegaIconNames.OPEN_EXTERNAL} />
|
||||
</ExternalLink>
|
||||
);
|
||||
};
|
||||
|
||||
export const RewardsMovedNotification = () => {
|
||||
const onRewardsPage = useMatch(Routes.REWARDS);
|
||||
if (!onRewardsPage) return null;
|
||||
|
||||
return (
|
||||
<NotificationBanner intent={Intent.Warning}>
|
||||
<Trans
|
||||
i18nKey="rewardsMovedNotification"
|
||||
components={[<ConsoleRewardsLink>Console</ConsoleRewardsLink>]}
|
||||
/>
|
||||
</NotificationBanner>
|
||||
);
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
import classNames from 'classnames';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet-react';
|
||||
import type { ReactNode } from 'react';
|
||||
import { AnnouncementBanner } from '@vegaprotocol/announcements';
|
||||
import { Nav } from '../nav';
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
ProtocolUpgradeProposalNotification,
|
||||
} from '@vegaprotocol/proposals';
|
||||
import { ViewingAsBanner } from '@vegaprotocol/ui-toolkit';
|
||||
import { RewardsMovedNotification } from '../notifications/rewards-moved-notification';
|
||||
|
||||
interface AppLayoutProps {
|
||||
children: ReactNode;
|
||||
@@ -45,8 +46,10 @@ export const AppLayout = ({ children }: AppLayoutProps) => {
|
||||
|
||||
const NotificationsContainer = () => {
|
||||
const { isReadOnly, pubKey, disconnect } = useVegaWallet();
|
||||
|
||||
return (
|
||||
<div data-testid="banners">
|
||||
<RewardsMovedNotification />
|
||||
<ProtocolUpgradeProposalNotification
|
||||
mode={ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING}
|
||||
/>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React from 'react';
|
||||
import { Children, type ReactNode } from 'react';
|
||||
|
||||
export interface TemplateSidebarProps {
|
||||
children: React.ReactNode;
|
||||
sidebar: React.ReactNode[];
|
||||
children: ReactNode;
|
||||
sidebar: ReactNode;
|
||||
}
|
||||
|
||||
export function TemplateSidebar({ children, sidebar }: TemplateSidebarProps) {
|
||||
@@ -12,9 +12,9 @@ export function TemplateSidebar({ children, sidebar }: TemplateSidebarProps) {
|
||||
{children}
|
||||
</main>
|
||||
<aside className="col-start-2 row-start-1 row-span-2 hidden lg:block p-4 bg-banner bg-contain border-l border-neutral-700">
|
||||
{sidebar.map((Component, i) => (
|
||||
{Children.map(sidebar, (child, i) => (
|
||||
<section className="mb-4 last:mb-0" key={i}>
|
||||
{Component}
|
||||
{child}
|
||||
</section>
|
||||
))}
|
||||
</aside>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
|
||||
import { useVegaWallet, useDialogStore } from '@vegaprotocol/wallet-react';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
@@ -10,9 +10,7 @@ interface VegaWalletContainerProps {
|
||||
export const VegaWalletContainer = ({ children }: VegaWalletContainerProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { pubKey } = useVegaWallet();
|
||||
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
|
||||
openVegaWalletDialog: store.openVegaWalletDialog,
|
||||
}));
|
||||
const openVegaWalletDialog = useDialogStore((store) => store.open);
|
||||
|
||||
if (!pubKey) {
|
||||
return (
|
||||
|
||||
@@ -9,7 +9,7 @@ import Routes from '../../routes/routes';
|
||||
export const RiskMessage = () => {
|
||||
return (
|
||||
<>
|
||||
<div className="bg-vega-light-100 dark:bg-vega-dark-100 p-6 mb-6">
|
||||
<div className="bg-vega-light-100 dark:bg-vega-dark-100 p-6">
|
||||
<ul className="list-[square] ml-4">
|
||||
<li>
|
||||
{t(
|
||||
@@ -23,7 +23,7 @@ export const RiskMessage = () => {
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<p className="mb-8">
|
||||
<p>
|
||||
{t(
|
||||
'By using the Vega Governance App, you acknowledge that you have read and understood the'
|
||||
)}{' '}
|
||||
|
||||
@@ -1,25 +1,39 @@
|
||||
import {
|
||||
VegaConnectDialog,
|
||||
VegaManageDialog,
|
||||
ViewAsDialog,
|
||||
} from '@vegaprotocol/wallet';
|
||||
ConnectDialogWithRiskAck,
|
||||
useDialogStore,
|
||||
} from '@vegaprotocol/wallet-react';
|
||||
import {
|
||||
AppStateActionType,
|
||||
useAppState,
|
||||
} from '../../contexts/app-state/app-state-context';
|
||||
import { useConnectors } from '../../lib/vega-connectors';
|
||||
import { RiskMessage } from './risk-message';
|
||||
import { VegaManageDialog } from '../manage-dialog';
|
||||
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
||||
import { Networks, useEnvironment } from '@vegaprotocol/environment';
|
||||
|
||||
export const VegaWalletDialogs = () => {
|
||||
const { VEGA_ENV } = useEnvironment();
|
||||
const { appState, appDispatch } = useAppState();
|
||||
const connectors = useConnectors();
|
||||
const [riskAccepted, setRiskAccepted] = useLocalStorage(
|
||||
'vega_wallet_risk_accepted'
|
||||
);
|
||||
const vegaWalletDialogOpen = useDialogStore((store) => store.isOpen);
|
||||
const setVegaWalletDialog = useDialogStore((store) => store.set);
|
||||
return (
|
||||
<>
|
||||
<VegaConnectDialog
|
||||
connectors={connectors}
|
||||
riskMessage={<RiskMessage />}
|
||||
<ConnectDialogWithRiskAck
|
||||
open={vegaWalletDialogOpen}
|
||||
onChange={setVegaWalletDialog}
|
||||
riskAccepted={
|
||||
VEGA_ENV === Networks.TESTNET ? riskAccepted === 'true' : true
|
||||
}
|
||||
riskAckContent={<RiskMessage />}
|
||||
onRiskAccepted={() => setRiskAccepted('true')}
|
||||
onRiskRejected={() => {
|
||||
setRiskAccepted('false');
|
||||
setVegaWalletDialog(false);
|
||||
}}
|
||||
/>
|
||||
|
||||
<VegaManageDialog
|
||||
dialogOpen={appState.vegaWalletManageOverlay}
|
||||
setDialogOpen={(open) =>
|
||||
@@ -29,8 +43,6 @@ export const VegaWalletDialogs = () => {
|
||||
})
|
||||
}
|
||||
/>
|
||||
|
||||
<ViewAsDialog connector={connectors.view} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -4,14 +4,13 @@ import keyBy from 'lodash/keyBy';
|
||||
import uniq from 'lodash/uniq';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ENV } from '../../config';
|
||||
|
||||
import noIcon from '../../images/token-no-icon.png';
|
||||
import vegaBlack from '../../images/vega_black.png';
|
||||
import vegaVesting from '../../images/vega_vesting.png';
|
||||
import { BigNumber } from '../../lib/bignumber';
|
||||
import { type WalletCardAssetProps } from '../wallet-card';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet-react';
|
||||
import { useContracts } from '../../contexts/contracts/contracts-context';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import {
|
||||
@@ -37,7 +36,6 @@ export const usePollForDelegations = () => {
|
||||
const { t } = useTranslation();
|
||||
const { pubKey } = useVegaWallet();
|
||||
const client = useApolloClient();
|
||||
const { delegationsPagination } = ENV;
|
||||
const [delegations, setDelegations] = React.useState<
|
||||
WalletDelegationFieldsFragment[]
|
||||
>([]);
|
||||
@@ -68,11 +66,9 @@ export const usePollForDelegations = () => {
|
||||
query: DelegationsDocument,
|
||||
variables: {
|
||||
partyId: pubKey,
|
||||
delegationsPagination: delegationsPagination
|
||||
? {
|
||||
first: Number(delegationsPagination),
|
||||
}
|
||||
: undefined,
|
||||
delegationsPagination: {
|
||||
first: 50,
|
||||
},
|
||||
},
|
||||
fetchPolicy: 'network-only',
|
||||
})
|
||||
@@ -236,14 +232,14 @@ export const usePollForDelegations = () => {
|
||||
// will just continue to fail
|
||||
clearInterval(interval);
|
||||
});
|
||||
}, 1000);
|
||||
}, 20000);
|
||||
}
|
||||
|
||||
return () => {
|
||||
clearInterval(interval);
|
||||
mounted = false;
|
||||
};
|
||||
}, [delegationsPagination, client, decimals, pubKey, t, vegaToken.address]);
|
||||
}, [client, decimals, pubKey, t, vegaToken.address]);
|
||||
|
||||
return { delegations, currentStakeAvailable, delegatedNodes, accounts };
|
||||
};
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { ButtonLink, Link } from '@vegaprotocol/ui-toolkit';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ExternalLinks } from '@vegaprotocol/environment';
|
||||
import { useViewAsDialog } from '@vegaprotocol/wallet';
|
||||
import { useConnect } from '@vegaprotocol/wallet-react';
|
||||
|
||||
export const VegaWalletPrompt = () => {
|
||||
const { t } = useTranslation();
|
||||
const setViewAsDialog = useViewAsDialog((state) => state.setOpen);
|
||||
const { connect } = useConnect();
|
||||
return (
|
||||
<>
|
||||
<h3 className="mt-4 mb-2">{t('getWallet')}</h3>
|
||||
@@ -16,7 +16,7 @@ export const VegaWalletPrompt = () => {
|
||||
<ButtonLink
|
||||
className="text-neutral-500"
|
||||
data-testid="view-as-user"
|
||||
onClick={() => setViewAsDialog(true)}
|
||||
onClick={() => connect('viewParty')}
|
||||
>
|
||||
{t('viewAsParty')}
|
||||
</ButtonLink>
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
} from '../wallet-card';
|
||||
import { VegaWalletPrompt } from './vega-wallet-prompt';
|
||||
import { usePollForDelegations } from './hooks';
|
||||
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
|
||||
import { useVegaWallet, useDialogStore } from '@vegaprotocol/wallet-react';
|
||||
import { Button, ButtonLink } from '@vegaprotocol/ui-toolkit';
|
||||
import { toBigNum } from '@vegaprotocol/utils';
|
||||
import { usePendingBalancesStore } from '../../hooks/use-pending-balances-manager';
|
||||
@@ -34,16 +34,17 @@ import omit from 'lodash/omit';
|
||||
|
||||
export const VegaWallet = () => {
|
||||
const { t } = useTranslation();
|
||||
const { pubKey, pubKeys } = useVegaWallet();
|
||||
const { status, pubKey, pubKeys } = useVegaWallet();
|
||||
const pubKeyObj = useMemo(() => {
|
||||
return pubKeys?.find((pk) => pk.publicKey === pubKey);
|
||||
}, [pubKey, pubKeys]);
|
||||
|
||||
const child = !pubKeys ? (
|
||||
<VegaWalletNotConnected />
|
||||
) : (
|
||||
<VegaWalletConnected vegaKeys={pubKeys.map((pk) => pk.publicKey)} />
|
||||
);
|
||||
const child =
|
||||
status === 'connected' ? (
|
||||
<VegaWalletConnected vegaKeys={pubKeys.map((pk) => pk.publicKey)} />
|
||||
) : (
|
||||
<VegaWalletNotConnected />
|
||||
);
|
||||
|
||||
return (
|
||||
<section className="vega-wallet" data-testid="vega-wallet">
|
||||
@@ -75,9 +76,7 @@ export const VegaWallet = () => {
|
||||
|
||||
const VegaWalletNotConnected = () => {
|
||||
const { t } = useTranslation();
|
||||
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
|
||||
openVegaWalletDialog: store.openVegaWalletDialog,
|
||||
}));
|
||||
const openVegaWalletDialog = useDialogStore((store) => store.open);
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
|
||||
@@ -65,7 +65,6 @@ export const ENV = {
|
||||
docsUrl: windowOrDefault('NX_VEGA_DOCS_URL'),
|
||||
ethWalletMnemonic: windowOrDefault('NX_ETH_WALLET_MNEMONIC'),
|
||||
localProviderUrl: windowOrDefault('NX_LOCAL_PROVIDER_URL'),
|
||||
delegationsPagination: windowOrDefault('NX_DELEGATIONS_PAGINATION'),
|
||||
rest: windowOrDefault('NX_VEGA_REST_URL'),
|
||||
addresses:
|
||||
ContractAddresses[(envName === 'local' ? 'CUSTOM' : envName) as Networks],
|
||||
|
||||
@@ -7,7 +7,7 @@ import { useWeb3React } from '@web3-react/core';
|
||||
|
||||
export const useListenForStakingEvents = (
|
||||
contract: Contract | undefined,
|
||||
vegaPublicKey: string | null,
|
||||
vegaPublicKey: string | undefined,
|
||||
numberOfConfirmations: number
|
||||
) => {
|
||||
const { account } = useWeb3React();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { toBigNum } from '@vegaprotocol/utils';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet-react';
|
||||
import { useEthereumConfig } from '@vegaprotocol/web3';
|
||||
import React from 'react';
|
||||
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
import { useEffect } from 'react';
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
||||
import { TELEMETRY_ON } from '../components/telemetry-dialog/telemetry-dialog';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import { ENV } from '../config';
|
||||
import { isPartyNotFoundError } from '../lib/party';
|
||||
|
||||
export const useSentryInit = () => {
|
||||
const { VEGA_ENV, GIT_COMMIT_HASH, GIT_BRANCH } = useEnvironment();
|
||||
const [telemetryOn] = useLocalStorage(TELEMETRY_ON);
|
||||
|
||||
useEffect(() => {
|
||||
if (ENV.dsn && telemetryOn === 'true') {
|
||||
Sentry.init({
|
||||
dsn: ENV.dsn,
|
||||
tracesSampleRate: 0.1,
|
||||
enabled: true,
|
||||
environment: VEGA_ENV,
|
||||
release: GIT_COMMIT_HASH,
|
||||
beforeSend(event, hint) {
|
||||
const error = hint?.originalException;
|
||||
const errorIsString = typeof error === 'string';
|
||||
const errorIsObject = error instanceof Error;
|
||||
const requestUrl = event.request?.url;
|
||||
const transaction = event.transaction;
|
||||
|
||||
if (
|
||||
(errorIsString && isPartyNotFoundError({ message: error })) ||
|
||||
(errorIsObject && isPartyNotFoundError(error))
|
||||
) {
|
||||
// This error is caused by a pubkey making an API request before
|
||||
// it has interacted with the chain. This isn't needed in Sentry.
|
||||
return null;
|
||||
}
|
||||
|
||||
const updatedRequest =
|
||||
requestUrl && requestUrl.includes('/claim?')
|
||||
? { ...event.request, url: removeQueryParams(requestUrl) }
|
||||
: event.request;
|
||||
|
||||
const updatedTransaction =
|
||||
transaction && transaction.includes('/claim?')
|
||||
? removeQueryParams(transaction)
|
||||
: transaction;
|
||||
|
||||
const updatedBreadcrumbs = event.breadcrumbs?.map((breadcrumb) => {
|
||||
if (
|
||||
breadcrumb.type === 'navigation' &&
|
||||
breadcrumb.data?.to?.includes('/claim?')
|
||||
) {
|
||||
return {
|
||||
...breadcrumb,
|
||||
data: {
|
||||
...breadcrumb.data,
|
||||
to: removeQueryParams(breadcrumb.data.to),
|
||||
},
|
||||
};
|
||||
}
|
||||
return breadcrumb;
|
||||
});
|
||||
|
||||
return {
|
||||
...event,
|
||||
request: updatedRequest,
|
||||
transaction: updatedTransaction,
|
||||
breadcrumbs: updatedBreadcrumbs ?? event.breadcrumbs,
|
||||
};
|
||||
},
|
||||
});
|
||||
Sentry.setTag('branch', GIT_BRANCH);
|
||||
Sentry.setTag('commit', GIT_COMMIT_HASH);
|
||||
} else {
|
||||
Sentry.close();
|
||||
}
|
||||
}, [GIT_COMMIT_HASH, GIT_BRANCH, VEGA_ENV, telemetryOn]);
|
||||
};
|
||||
|
||||
const removeQueryParams = (url: string) => {
|
||||
return url.split('?')[0];
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
import { useMemo } from 'react';
|
||||
import {
|
||||
InjectedConnector,
|
||||
JsonRpcConnector,
|
||||
SnapConnector,
|
||||
ViewPartyConnector,
|
||||
createConfig,
|
||||
fairground,
|
||||
stagnet,
|
||||
mainnet,
|
||||
} from '@vegaprotocol/wallet';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
|
||||
export const useVegaWalletConfig = () => {
|
||||
const { VEGA_ENV, VEGA_URL, VEGA_WALLET_URL } = useEnvironment();
|
||||
return useMemo(() => {
|
||||
if (!VEGA_ENV || !VEGA_URL || !VEGA_WALLET_URL) return;
|
||||
|
||||
const injected = new InjectedConnector();
|
||||
|
||||
const jsonRpc = new JsonRpcConnector({
|
||||
url: VEGA_WALLET_URL,
|
||||
});
|
||||
|
||||
const snap = new SnapConnector({
|
||||
node: new URL(VEGA_URL).origin,
|
||||
snapId: 'npm:@vegaprotocol/snap',
|
||||
version: '1.0.1',
|
||||
});
|
||||
|
||||
const viewParty = new ViewPartyConnector();
|
||||
|
||||
const config = createConfig({
|
||||
chains: [mainnet, fairground, stagnet],
|
||||
defaultChainId: fairground.id,
|
||||
connectors: [injected, snap, jsonRpc, viewParty],
|
||||
});
|
||||
|
||||
return config;
|
||||
}, [VEGA_ENV, VEGA_URL, VEGA_WALLET_URL]);
|
||||
};
|
||||
@@ -31,7 +31,7 @@ i18n
|
||||
load: 'languageOnly',
|
||||
debug: isInDev,
|
||||
// have a common namespace used around the full app
|
||||
ns: ['governance'],
|
||||
ns: ['governance', 'wallet', 'wallet-react'],
|
||||
defaultNS: 'governance',
|
||||
keySeparator: false, // we use content as keys
|
||||
nsSeparator: false,
|
||||
|
||||
@@ -20,6 +20,7 @@ describe('getMultisigStatus', () => {
|
||||
expect(result).toEqual({
|
||||
multisigStatus: MultisigStatus.noNodes,
|
||||
showMultisigStatusError: true,
|
||||
zeroScoreNodes: [],
|
||||
});
|
||||
});
|
||||
|
||||
@@ -35,6 +36,7 @@ describe('getMultisigStatus', () => {
|
||||
expect(result).toEqual({
|
||||
multisigStatus: MultisigStatus.correct,
|
||||
showMultisigStatusError: false,
|
||||
zeroScoreNodes: [],
|
||||
});
|
||||
});
|
||||
|
||||
@@ -50,6 +52,22 @@ describe('getMultisigStatus', () => {
|
||||
expect(result).toEqual({
|
||||
multisigStatus: MultisigStatus.nodeNeedsRemoving,
|
||||
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({
|
||||
multisigStatus: MultisigStatus.nodeNeedsAdding,
|
||||
showMultisigStatusError: true,
|
||||
zeroScoreNodes: [
|
||||
{
|
||||
id: '1',
|
||||
rewardScore: {
|
||||
multisigScore: '0',
|
||||
},
|
||||
stakedTotal: '1000',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
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 {
|
||||
'correct' = 'correct',
|
||||
@@ -17,12 +20,15 @@ export const getMultisigStatusInfo = (
|
||||
previousEpochData?.epoch.validatorsConnection?.edges
|
||||
);
|
||||
|
||||
const hasZero = allNodesInPreviousEpoch.some(
|
||||
(node) => Number(node?.rewardScore?.multisigScore) === 0
|
||||
);
|
||||
const hasOne = allNodesInPreviousEpoch.some(
|
||||
(node) => Number(node?.rewardScore?.multisigScore) === 1
|
||||
);
|
||||
const zeroScore = (node: ValidatorNodeFragment) =>
|
||||
Number(node.rewardScore?.multisigScore) === 0;
|
||||
const oneScore = (node: ValidatorNodeFragment) =>
|
||||
Number(node.rewardScore?.multisigScore) === 1;
|
||||
|
||||
const hasZero = allNodesInPreviousEpoch.some(zeroScore);
|
||||
const hasOne = allNodesInPreviousEpoch.some(oneScore);
|
||||
|
||||
const zeroScoreNodes = allNodesInPreviousEpoch.filter(zeroScore);
|
||||
|
||||
if (hasZero && hasOne) {
|
||||
// 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 {
|
||||
showMultisigStatusError: status !== MultisigStatus.correct,
|
||||
multisigStatus: status,
|
||||
zeroScoreNodes,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
import { useMemo } from 'react';
|
||||
import {
|
||||
JsonRpcConnector,
|
||||
ViewConnector,
|
||||
InjectedConnector,
|
||||
SnapConnector,
|
||||
DEFAULT_SNAP_ID,
|
||||
} from '@vegaprotocol/wallet';
|
||||
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
|
||||
export const jsonRpc = new JsonRpcConnector();
|
||||
export const injected = new InjectedConnector();
|
||||
export const view = new ViewConnector(urlParams.get('address'));
|
||||
|
||||
export const snap = new SnapConnector(DEFAULT_SNAP_ID);
|
||||
|
||||
export const useConnectors = () => {
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
return useMemo(
|
||||
() => ({
|
||||
injected,
|
||||
jsonRpc,
|
||||
view,
|
||||
snap: featureFlags.METAMASK_SNAPS ? snap : undefined,
|
||||
}),
|
||||
[featureFlags.METAMASK_SNAPS]
|
||||
);
|
||||
};
|
||||
@@ -1,3 +1,4 @@
|
||||
import compact from 'lodash/compact';
|
||||
import { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link } from 'react-router-dom';
|
||||
@@ -12,10 +13,10 @@ import { useRefreshAfterEpoch } from '../../hooks/use-refresh-after-epoch';
|
||||
import { ProposalsListItem } from '../proposals/components/proposals-list-item';
|
||||
import { ProtocolUpgradeProposalsListItem } from '../proposals/components/protocol-upgrade-proposals-list-item/protocol-upgrade-proposals-list-item';
|
||||
import Routes from '../routes';
|
||||
import { ExternalLinks, useFeatureFlags } from '@vegaprotocol/environment';
|
||||
import { ExternalLinks } from '@vegaprotocol/environment';
|
||||
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
||||
import { useNodesQuery } from '../staking/home/__generated__/Nodes';
|
||||
import { useProposalsQuery } from '../proposals/proposals/__generated__/Proposals';
|
||||
import { useProposalsQuery } from '../proposals/__generated__/Proposals';
|
||||
import {
|
||||
getNotRejectedProposals,
|
||||
getNotRejectedProtocolUpgradeProposals,
|
||||
@@ -31,7 +32,7 @@ import {
|
||||
orderByUpgradeBlockHeight,
|
||||
} from '../proposals/components/proposals-list/proposals-list';
|
||||
import { BigNumber } from '../../lib/bignumber';
|
||||
import { type Proposal } from '../proposals/types';
|
||||
import { type Proposal, type BatchProposal } from '../proposals/types';
|
||||
|
||||
const nodesToShow = 6;
|
||||
|
||||
@@ -39,7 +40,7 @@ const HomeProposals = ({
|
||||
proposals,
|
||||
protocolUpgradeProposals,
|
||||
}: {
|
||||
proposals: Proposal[];
|
||||
proposals: Array<Proposal | BatchProposal>;
|
||||
protocolUpgradeProposals: ProtocolUpgradeProposalFieldsFragment[];
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
@@ -60,12 +61,9 @@ const HomeProposals = ({
|
||||
<ProtocolUpgradeProposalsListItem key={index} proposal={proposal} />
|
||||
))}
|
||||
|
||||
{proposals.map(
|
||||
(proposal) =>
|
||||
proposal?.id && (
|
||||
<ProposalsListItem key={proposal.id} proposal={proposal} />
|
||||
)
|
||||
)}
|
||||
{compact(proposals).map((proposal) => {
|
||||
return <ProposalsListItem key={proposal.id} proposal={proposal} />;
|
||||
})}
|
||||
</ul>
|
||||
|
||||
<div className="mt-6">
|
||||
@@ -175,7 +173,6 @@ export const ValidatorDetailsLink = ({
|
||||
};
|
||||
|
||||
const GovernanceHome = ({ name }: RouteChildProps) => {
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
useDocumentTitle(name);
|
||||
const { t } = useTranslation();
|
||||
const {
|
||||
@@ -186,11 +183,6 @@ const GovernanceHome = ({ name }: RouteChildProps) => {
|
||||
pollInterval: 5000,
|
||||
fetchPolicy: 'network-only',
|
||||
errorPolicy: 'ignore',
|
||||
variables: {
|
||||
includeNewMarketProductFields: !!featureFlags.PRODUCT_PERPETUALS,
|
||||
includeUpdateMarketStates: !!featureFlags.UPDATE_MARKET_STATE,
|
||||
includeUpdateReferralPrograms: !!featureFlags.REFERRALS,
|
||||
},
|
||||
});
|
||||
|
||||
const {
|
||||
@@ -212,15 +204,18 @@ const GovernanceHome = ({ name }: RouteChildProps) => {
|
||||
|
||||
useRefreshAfterEpoch(validatorsData?.epoch.timestamps.expiry, refetch);
|
||||
|
||||
const proposals = useMemo(
|
||||
() =>
|
||||
proposalsData
|
||||
? getNotRejectedProposals(
|
||||
removePaginationWrapper(proposalsData.proposalsConnection?.edges)
|
||||
const proposals = useMemo(() => {
|
||||
if (!proposalsData?.proposalsConnection?.edges?.length) return [];
|
||||
return proposalsData
|
||||
? getNotRejectedProposals(
|
||||
compact(
|
||||
proposalsData.proposalsConnection.edges.map(
|
||||
(edge) => edge?.proposalNode
|
||||
)
|
||||
)
|
||||
: [],
|
||||
[proposalsData]
|
||||
);
|
||||
)
|
||||
: [];
|
||||
}, [proposalsData]);
|
||||
|
||||
const sortedProposals = useMemo(
|
||||
() => orderByDate(proposals).reverse(),
|
||||
|
||||
@@ -0,0 +1,493 @@
|
||||
fragment UpdateMarketStates on UpdateMarketState {
|
||||
__typename
|
||||
updateType
|
||||
market {
|
||||
decimalPlaces
|
||||
id
|
||||
tradableInstrument {
|
||||
instrument {
|
||||
product {
|
||||
__typename
|
||||
... on Future {
|
||||
quoteName
|
||||
}
|
||||
... on Perpetual {
|
||||
quoteName
|
||||
}
|
||||
}
|
||||
name
|
||||
code
|
||||
}
|
||||
}
|
||||
}
|
||||
updateType
|
||||
price
|
||||
}
|
||||
|
||||
fragment UpdateReferralPrograms on UpdateReferralProgram {
|
||||
__typename
|
||||
benefitTiers {
|
||||
minimumEpochs
|
||||
minimumRunningNotionalTakerVolume
|
||||
referralDiscountFactor
|
||||
referralRewardFactor
|
||||
}
|
||||
endOfProgram: endOfProgramTimestamp
|
||||
windowLength
|
||||
stakingTiers {
|
||||
minimumStakedTokens
|
||||
referralRewardMultiplier
|
||||
}
|
||||
}
|
||||
|
||||
fragment UpdateVolumeDiscountPrograms on UpdateVolumeDiscountProgram {
|
||||
__typename
|
||||
benefitTiers {
|
||||
minimumRunningNotionalTakerVolume
|
||||
volumeDiscountFactor
|
||||
}
|
||||
endOfProgramTimestamp
|
||||
windowLength
|
||||
}
|
||||
|
||||
# I prefix due to clash in libs/proposals
|
||||
fragment IUpdateMarketFields on UpdateMarket {
|
||||
__typename
|
||||
marketId
|
||||
updateMarketConfiguration {
|
||||
instrument {
|
||||
code
|
||||
product {
|
||||
... on UpdateFutureProduct {
|
||||
quoteName
|
||||
dataSourceSpecForSettlementData {
|
||||
sourceType {
|
||||
... on DataSourceDefinitionInternal {
|
||||
sourceType {
|
||||
... on DataSourceSpecConfigurationTime {
|
||||
conditions {
|
||||
operator
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
... on DataSourceDefinitionExternal {
|
||||
sourceType {
|
||||
... on DataSourceSpecConfiguration {
|
||||
signers {
|
||||
signer {
|
||||
... on PubKey {
|
||||
key
|
||||
}
|
||||
... on ETHAddress {
|
||||
address
|
||||
}
|
||||
}
|
||||
}
|
||||
filters {
|
||||
key {
|
||||
name
|
||||
type
|
||||
}
|
||||
conditions {
|
||||
operator
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
# dataSourceSpecForTradingTermination {
|
||||
# sourceType {
|
||||
# ... on DataSourceDefinitionInternal {
|
||||
# sourceType {
|
||||
# ... on DataSourceSpecConfigurationTime {
|
||||
# conditions {
|
||||
# operator
|
||||
# value
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# ... on DataSourceDefinitionExternal {
|
||||
# sourceType {
|
||||
# ... on DataSourceSpecConfiguration {
|
||||
# signers {
|
||||
# signer {
|
||||
# ... on PubKey {
|
||||
# key
|
||||
# }
|
||||
# ... on ETHAddress {
|
||||
# address
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# filters {
|
||||
# key {
|
||||
# name
|
||||
# type
|
||||
# }
|
||||
# conditions {
|
||||
# operator
|
||||
# value
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
dataSourceSpecBinding {
|
||||
settlementDataProperty
|
||||
tradingTerminationProperty
|
||||
}
|
||||
}
|
||||
... on UpdatePerpetualProduct {
|
||||
quoteName
|
||||
dataSourceSpecForSettlementData {
|
||||
sourceType {
|
||||
... on DataSourceDefinitionInternal {
|
||||
sourceType {
|
||||
... on DataSourceSpecConfigurationTime {
|
||||
conditions {
|
||||
operator
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
... on DataSourceDefinitionExternal {
|
||||
sourceType {
|
||||
... on DataSourceSpecConfiguration {
|
||||
signers {
|
||||
signer {
|
||||
... on PubKey {
|
||||
key
|
||||
}
|
||||
... on ETHAddress {
|
||||
address
|
||||
}
|
||||
}
|
||||
}
|
||||
filters {
|
||||
key {
|
||||
name
|
||||
type
|
||||
}
|
||||
conditions {
|
||||
operator
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dataSourceSpecBinding {
|
||||
settlementDataProperty
|
||||
settlementScheduleProperty
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
metadata
|
||||
priceMonitoringParameters {
|
||||
triggers {
|
||||
horizonSecs
|
||||
probability
|
||||
auctionExtensionSecs
|
||||
}
|
||||
}
|
||||
liquidityMonitoringParameters {
|
||||
targetStakeParameters {
|
||||
timeWindow
|
||||
scalingFactor
|
||||
}
|
||||
}
|
||||
riskParameters {
|
||||
... on UpdateMarketSimpleRiskModel {
|
||||
simple {
|
||||
factorLong
|
||||
factorShort
|
||||
}
|
||||
}
|
||||
... on UpdateMarketLogNormalRiskModel {
|
||||
logNormal {
|
||||
riskAversionParameter
|
||||
tau
|
||||
params {
|
||||
r
|
||||
sigma
|
||||
mu
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# I prefix due to clash in libs/proposals
|
||||
fragment INewMarketFields on NewMarket {
|
||||
__typename
|
||||
decimalPlaces
|
||||
metadata
|
||||
riskParameters {
|
||||
... on LogNormalRiskModel {
|
||||
riskAversionParameter
|
||||
tau
|
||||
params {
|
||||
mu
|
||||
r
|
||||
sigma
|
||||
}
|
||||
}
|
||||
... on SimpleRiskModel {
|
||||
params {
|
||||
factorLong
|
||||
factorShort
|
||||
}
|
||||
}
|
||||
}
|
||||
successorConfiguration {
|
||||
parentMarketId
|
||||
}
|
||||
instrument {
|
||||
name
|
||||
code
|
||||
product {
|
||||
... on FutureProduct {
|
||||
settlementAsset {
|
||||
id
|
||||
name
|
||||
symbol
|
||||
decimals
|
||||
quantum
|
||||
}
|
||||
quoteName
|
||||
dataSourceSpecBinding {
|
||||
settlementDataProperty
|
||||
tradingTerminationProperty
|
||||
}
|
||||
dataSourceSpecForSettlementData {
|
||||
sourceType {
|
||||
... on DataSourceDefinitionInternal {
|
||||
sourceType {
|
||||
... on DataSourceSpecConfigurationTime {
|
||||
conditions {
|
||||
operator
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
... on DataSourceDefinitionExternal {
|
||||
sourceType {
|
||||
... on DataSourceSpecConfiguration {
|
||||
signers {
|
||||
signer {
|
||||
... on PubKey {
|
||||
key
|
||||
}
|
||||
... on ETHAddress {
|
||||
address
|
||||
}
|
||||
}
|
||||
}
|
||||
filters {
|
||||
key {
|
||||
name
|
||||
type
|
||||
}
|
||||
conditions {
|
||||
operator
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
... on PerpetualProduct {
|
||||
settlementAsset {
|
||||
id
|
||||
name
|
||||
symbol
|
||||
decimals
|
||||
quantum
|
||||
}
|
||||
quoteName
|
||||
}
|
||||
}
|
||||
}
|
||||
priceMonitoringParameters {
|
||||
triggers {
|
||||
horizonSecs
|
||||
probability
|
||||
auctionExtensionSecs
|
||||
}
|
||||
}
|
||||
liquidityMonitoringParameters {
|
||||
targetStakeParameters {
|
||||
timeWindow
|
||||
scalingFactor
|
||||
}
|
||||
}
|
||||
positionDecimalPlaces
|
||||
linearSlippageFactor
|
||||
}
|
||||
|
||||
# I prefix due to clash in lib/proposals
|
||||
fragment INewAssetFields on NewAsset {
|
||||
__typename
|
||||
name
|
||||
symbol
|
||||
decimals
|
||||
quantum
|
||||
source {
|
||||
... on BuiltinAsset {
|
||||
maxFaucetAmountMint
|
||||
}
|
||||
... on ERC20 {
|
||||
contractAddress
|
||||
withdrawThreshold
|
||||
lifetimeLimit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# I prefix due to clash in libs/proposals
|
||||
fragment IUpdateAssetFields on UpdateAsset {
|
||||
__typename
|
||||
assetId
|
||||
quantum
|
||||
source {
|
||||
... on UpdateERC20 {
|
||||
lifetimeLimit
|
||||
withdrawThreshold
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# I prefix due to clash in libs/proposals
|
||||
fragment IUpdateNetworkParameterFields on UpdateNetworkParameter {
|
||||
__typename
|
||||
networkParameter {
|
||||
key
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
fragment VoteFields on ProposalVotes {
|
||||
yes {
|
||||
totalTokens
|
||||
totalNumber
|
||||
totalEquityLikeShareWeight
|
||||
}
|
||||
no {
|
||||
totalTokens
|
||||
totalNumber
|
||||
totalEquityLikeShareWeight
|
||||
}
|
||||
}
|
||||
|
||||
fragment ProposalTermsFields on ProposalTerms {
|
||||
closingDatetime
|
||||
enactmentDatetime
|
||||
change {
|
||||
__typename
|
||||
...UpdateMarketStates
|
||||
...UpdateReferralPrograms
|
||||
...UpdateVolumeDiscountPrograms
|
||||
...INewMarketFields
|
||||
...IUpdateMarketFields
|
||||
...INewAssetFields
|
||||
...IUpdateNetworkParameterFields
|
||||
...IUpdateAssetFields
|
||||
}
|
||||
}
|
||||
|
||||
fragment ProposalFields on Proposal {
|
||||
id
|
||||
rationale {
|
||||
title
|
||||
description
|
||||
}
|
||||
reference
|
||||
state
|
||||
datetime
|
||||
rejectionReason
|
||||
party {
|
||||
id
|
||||
}
|
||||
errorDetails
|
||||
terms {
|
||||
...ProposalTermsFields
|
||||
}
|
||||
votes {
|
||||
...VoteFields
|
||||
}
|
||||
}
|
||||
|
||||
fragment BatchProposalFields on BatchProposal {
|
||||
id
|
||||
rationale {
|
||||
title
|
||||
description
|
||||
}
|
||||
reference
|
||||
state
|
||||
datetime
|
||||
rejectionReason
|
||||
party {
|
||||
id
|
||||
}
|
||||
errorDetails
|
||||
batchTerms {
|
||||
closingDatetime
|
||||
changes {
|
||||
enactmentDatetime
|
||||
}
|
||||
}
|
||||
subProposals {
|
||||
datetime
|
||||
terms {
|
||||
...ProposalTermsFields
|
||||
}
|
||||
}
|
||||
votes {
|
||||
...VoteFields
|
||||
}
|
||||
}
|
||||
|
||||
query Proposals {
|
||||
proposalsConnection {
|
||||
edges {
|
||||
proposalNode {
|
||||
__typename
|
||||
... on Proposal {
|
||||
...ProposalFields
|
||||
}
|
||||
... on BatchProposal {
|
||||
...BatchProposalFields
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query Proposal($proposalId: ID!) {
|
||||
proposal(id: $proposalId) {
|
||||
... on Proposal {
|
||||
...ProposalFields
|
||||
}
|
||||
... on BatchProposal {
|
||||
...BatchProposalFields
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user