Compare commits
25 Commits
develop
...
feat/mobil
Author | SHA1 | Date | |
---|---|---|---|
|
885dc0b52b | ||
|
58df46687b | ||
|
96a07b6379 | ||
|
619b73212f | ||
|
3e2449fdea | ||
|
0351d3f478 | ||
|
5ab455159c | ||
|
5742759780 | ||
|
94c34a1d76 | ||
|
2c1a3d106c | ||
|
d41e9456bc | ||
|
0bad7e3317 | ||
|
b2d52f6f5c | ||
|
3ae2fe3e1c | ||
|
8eed5f1eb4 | ||
|
84bb32bdd7 | ||
|
87579c0c5f | ||
|
4bb50c0dab | ||
|
96b46d1533 | ||
|
19732d90e0 | ||
|
853d0654d7 | ||
|
b24b630f5f | ||
|
7777420f1f | ||
|
f5406941e7 | ||
|
18a3786c98 |
@ -4,5 +4,6 @@ tmp/*
|
|||||||
.dockerignore
|
.dockerignore
|
||||||
dockerfiles
|
dockerfiles
|
||||||
node_modules
|
node_modules
|
||||||
|
.git
|
||||||
.github
|
.github
|
||||||
.vscode
|
.vscode
|
||||||
|
3
.github/workflows/ci-cd-trigger.yml
vendored
3
.github/workflows/ci-cd-trigger.yml
vendored
@ -196,9 +196,9 @@ jobs:
|
|||||||
cypress:
|
cypress:
|
||||||
needs: [build-sources, check-e2e-needed]
|
needs: [build-sources, check-e2e-needed]
|
||||||
name: '(CI) cypress'
|
name: '(CI) cypress'
|
||||||
|
if: ${{ needs.check-e2e-needed.outputs.run-tests == 'true' }}
|
||||||
uses: ./.github/workflows/cypress-run.yml
|
uses: ./.github/workflows/cypress-run.yml
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
if: needs.check-e2e-needed.outputs.run-tests == 'true' && (contains(needs.build-sources.outputs.projects, 'governance') || contains(needs.build-sources.outputs.projects, 'explorer'))
|
|
||||||
with:
|
with:
|
||||||
projects: ${{ needs.build-sources.outputs.projects-e2e }}
|
projects: ${{ needs.build-sources.outputs.projects-e2e }}
|
||||||
tags: '@smoke'
|
tags: '@smoke'
|
||||||
@ -287,7 +287,6 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- run: |
|
- run: |
|
||||||
result="${{ needs.cypress.result }}"
|
result="${{ needs.cypress.result }}"
|
||||||
echo "Result: $result"
|
|
||||||
if [[ $result == "success" || $result == "skipped" ]]; then
|
if [[ $result == "success" || $result == "skipped" ]]; then
|
||||||
exit 0
|
exit 0
|
||||||
else
|
else
|
||||||
|
4
.github/workflows/console-test-run.yml
vendored
4
.github/workflows/console-test-run.yml
vendored
@ -10,7 +10,7 @@ on:
|
|||||||
inputs:
|
inputs:
|
||||||
console-test-branch:
|
console-test-branch:
|
||||||
type: choice
|
type: choice
|
||||||
description: 'main: v0.73.13, develop: v0.74.0'
|
description: 'main: v0.73.5, develop: v0.73.5'
|
||||||
options:
|
options:
|
||||||
- main
|
- main
|
||||||
- develop
|
- develop
|
||||||
@ -205,7 +205,7 @@ jobs:
|
|||||||
# run tests
|
# run tests
|
||||||
#----------------------------------------------
|
#----------------------------------------------
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: CONSOLE_IMAGE_NAME=ci/trading:local poetry run pytest -v --numprocesses 4 --dist loadfile --durations=45
|
run: CONSOLE_IMAGE_NAME=ci/trading:local poetry run pytest -v --numprocesses 1 --dist loadfile --durations=45
|
||||||
working-directory: apps/trading/e2e
|
working-directory: apps/trading/e2e
|
||||||
#----------------------------------------------
|
#----------------------------------------------
|
||||||
# upload traces
|
# upload traces
|
||||||
|
36
.github/workflows/cypress-live-test.yml
vendored
Normal file
36
.github/workflows/cypress-live-test.yml
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
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 }}
|
1
.github/workflows/cypress-manual-trigger.yml
vendored
1
.github/workflows/cypress-manual-trigger.yml
vendored
@ -12,6 +12,7 @@ on:
|
|||||||
options:
|
options:
|
||||||
- explorer-e2e
|
- explorer-e2e
|
||||||
- governance-e2e
|
- governance-e2e
|
||||||
|
- trading-e2e
|
||||||
tags:
|
tags:
|
||||||
description: 'Test tags to run'
|
description: 'Test tags to run'
|
||||||
required: true
|
required: true
|
||||||
|
2
.github/workflows/cypress-nightly.yml
vendored
2
.github/workflows/cypress-nightly.yml
vendored
@ -10,5 +10,5 @@ jobs:
|
|||||||
uses: ./.github/workflows/cypress-run.yml
|
uses: ./.github/workflows/cypress-run.yml
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
with:
|
with:
|
||||||
projects: '["explorer-e2e","governance-e2e"]'
|
projects: '["explorer-e2e","governance-e2e","trading-e2e"]'
|
||||||
tags: '@smoke @regression @slow'
|
tags: '@smoke @regression @slow'
|
||||||
|
4
.github/workflows/lint-pr.yml
vendored
4
.github/workflows/lint-pr.yml
vendored
@ -25,7 +25,7 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
rm package.json
|
rm package.json
|
||||||
npm install --no-save @commitlint/cli@16.3.0 @commitlint/config-conventional@18.6.1 @commitlint/config-nx-scopes@18.6.1 nx@17.1.2
|
npm install --no-save @commitlint/cli @commitlint/config-conventional @commitlint/config-nx-scopes nx
|
||||||
|
|
||||||
- name: Check PR title
|
- name: Check PR title
|
||||||
run: echo "${{ github.event.pull_request.title }}" | npx @commitlint/cli@16.3.0 --config ./commitlint.config-ci.js
|
run: echo "${{ github.event.pull_request.title }}" | npx commitlint --config ./commitlint.config-ci.js
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
# path to a directory with all packages
|
|
||||||
storage: ../tmp/local-registry/storage
|
|
||||||
|
|
||||||
# a list of other known repositories we can talk to
|
|
||||||
uplinks:
|
|
||||||
npmjs:
|
|
||||||
url: https://registry.yarnpkg.com
|
|
||||||
maxage: 60m
|
|
||||||
|
|
||||||
packages:
|
|
||||||
'**':
|
|
||||||
# give all users (including non-authenticated users) full access
|
|
||||||
# because it is a local registry
|
|
||||||
access: $all
|
|
||||||
publish: $all
|
|
||||||
unpublish: $all
|
|
||||||
|
|
||||||
# if package is not available locally, proxy requests to npm registry
|
|
||||||
proxy: npmjs
|
|
||||||
|
|
||||||
# log settings
|
|
||||||
logs:
|
|
||||||
type: stdout
|
|
||||||
format: pretty
|
|
||||||
level: warn
|
|
||||||
|
|
||||||
publish:
|
|
||||||
allow_offline: true # set offline to true to allow publish offline
|
|
@ -1,7 +1,7 @@
|
|||||||
import { getNewAssetTxBody } from '../support/governance.functions';
|
import { getNewAssetTxBody } from '../support/governance.functions';
|
||||||
|
|
||||||
context('Proposal page', { tags: '@smoke' }, function () {
|
context('Proposal page', { tags: '@smoke' }, function () {
|
||||||
describe.skip('Verify elements on page', function () {
|
describe('Verify elements on page', function () {
|
||||||
const proposalHeading = 'proposals-heading';
|
const proposalHeading = 'proposals-heading';
|
||||||
const dateTimeRegex =
|
const dateTimeRegex =
|
||||||
/(\d{1,2})\/(\d{1,2})\/(\d{4}), (\d{1,2}):(\d{1,2}):(\d{1,2})/gm;
|
/(\d{1,2})\/(\d{1,2})\/(\d{4}), (\d{1,2}):(\d{1,2}):(\d{1,2})/gm;
|
||||||
@ -24,6 +24,10 @@ context('Proposal page', { tags: '@smoke' }, function () {
|
|||||||
cy.get_element_by_col_id('title').should('have.text', proposalTitle);
|
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('type').should('have.text', 'NewMarket');
|
||||||
cy.get_element_by_col_id('state').should('have.text', 'Enacted');
|
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"]')
|
cy.get('[col-id="cDate"]')
|
||||||
.invoke('text')
|
.invoke('text')
|
||||||
.should('match', dateTimeRegex);
|
.should('match', dateTimeRegex);
|
||||||
@ -69,6 +73,10 @@ context('Proposal page', { tags: '@smoke' }, function () {
|
|||||||
'have.text',
|
'have.text',
|
||||||
'Waiting for Node Vote'
|
'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"]')
|
cy.get('[col-id="cDate"]')
|
||||||
.invoke('text')
|
.invoke('text')
|
||||||
.should('match', dateTimeRegex);
|
.should('match', dateTimeRegex);
|
||||||
|
@ -7,7 +7,6 @@ export type AssetBalanceProps = {
|
|||||||
price: string;
|
price: string;
|
||||||
showAssetLink?: boolean;
|
showAssetLink?: boolean;
|
||||||
showAssetSymbol?: boolean;
|
showAssetSymbol?: boolean;
|
||||||
rounded?: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -19,17 +18,12 @@ const AssetBalance = ({
|
|||||||
price,
|
price,
|
||||||
showAssetLink = true,
|
showAssetLink = true,
|
||||||
showAssetSymbol = false,
|
showAssetSymbol = false,
|
||||||
rounded = false,
|
|
||||||
}: AssetBalanceProps) => {
|
}: AssetBalanceProps) => {
|
||||||
const { data: asset, loading } = useAssetDataProvider(assetId);
|
const { data: asset, loading } = useAssetDataProvider(assetId);
|
||||||
|
|
||||||
const label =
|
const label =
|
||||||
!loading && asset && asset.decimals
|
!loading && asset && asset.decimals
|
||||||
? addDecimalsFixedFormatNumber(
|
? addDecimalsFixedFormatNumber(price, asset.decimals)
|
||||||
price,
|
|
||||||
asset.decimals,
|
|
||||||
rounded ? 0 : undefined
|
|
||||||
)
|
|
||||||
: price;
|
: price;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -41,7 +41,6 @@ export const Header = () => {
|
|||||||
Routes.ASSETS,
|
Routes.ASSETS,
|
||||||
Routes.MARKETS,
|
Routes.MARKETS,
|
||||||
Routes.GOVERNANCE,
|
Routes.GOVERNANCE,
|
||||||
Routes.TREASURY,
|
|
||||||
Routes.NETWORK_PARAMETERS,
|
Routes.NETWORK_PARAMETERS,
|
||||||
Routes.GENESIS,
|
Routes.GENESIS,
|
||||||
].map((n) => pages.find((r) => r.path === n))
|
].map((n) => pages.find((r) => r.path === n))
|
||||||
|
@ -32,17 +32,9 @@ export function getNameForParty(id: string, data?: ExplorerNodeNamesQuery) {
|
|||||||
export type PartyLinkProps = Partial<ComponentProps<typeof Link>> & {
|
export type PartyLinkProps = Partial<ComponentProps<typeof Link>> & {
|
||||||
id: string;
|
id: string;
|
||||||
truncate?: boolean;
|
truncate?: boolean;
|
||||||
networkLabel?: string;
|
|
||||||
truncateLength?: number;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const PartyLink = ({
|
const PartyLink = ({ id, truncate = false, ...props }: PartyLinkProps) => {
|
||||||
id,
|
|
||||||
truncate = false,
|
|
||||||
truncateLength = 4,
|
|
||||||
networkLabel = t('Network'),
|
|
||||||
...props
|
|
||||||
}: PartyLinkProps) => {
|
|
||||||
const { data } = useExplorerNodeNamesQuery();
|
const { data } = useExplorerNodeNamesQuery();
|
||||||
const name = useMemo(() => getNameForParty(id, data), [data, id]);
|
const name = useMemo(() => getNameForParty(id, data), [data, id]);
|
||||||
const useName = name !== id;
|
const useName = name !== id;
|
||||||
@ -52,7 +44,7 @@ const PartyLink = ({
|
|||||||
if (id === SPECIAL_CASE_NETWORK || id === SPECIAL_CASE_NETWORK_ID) {
|
if (id === SPECIAL_CASE_NETWORK || id === SPECIAL_CASE_NETWORK_ID) {
|
||||||
return (
|
return (
|
||||||
<span className="font-mono" data-testid="network">
|
<span className="font-mono" data-testid="network">
|
||||||
{networkLabel}
|
{t('Network')}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -78,11 +70,7 @@ const PartyLink = ({
|
|||||||
{useName ? (
|
{useName ? (
|
||||||
name
|
name
|
||||||
) : (
|
) : (
|
||||||
<Hash
|
<Hash text={truncate ? truncateMiddle(id, 4, 4) : id} />
|
||||||
text={
|
|
||||||
truncate ? truncateMiddle(id, truncateLength, truncateLength) : id
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</Link>
|
</Link>
|
||||||
</span>
|
</span>
|
||||||
|
@ -175,7 +175,6 @@ describe('Amend order details', () => {
|
|||||||
|
|
||||||
const res = renderExistingAmend('123', 1, amend);
|
const res = renderExistingAmend('123', 1, amend);
|
||||||
expect(await res.findByText('New size')).toBeInTheDocument();
|
expect(await res.findByText('New size')).toBeInTheDocument();
|
||||||
expect(await res.findByText('Size ±')).toBeInTheDocument();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Renders Reference if provided', async () => {
|
it('Renders Reference if provided', async () => {
|
||||||
|
@ -82,7 +82,7 @@ const AmendOrderDetails = ({ id, version, amend }: AmendOrderDetailsProps) => {
|
|||||||
{amend.sizeDelta && amend.sizeDelta !== '0' ? (
|
{amend.sizeDelta && amend.sizeDelta !== '0' ? (
|
||||||
<div className="mb-12 md:mb-0">
|
<div className="mb-12 md:mb-0">
|
||||||
<h2 className="text-dark mb-4 text-2xl font-bold">
|
<h2 className="text-dark mb-4 text-2xl font-bold">
|
||||||
{t('Size ±')}
|
{t('New size')}
|
||||||
</h2>
|
</h2>
|
||||||
<h5
|
<h5
|
||||||
className={`mb-0 text-lg font-medium capitalize text-gray-500 ${getSideDeltaColour(
|
className={`mb-0 text-lg font-medium capitalize text-gray-500 ${getSideDeltaColour(
|
||||||
@ -93,16 +93,6 @@ const AmendOrderDetails = ({ id, version, amend }: AmendOrderDetailsProps) => {
|
|||||||
</h5>
|
</h5>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
{o && (
|
|
||||||
<div className="">
|
|
||||||
<h2 className="text-dark mb-4 text-2xl font-bold">
|
|
||||||
{t('New size')}
|
|
||||||
</h2>
|
|
||||||
<h5 className="mb-0 text-lg font-medium text-gray-500">
|
|
||||||
{o ? o.size : null}
|
|
||||||
</h5>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{amend.price && amend.price !== '0' ? (
|
{amend.price && amend.price !== '0' ? (
|
||||||
<div className="">
|
<div className="">
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { type ProposalListFieldsFragment } from '@vegaprotocol/proposals';
|
import { type ProposalListFieldsFragment } from '@vegaprotocol/proposals';
|
||||||
|
import { VoteProgress } from '@vegaprotocol/proposals';
|
||||||
import { type AgGridReact } from 'ag-grid-react';
|
import { type AgGridReact } from 'ag-grid-react';
|
||||||
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||||
import { AgGrid } from '@vegaprotocol/datagrid';
|
import { AgGrid } from '@vegaprotocol/datagrid';
|
||||||
@ -11,7 +12,12 @@ import { type ColDef } from 'ag-grid-community';
|
|||||||
import type { RowClickedEvent } from 'ag-grid-community';
|
import type { RowClickedEvent } from 'ag-grid-community';
|
||||||
import { getDateTimeFormat } from '@vegaprotocol/utils';
|
import { getDateTimeFormat } from '@vegaprotocol/utils';
|
||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
import {
|
||||||
|
NetworkParams,
|
||||||
|
useNetworkParams,
|
||||||
|
} from '@vegaprotocol/network-parameters';
|
||||||
import { ProposalStateMapping } from '@vegaprotocol/types';
|
import { ProposalStateMapping } from '@vegaprotocol/types';
|
||||||
|
import BigNumber from 'bignumber.js';
|
||||||
import { DApp, TOKEN_PROPOSAL, useLinks } from '@vegaprotocol/environment';
|
import { DApp, TOKEN_PROPOSAL, useLinks } from '@vegaprotocol/environment';
|
||||||
import { BREAKPOINT_MD } from '../../config/breakpoints';
|
import { BREAKPOINT_MD } from '../../config/breakpoints';
|
||||||
import { JsonViewerDialog } from '../dialogs/json-viewer-dialog';
|
import { JsonViewerDialog } from '../dialogs/json-viewer-dialog';
|
||||||
@ -25,7 +31,15 @@ type ProposalsTableProps = {
|
|||||||
data: ProposalListFieldsFragment[] | null;
|
data: ProposalListFieldsFragment[] | null;
|
||||||
};
|
};
|
||||||
export const ProposalsTable = ({ data }: ProposalsTableProps) => {
|
export const ProposalsTable = ({ data }: ProposalsTableProps) => {
|
||||||
|
const { params } = useNetworkParams([
|
||||||
|
NetworkParams.governance_proposal_market_requiredMajority,
|
||||||
|
]);
|
||||||
const tokenLink = useLinks(DApp.Governance);
|
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);
|
const gridRef = useRef<AgGridReact>(null);
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
@ -76,6 +90,33 @@ export const ProposalsTable = ({ data }: ProposalsTableProps) => {
|
|||||||
return value ? ProposalStateMapping[value] : '-';
|
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',
|
colId: 'cDate',
|
||||||
maxWidth: 150,
|
maxWidth: 150,
|
||||||
@ -143,7 +184,7 @@ export const ProposalsTable = ({ data }: ProposalsTableProps) => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[tokenLink]
|
[requiredMajorityPercentage, tokenLink]
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -64,9 +64,7 @@ export const ProposalSummary = ({
|
|||||||
return (
|
return (
|
||||||
<div className="w-auto max-w-lg border-2 border-solid border-vega-light-100 dark:border-vega-dark-200 p-5">
|
<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} />}
|
{id && <ProposalStatusIcon id={id} />}
|
||||||
{rationale?.title && (
|
{rationale?.title && <h1 className="text-xl pb-1">{rationale.title}</h1>}
|
||||||
<h1 className="text-xl pb-1 break-all">{rationale.title}</h1>
|
|
||||||
)}
|
|
||||||
{rationale?.description && (
|
{rationale?.description && (
|
||||||
<div className="pt-2 text-sm leading-tight">
|
<div className="pt-2 text-sm leading-tight">
|
||||||
<ReactMarkdown
|
<ReactMarkdown
|
||||||
|
@ -1,46 +0,0 @@
|
|||||||
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>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,18 +0,0 @@
|
|||||||
query ExplorerTransferStatus($id: ID!) {
|
|
||||||
transfer(id: $id) {
|
|
||||||
transfer {
|
|
||||||
reference
|
|
||||||
timestamp
|
|
||||||
status
|
|
||||||
reason
|
|
||||||
fromAccountType
|
|
||||||
from
|
|
||||||
to
|
|
||||||
toAccountType
|
|
||||||
asset {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
amount
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
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>;
|
|
@ -111,7 +111,7 @@ export function TransferParticipants({
|
|||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
viewBox="0 0 16 9"
|
viewBox="0 0 16 9"
|
||||||
className="fill-white dark:fill-black"
|
className="fill-vega-light-100 dark:fill-black"
|
||||||
>
|
>
|
||||||
<path d="M0,0L8,9l8,-9Z" />
|
<path d="M0,0L8,9l8,-9Z" />
|
||||||
</svg>
|
</svg>
|
||||||
@ -120,7 +120,7 @@ export function TransferParticipants({
|
|||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
viewBox="0 0 16 9"
|
viewBox="0 0 16 9"
|
||||||
className="fill-vega-light-200 dark:fill-vega-dark-200"
|
className="fill-vega-light-100 dark:fill-vega-dark-200"
|
||||||
>
|
>
|
||||||
<path d="M0,0L8,9l8,-9Z" />
|
<path d="M0,0L8,9l8,-9Z" />
|
||||||
</svg>
|
</svg>
|
||||||
|
@ -1,223 +1,97 @@
|
|||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import { AssetLink, MarketLink } from '../../../../links';
|
import { AssetLink, MarketLink } from '../../../../links';
|
||||||
|
import { headerClasses, wrapperClasses } from '../transfer-details';
|
||||||
import type { components } from '../../../../../../types/explorer';
|
import type { components } from '../../../../../../types/explorer';
|
||||||
import type { Recurring } from '../transfer-details';
|
import type { Recurring } from '../transfer-details';
|
||||||
import {
|
import { DispatchMetricLabels } from '@vegaprotocol/types';
|
||||||
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 Metric = components['schemas']['vegaDispatchMetric'];
|
||||||
export type Strategy = components['schemas']['vegaDispatchStrategy'];
|
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> = {
|
const metricLabels: Record<Metric, string> = {
|
||||||
DISPATCH_METRIC_UNSPECIFIED: 'Unknown metric',
|
DISPATCH_METRIC_UNSPECIFIED: 'Unknown metric',
|
||||||
...DispatchMetricLabels,
|
...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 {
|
interface TransferRewardsProps {
|
||||||
recurring: Recurring;
|
recurring: Recurring;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders recurring transfers/game details in a way that is, perhaps, easy to understand
|
* Renderer for a transfer. These can vary quite
|
||||||
|
* widely, essentially every field can be null.
|
||||||
*
|
*
|
||||||
* @param transfer A recurring transfer object
|
* @param transfer A recurring transfer object
|
||||||
*/
|
*/
|
||||||
export function TransferRewards({ recurring }: TransferRewardsProps) {
|
export function TransferRewards({ recurring }: TransferRewardsProps) {
|
||||||
|
const metric =
|
||||||
|
recurring?.dispatchStrategy?.metric || 'DISPATCH_METRIC_UNSPECIFIED';
|
||||||
|
|
||||||
if (!recurring || !recurring.dispatchStrategy) {
|
if (!recurring || !recurring.dispatchStrategy) {
|
||||||
return null;
|
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 (
|
return (
|
||||||
<div className={wrapperClasses}>
|
<div className={wrapperClasses}>
|
||||||
<h2 className={headerClasses}>{getRewardTitle(entityScope)}</h2>
|
<h2 className={headerClasses}>{t('Reward metrics')}</h2>
|
||||||
<ul className="relative block rounded-lg py-6 text-left p-6">
|
<ul className="relative block rounded-lg py-6 text-center p-6">
|
||||||
{entityScope && entityScopeIcons[entityScope] ? (
|
{recurring.dispatchStrategy.assetForMetric ? (
|
||||||
<li>
|
<li>
|
||||||
<strong>{t('Scope')}</strong>:{' '}
|
<strong>{t('Asset')}</strong>:{' '}
|
||||||
<VegaIcon name={entityScopeIcons[entityScope]} />
|
<AssetLink assetId={recurring.dispatchStrategy.assetForMetric} />
|
||||||
|
|
||||||
{individualScope ? individualScopeLabels[individualScope] : null}
|
|
||||||
{getScopeLabel(entityScope, teamScope)}
|
|
||||||
</li>
|
</li>
|
||||||
) : null}
|
) : null}
|
||||||
{recurring.dispatchStrategy &&
|
<li>
|
||||||
recurring.dispatchStrategy.assetForMetric && (
|
<strong>{t('Metric')}</strong>: {metricLabels[metric]}
|
||||||
<li>
|
</li>
|
||||||
<strong>{t('Asset for metric')}</strong>:{' '}
|
{recurring.dispatchStrategy.markets &&
|
||||||
<AssetLink assetId={recurring.dispatchStrategy.assetForMetric} />
|
recurring.dispatchStrategy.markets.length > 0 ? (
|
||||||
</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>
|
<li>
|
||||||
<strong>{t('Markets in scope')}</strong>:
|
<strong>{t('Markets in scope')}</strong>:
|
||||||
<ul className="inline-block ml-1">
|
<ul>
|
||||||
{markets.map((m) => (
|
{recurring.dispatchStrategy.markets.map((m) => (
|
||||||
<li key={m} className="inline-block mr-2">
|
<li key={m}>
|
||||||
<MarketLink id={m} />
|
<MarketLink id={m} />
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
) : null}
|
) : null}
|
||||||
|
<li>
|
||||||
{stakingRequirement && stakingRequirement !== '0' ? (
|
<strong>{t('Factor')}</strong>: {recurring.factor}
|
||||||
<li>
|
</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>
|
</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>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getScopeLabel(
|
interface TransferRecurringStrategyProps {
|
||||||
scope: components['schemas']['vegaEntityScope'] | undefined,
|
strategy: Strategy;
|
||||||
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 '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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'],
|
* Simple renderer for a dispatch strategy in a recurring transfer
|
||||||
string
|
*
|
||||||
> = {
|
* @param strategy Dispatch strategy object
|
||||||
// Unspecified and All are not rendered
|
*/
|
||||||
INDIVIDUAL_SCOPE_UNSPECIFIED: '',
|
export function TransferRecurringStrategy({
|
||||||
INDIVIDUAL_SCOPE_ALL: '',
|
strategy,
|
||||||
INDIVIDUAL_SCOPE_IN_TEAM: '(in team)',
|
}: TransferRecurringStrategyProps) {
|
||||||
INDIVIDUAL_SCOPE_NOT_IN_TEAM: '(not in team)',
|
if (!strategy) {
|
||||||
};
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{strategy.assetForMetric ? (
|
||||||
|
<li>
|
||||||
|
<strong>{t('Asset for metric')}</strong>:{' '}
|
||||||
|
<AssetLink assetId={strategy.assetForMetric} />
|
||||||
|
</li>
|
||||||
|
) : null}
|
||||||
|
<li>
|
||||||
|
<strong>{t('Metric')}</strong>: {strategy.metric}
|
||||||
|
</li>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -1,100 +0,0 @@
|
|||||||
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,15 +2,12 @@ import type { components } from '../../../../../types/explorer';
|
|||||||
import { TransferRepeat } from './blocks/transfer-repeat';
|
import { TransferRepeat } from './blocks/transfer-repeat';
|
||||||
import { TransferRewards } from './blocks/transfer-rewards';
|
import { TransferRewards } from './blocks/transfer-rewards';
|
||||||
import { TransferParticipants } from './blocks/transfer-participants';
|
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 Recurring = components['schemas']['commandsv1RecurringTransfer'];
|
||||||
export type Metric = components['schemas']['vegaDispatchMetric'];
|
export type Metric = components['schemas']['vegaDispatchMetric'];
|
||||||
|
|
||||||
export const wrapperClasses =
|
export const wrapperClasses =
|
||||||
'border border-vega-light-150 dark:border-vega-dark-200 pv-2 w-full sm:w-1/3 basis-1/3';
|
'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] ';
|
||||||
export const headerClasses =
|
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';
|
'bg-solid bg-vega-light-150 dark:bg-vega-dark-150 border-vega-light-150 text-center text-xl py-2 font-alpha calt';
|
||||||
|
|
||||||
@ -19,7 +16,6 @@ export type Transfer = components['schemas']['commandsv1Transfer'];
|
|||||||
interface TransferDetailsProps {
|
interface TransferDetailsProps {
|
||||||
transfer: Transfer;
|
transfer: Transfer;
|
||||||
from: string;
|
from: string;
|
||||||
id: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -28,24 +24,13 @@ interface TransferDetailsProps {
|
|||||||
*
|
*
|
||||||
* @param transfer A recurring transfer object
|
* @param transfer A recurring transfer object
|
||||||
*/
|
*/
|
||||||
export function TransferDetails({ transfer, from, id }: TransferDetailsProps) {
|
export function TransferDetails({ transfer, from }: TransferDetailsProps) {
|
||||||
const recurring = transfer.recurring;
|
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 (
|
return (
|
||||||
<div className="flex flex-wrap">
|
<div className="flex gap-5 flex-wrap">
|
||||||
<TransferParticipants from={from} transfer={transfer} />
|
<TransferParticipants from={from} transfer={transfer} />
|
||||||
{recurring ? <TransferRepeat recurring={transfer.recurring} /> : null}
|
{recurring ? <TransferRepeat recurring={transfer.recurring} /> : null}
|
||||||
<TransferStatusView status={status} error={error} loading={loading} />
|
|
||||||
{recurring && recurring.dispatchStrategy ? (
|
{recurring && recurring.dispatchStrategy ? (
|
||||||
<TransferRewards recurring={transfer.recurring} />
|
<TransferRewards recurring={transfer.recurring} />
|
||||||
) : null}
|
) : null}
|
||||||
|
@ -1,172 +0,0 @@
|
|||||||
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();
|
|
||||||
});
|
|
||||||
});
|
|
@ -34,7 +34,6 @@ import { TxDetailsUpdateReferralSet } from './tx-update-referral-set';
|
|||||||
import { TxDetailsJoinTeam } from './tx-join-team';
|
import { TxDetailsJoinTeam } from './tx-join-team';
|
||||||
import { TxDetailsUpdateMarginMode } from './tx-update-margin-mode';
|
import { TxDetailsUpdateMarginMode } from './tx-update-margin-mode';
|
||||||
import { TxBatchProposal } from './tx-batch-proposal';
|
import { TxBatchProposal } from './tx-batch-proposal';
|
||||||
import { TxDetailsUpdatePartyProfile } from './proposal/tx-update-party-profile';
|
|
||||||
|
|
||||||
interface TxDetailsWrapperProps {
|
interface TxDetailsWrapperProps {
|
||||||
txData: BlockExplorerTransactionResult | undefined;
|
txData: BlockExplorerTransactionResult | undefined;
|
||||||
@ -140,8 +139,6 @@ function getTransactionComponent(txData?: BlockExplorerTransactionResult) {
|
|||||||
return TxDetailsUpdateMarginMode;
|
return TxDetailsUpdateMarginMode;
|
||||||
case 'Batch Proposal':
|
case 'Batch Proposal':
|
||||||
return TxBatchProposal;
|
return TxBatchProposal;
|
||||||
case 'Update Party Profile':
|
|
||||||
return TxDetailsUpdatePartyProfile;
|
|
||||||
default:
|
default:
|
||||||
return TxDetailsGeneric;
|
return TxDetailsGeneric;
|
||||||
}
|
}
|
||||||
|
@ -12,8 +12,6 @@ import { ProposalSignatureBundleNewAsset } from './proposal/signature-bundle-new
|
|||||||
import { ProposalSignatureBundleUpdateAsset } from './proposal/signature-bundle-update';
|
import { ProposalSignatureBundleUpdateAsset } from './proposal/signature-bundle-update';
|
||||||
import { MarketLink } from '../../links';
|
import { MarketLink } from '../../links';
|
||||||
import { formatNumber } from '@vegaprotocol/utils';
|
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 Proposal = components['schemas']['v1ProposalSubmission'];
|
||||||
export type ProposalTerms = components['schemas']['vegaProposalTerms'];
|
export type ProposalTerms = components['schemas']['vegaProposalTerms'];
|
||||||
@ -106,12 +104,6 @@ export const TxProposal = ({ txData, pubKey, blockData }: TxProposalProps) => {
|
|||||||
? ProposalSignatureBundleNewAsset
|
? ProposalSignatureBundleNewAsset
|
||||||
: ProposalSignatureBundleUpdateAsset;
|
: ProposalSignatureBundleUpdateAsset;
|
||||||
|
|
||||||
let transfer, from;
|
|
||||||
if (proposal.terms?.newTransfer?.changes) {
|
|
||||||
transfer = proposalToTransfer(proposal.terms?.newTransfer.changes);
|
|
||||||
from = proposal.terms.newTransfer.changes.source;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TableWithTbody className="mb-8" allowWrap={true}>
|
<TableWithTbody className="mb-8" allowWrap={true}>
|
||||||
@ -157,26 +149,14 @@ export const TxProposal = ({ txData, pubKey, blockData }: TxProposalProps) => {
|
|||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
</TableWithTbody>
|
</TableWithTbody>
|
||||||
|
|
||||||
<ProposalSummary
|
<ProposalSummary
|
||||||
id={deterministicId}
|
id={deterministicId}
|
||||||
rationale={proposal.rationale}
|
rationale={proposal.rationale}
|
||||||
terms={proposal?.terms}
|
terms={proposal?.terms}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{proposalRequiresSignatureBundle(proposal) && (
|
{proposalRequiresSignatureBundle(proposal) && (
|
||||||
<SignatureBundleComponent id={deterministicId} tx={tx} />
|
<SignatureBundleComponent id={deterministicId} tx={tx} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{transfer && (
|
|
||||||
<div className="mt-8">
|
|
||||||
<TransferDetails
|
|
||||||
transfer={transfer}
|
|
||||||
from={from || ''}
|
|
||||||
id={deterministicId}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -13,7 +13,6 @@ import {
|
|||||||
SPECIAL_CASE_NETWORK_ID,
|
SPECIAL_CASE_NETWORK_ID,
|
||||||
} from '../../links/party-link/party-link';
|
} from '../../links/party-link/party-link';
|
||||||
import { txSignatureToDeterministicId } from '../lib/deterministic-ids';
|
import { txSignatureToDeterministicId } from '../lib/deterministic-ids';
|
||||||
import Hash from '../../links/hash';
|
|
||||||
|
|
||||||
type Transfer = components['schemas']['commandsv1Transfer'];
|
type Transfer = components['schemas']['commandsv1Transfer'];
|
||||||
|
|
||||||
@ -61,7 +60,7 @@ export const TxDetailsTransfer = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const from = txData.submitter;
|
const from = txData.submitter;
|
||||||
const id = txSignatureToDeterministicId(txData.signature.value);
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TableWithTbody className="mb-8" allowWrap={true}>
|
<TableWithTbody className="mb-8" allowWrap={true}>
|
||||||
@ -72,7 +71,7 @@ export const TxDetailsTransfer = ({
|
|||||||
<TableRow modifier="bordered" data-testid="id">
|
<TableRow modifier="bordered" data-testid="id">
|
||||||
<TableCell {...sharedHeaderProps}>{t('Transfer ID')}</TableCell>
|
<TableCell {...sharedHeaderProps}>{t('Transfer ID')}</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<Hash text={id} />
|
{txSignatureToDeterministicId(txData.signature.value)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
<TxDetailsShared
|
<TxDetailsShared
|
||||||
@ -106,7 +105,7 @@ export const TxDetailsTransfer = ({
|
|||||||
</TableRow>
|
</TableRow>
|
||||||
) : null}
|
) : null}
|
||||||
</TableWithTbody>
|
</TableWithTbody>
|
||||||
<TransferDetails from={from} transfer={transfer} id={id} />
|
<TransferDetails from={from} transfer={transfer} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,29 +0,0 @@
|
|||||||
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,
|
|
||||||
};
|
|
||||||
}
|
|
@ -44,7 +44,6 @@ export type FilterOption =
|
|||||||
| 'Submit Order'
|
| 'Submit Order'
|
||||||
| 'Transfer Funds'
|
| 'Transfer Funds'
|
||||||
| 'Undelegate'
|
| 'Undelegate'
|
||||||
| 'Update Party Profile'
|
|
||||||
| 'Update Referral Set'
|
| 'Update Referral Set'
|
||||||
| 'Update Margin Mode'
|
| 'Update Margin Mode'
|
||||||
| 'Validator Heartbeat'
|
| 'Validator Heartbeat'
|
||||||
@ -80,7 +79,6 @@ export const filterOptions: Record<string, FilterOption[]> = {
|
|||||||
'Apply Referral Code',
|
'Apply Referral Code',
|
||||||
'Create Referral Set',
|
'Create Referral Set',
|
||||||
'Join Team',
|
'Join Team',
|
||||||
'Update Party Profile',
|
|
||||||
'Update Referral Set',
|
'Update Referral Set',
|
||||||
],
|
],
|
||||||
'External Data': ['Chain Event', 'Submit Oracle Data'],
|
'External Data': ['Chain Event', 'Submit Oracle Data'],
|
||||||
|
@ -12,5 +12,4 @@ export const Routes = {
|
|||||||
ORACLES: 'oracles',
|
ORACLES: 'oracles',
|
||||||
NETWORK_PARAMETERS: 'network-parameters',
|
NETWORK_PARAMETERS: 'network-parameters',
|
||||||
DISCLAIMER: 'disclaimer',
|
DISCLAIMER: 'disclaimer',
|
||||||
TREASURY: 'treasury',
|
|
||||||
};
|
};
|
||||||
|
@ -30,7 +30,6 @@ import { PartyAccountsByAsset } from './parties/id/accounts';
|
|||||||
import { Disclaimer } from './pages/disclaimer';
|
import { Disclaimer } from './pages/disclaimer';
|
||||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||||
import RestrictedPage from './restricted';
|
import RestrictedPage from './restricted';
|
||||||
import { NetworkTreasury } from './treasury';
|
|
||||||
|
|
||||||
export type Navigable = {
|
export type Navigable = {
|
||||||
path: string;
|
path: string;
|
||||||
@ -230,17 +229,6 @@ export const useRouterConfig = () => {
|
|||||||
]
|
]
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
const treasuryRoutes: Route[] = [
|
|
||||||
{
|
|
||||||
path: Routes.TREASURY,
|
|
||||||
handle: {
|
|
||||||
name: t('Treasury'),
|
|
||||||
text: t('Treasury'),
|
|
||||||
breadcrumb: () => <Link to={Routes.TREASURY}>{t('Treasury')}</Link>,
|
|
||||||
},
|
|
||||||
element: <NetworkTreasury />,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
const validators: Route[] = featureFlags.EXPLORER_VALIDATORS
|
const validators: Route[] = featureFlags.EXPLORER_VALIDATORS
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
@ -370,7 +358,6 @@ export const useRouterConfig = () => {
|
|||||||
...marketsRoutes,
|
...marketsRoutes,
|
||||||
...networkParametersRoutes,
|
...networkParametersRoutes,
|
||||||
...validators,
|
...validators,
|
||||||
...treasuryRoutes,
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
query ExplorerTreasury {
|
|
||||||
assetsConnection(pagination: { last: 1000 }) {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
id
|
|
||||||
networkTreasuryAccount {
|
|
||||||
balance
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,52 +0,0 @@
|
|||||||
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>;
|
|
@ -1,84 +0,0 @@
|
|||||||
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>;
|
|
@ -1,37 +0,0 @@
|
|||||||
// 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} />;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,20 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,28 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,171 +0,0 @@
|
|||||||
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();
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,87 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
@ -1,262 +0,0 @@
|
|||||||
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'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,253 +0,0 @@
|
|||||||
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>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1 +0,0 @@
|
|||||||
export * from './network-treasury';
|
|
@ -1,28 +0,0 @@
|
|||||||
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,9 +1,7 @@
|
|||||||
const { join } = require('path');
|
const { join } = require('path');
|
||||||
const { createGlobPatternsForDependencies } = require('@nx/react/tailwind');
|
const { createGlobPatternsForDependencies } = require('@nx/react/tailwind');
|
||||||
const { theme } = require('../../libs/tailwindcss-config/src/theme');
|
const theme = require('../../libs/tailwindcss-config/src/theme');
|
||||||
const {
|
const vegaCustomClasses = require('../../libs/tailwindcss-config/src/vega-custom-classes');
|
||||||
vegaCustomClasses,
|
|
||||||
} = require('../../libs/tailwindcss-config/src/vega-custom-classes');
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
content: [
|
content: [
|
||||||
|
@ -2,7 +2,7 @@ export const proposalsData = {
|
|||||||
proposalsConnection: {
|
proposalsConnection: {
|
||||||
edges: [
|
edges: [
|
||||||
{
|
{
|
||||||
proposalNode: {
|
node: {
|
||||||
id: 'e8ba9d268e12514644fd1fc7ff289292f4ce6489cc32cc73133aea52c04aef89',
|
id: 'e8ba9d268e12514644fd1fc7ff289292f4ce6489cc32cc73133aea52c04aef89',
|
||||||
rationale: {
|
rationale: {
|
||||||
title: 'Add asset Wrapped Ether',
|
title: 'Add asset Wrapped Ether',
|
||||||
@ -56,7 +56,7 @@ export const proposalsData = {
|
|||||||
__typename: 'ProposalEdge',
|
__typename: 'ProposalEdge',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
proposalNode: {
|
node: {
|
||||||
id: 'd848fc7881f13d366df5f61ab139d5fcfa72bf838151bb51b54381870e357931',
|
id: 'd848fc7881f13d366df5f61ab139d5fcfa72bf838151bb51b54381870e357931',
|
||||||
rationale: {
|
rationale: {
|
||||||
title: 'Add asset Dai Stablecoin',
|
title: 'Add asset Dai Stablecoin',
|
||||||
@ -110,7 +110,60 @@ export const proposalsData = {
|
|||||||
__typename: 'ProposalEdge',
|
__typename: 'ProposalEdge',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
proposalNode: {
|
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: {
|
||||||
id: 'bc70383f0e9515b15542cf4c63590cd2ca46b3363ba7c4a72af0e62112b3951b',
|
id: 'bc70383f0e9515b15542cf4c63590cd2ca46b3363ba7c4a72af0e62112b3951b',
|
||||||
rationale: {
|
rationale: {
|
||||||
title: 'USDC-III',
|
title: 'USDC-III',
|
||||||
@ -164,7 +217,60 @@ export const proposalsData = {
|
|||||||
__typename: 'ProposalEdge',
|
__typename: 'ProposalEdge',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
proposalNode: {
|
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: {
|
||||||
id: '9c48796e7988769ededc2b2b02220b00e93f65f23e8141bf1fd23a6983d95943',
|
id: '9c48796e7988769ededc2b2b02220b00e93f65f23e8141bf1fd23a6983d95943',
|
||||||
rationale: {
|
rationale: {
|
||||||
title: 'Update governance.proposal.asset.requiredMajority',
|
title: 'Update governance.proposal.asset.requiredMajority',
|
||||||
|
@ -7,7 +7,6 @@ import {
|
|||||||
navigateTo,
|
navigateTo,
|
||||||
navigation,
|
navigation,
|
||||||
turnTelemetryOff,
|
turnTelemetryOff,
|
||||||
setRiskAccepted,
|
|
||||||
} from '../../support/common.functions';
|
} from '../../support/common.functions';
|
||||||
import {
|
import {
|
||||||
clickOnValidatorFromList,
|
clickOnValidatorFromList,
|
||||||
@ -58,7 +57,6 @@ context(
|
|||||||
// 1002-STKE-002, 1002-STKE-032
|
// 1002-STKE-002, 1002-STKE-032
|
||||||
before('visit staking tab and connect vega wallet', function () {
|
before('visit staking tab and connect vega wallet', function () {
|
||||||
cy.visit('/');
|
cy.visit('/');
|
||||||
setRiskAccepted();
|
|
||||||
ethereumWalletConnect();
|
ethereumWalletConnect();
|
||||||
cy.connectVegaWallet();
|
cy.connectVegaWallet();
|
||||||
vegaWalletSetSpecifiedApprovalAmount('1000');
|
vegaWalletSetSpecifiedApprovalAmount('1000');
|
||||||
@ -238,7 +236,7 @@ context(
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 1002-STKE-041 1002-STKE-053
|
// 1002-STKE-041 1002-STKE-053
|
||||||
it.skip(
|
it(
|
||||||
'Able to remove part of a stake against a validator',
|
'Able to remove part of a stake against a validator',
|
||||||
// @ts-ignore clash between jest and cypress
|
// @ts-ignore clash between jest and cypress
|
||||||
{ tags: '@smoke' },
|
{ tags: '@smoke' },
|
||||||
|
@ -5,7 +5,6 @@ import {
|
|||||||
navigateTo,
|
navigateTo,
|
||||||
navigation,
|
navigation,
|
||||||
turnTelemetryOff,
|
turnTelemetryOff,
|
||||||
setRiskAccepted,
|
|
||||||
} from '../../support/common.functions';
|
} from '../../support/common.functions';
|
||||||
import {
|
import {
|
||||||
stakingPageAssociateTokens,
|
stakingPageAssociateTokens,
|
||||||
@ -58,7 +57,6 @@ context(
|
|||||||
function () {
|
function () {
|
||||||
cy.clearLocalStorage();
|
cy.clearLocalStorage();
|
||||||
turnTelemetryOff();
|
turnTelemetryOff();
|
||||||
setRiskAccepted();
|
|
||||||
cy.mockChainId();
|
cy.mockChainId();
|
||||||
cy.reload();
|
cy.reload();
|
||||||
waitForSpinner();
|
waitForSpinner();
|
||||||
|
@ -27,7 +27,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
|||||||
cy.getByTestId('app-announcement').should('not.exist');
|
cy.getByTestId('app-announcement').should('not.exist');
|
||||||
});
|
});
|
||||||
|
|
||||||
it.skip('should show open or enacted proposals without proposal summary', function () {
|
it('should show open or enacted proposals without proposal summary', function () {
|
||||||
cy.get('body').then(($body) => {
|
cy.get('body').then(($body) => {
|
||||||
if (!$body.find('[data-testid="proposals-list-item"]').length) {
|
if (!$body.find('[data-testid="proposals-list-item"]').length) {
|
||||||
cy.createMarket();
|
cy.createMarket();
|
||||||
@ -79,21 +79,21 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it.skip('should have information on active nodes', function () {
|
it('should have information on active nodes', function () {
|
||||||
cy.getByTestId('node-information')
|
cy.getByTestId('node-information')
|
||||||
.first()
|
.first()
|
||||||
.should('contain.text', '2')
|
.should('contain.text', '2')
|
||||||
.and('contain.text', 'active nodes');
|
.and('contain.text', 'active nodes');
|
||||||
});
|
});
|
||||||
|
|
||||||
it.skip('should have information on consensus nodes', function () {
|
it('should have information on consensus nodes', function () {
|
||||||
cy.getByTestId('node-information')
|
cy.getByTestId('node-information')
|
||||||
.last()
|
.last()
|
||||||
.should('contain.text', '2')
|
.should('contain.text', '2')
|
||||||
.and('contain.text', 'consensus nodes');
|
.and('contain.text', 'consensus nodes');
|
||||||
});
|
});
|
||||||
|
|
||||||
it.skip('should contain link to specific validators', function () {
|
it('should contain link to specific validators', function () {
|
||||||
cy.getByTestId('validators')
|
cy.getByTestId('validators')
|
||||||
.should('have.length', '2')
|
.should('have.length', '2')
|
||||||
.each(($validator) => {
|
.each(($validator) => {
|
||||||
@ -153,13 +153,13 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
|||||||
.invoke('text')
|
.invoke('text')
|
||||||
.should('not.eq', currentBlockHeight);
|
.should('not.eq', currentBlockHeight);
|
||||||
});
|
});
|
||||||
cy.getByTestId('subscription-cell').should('be.be.visible');
|
cy.getByTestId('subscription-cell').should('have.text', 'Yes');
|
||||||
});
|
});
|
||||||
cy.getByTestId('connect').should('be.disabled');
|
cy.getByTestId('connect').should('be.disabled');
|
||||||
cy.getByTestId('node-url-custom').click({ force: true });
|
cy.getByTestId('node-url-custom').click({ force: true });
|
||||||
cy.get('input').should('exist');
|
cy.get('input').should('exist');
|
||||||
cy.getByTestId('connect').should('be.disabled');
|
cy.getByTestId('connect').should('be.disabled');
|
||||||
cy.getByTestId('dialog-close').click();
|
cy.getByTestId('icon-cross').click();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display eth data', function () {
|
it('should display eth data', function () {
|
||||||
@ -189,7 +189,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
|||||||
cy.viewport('iphone-xr');
|
cy.viewport('iphone-xr');
|
||||||
});
|
});
|
||||||
|
|
||||||
it.skip('should have burger button', () => {
|
it('should have burger button', () => {
|
||||||
cy.getByTestId('button-menu-drawer').should('be.visible').click();
|
cy.getByTestId('button-menu-drawer').should('be.visible').click();
|
||||||
cy.getByTestId('menu-drawer').should('be.visible');
|
cy.getByTestId('menu-drawer').should('be.visible');
|
||||||
});
|
});
|
||||||
|
@ -34,10 +34,16 @@ context('View functionality with public key', { tags: '@smoke' }, function () {
|
|||||||
cy.connectPublicKey(vegaWalletPubKey);
|
cy.connectPublicKey(vegaWalletPubKey);
|
||||||
});
|
});
|
||||||
|
|
||||||
it.skip('Able to connect public key via wallet and view assets in wallet', function () {
|
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 () {
|
||||||
verifyConnectedToPubKey();
|
verifyConnectedToPubKey();
|
||||||
cy.getByTestId('currency-title', { timeout: 10000 })
|
cy.getByTestId('currency-title', { timeout: 10000 })
|
||||||
.should('have.length.at.least', 2)
|
.should('have.length.at.least', 4)
|
||||||
.and('contain.text', 'USDC (fake)');
|
.and('contain.text', 'USDC (fake)');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
||||||
import {
|
import {
|
||||||
navigation,
|
navigation,
|
||||||
setRiskAccepted,
|
|
||||||
verifyPageHeader,
|
verifyPageHeader,
|
||||||
verifyTabHighlighted,
|
verifyTabHighlighted,
|
||||||
} from '../../support/common.functions';
|
} from '../../support/common.functions';
|
||||||
@ -188,7 +187,6 @@ context('Validators Page - verify elements on page', function () {
|
|||||||
before('connect wallets and click on validator', function () {
|
before('connect wallets and click on validator', function () {
|
||||||
cy.mockChainId();
|
cy.mockChainId();
|
||||||
cy.visit('/validators');
|
cy.visit('/validators');
|
||||||
setRiskAccepted();
|
|
||||||
cy.connectVegaWallet();
|
cy.connectVegaWallet();
|
||||||
clickOnValidatorFromList(0);
|
clickOnValidatorFromList(0);
|
||||||
});
|
});
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
import { truncateByChars } from '@vegaprotocol/utils';
|
import { truncateByChars } from '@vegaprotocol/utils';
|
||||||
import {
|
import { waitForSpinner } from '../../support/common.functions';
|
||||||
setRiskAccepted,
|
|
||||||
waitForSpinner,
|
|
||||||
} from '../../support/common.functions';
|
|
||||||
import {
|
import {
|
||||||
vegaWalletFaucetAssetsWithoutCheck,
|
vegaWalletFaucetAssetsWithoutCheck,
|
||||||
vegaWalletTeardown,
|
vegaWalletTeardown,
|
||||||
@ -14,6 +11,7 @@ const connectButton = 'connect-vega-wallet';
|
|||||||
const getVegaLink = 'link';
|
const getVegaLink = 'link';
|
||||||
const dialog = '[role="dialog"]:visible';
|
const dialog = '[role="dialog"]:visible';
|
||||||
const dialogHeader = 'dialog-title';
|
const dialogHeader = 'dialog-title';
|
||||||
|
const walletDialogHeader = 'wallet-dialog-title';
|
||||||
const connectorsList = 'connectors-list';
|
const connectorsList = 'connectors-list';
|
||||||
const dialogCloseBtn = 'dialog-close';
|
const dialogCloseBtn = 'dialog-close';
|
||||||
const accountNo = 'vega-account-truncated';
|
const accountNo = 'vega-account-truncated';
|
||||||
@ -36,7 +34,6 @@ context(
|
|||||||
() => {
|
() => {
|
||||||
before('visit token home page', () => {
|
before('visit token home page', () => {
|
||||||
cy.visit('/');
|
cy.visit('/');
|
||||||
setRiskAccepted();
|
|
||||||
cy.get(walletContainer, { timeout: 60000 }).should('be.visible');
|
cy.get(walletContainer, { timeout: 60000 }).should('be.visible');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -66,12 +63,17 @@ context(
|
|||||||
|
|
||||||
it('should have Connect Vega header visible', () => {
|
it('should have Connect Vega header visible', () => {
|
||||||
cy.get(dialog).within(() => {
|
cy.get(dialog).within(() => {
|
||||||
cy.getByTestId(connectorsList)
|
cy.getByTestId(walletDialogHeader)
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
.and(
|
.and('have.text', 'Get a Vega wallet');
|
||||||
'have.text',
|
});
|
||||||
'Get the Vega WalletGet MetaMask>_Command Line WalletView as public key'
|
});
|
||||||
);
|
|
||||||
|
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');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -86,6 +88,7 @@ context(
|
|||||||
before('connect vega wallet', function () {
|
before('connect vega wallet', function () {
|
||||||
cy.mockChainId();
|
cy.mockChainId();
|
||||||
cy.visit('/');
|
cy.visit('/');
|
||||||
|
cy.wait('@ChainId');
|
||||||
cy.connectVegaWallet();
|
cy.connectVegaWallet();
|
||||||
vegaWalletTeardown();
|
vegaWalletTeardown();
|
||||||
});
|
});
|
||||||
|
@ -102,12 +102,6 @@ export function turnTelemetryOff() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setRiskAccepted() {
|
|
||||||
cy.window().then((win) =>
|
|
||||||
win.localStorage.setItem('vega_wallet_risk_accepted', 'true')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function dissociateFromSecondWalletKey() {
|
export function dissociateFromSecondWalletKey() {
|
||||||
const secondWalletKey = Cypress.env('vegaWalletPublicKey2Short');
|
const secondWalletKey = Cypress.env('vegaWalletPublicKey2Short');
|
||||||
cy.getByTestId('vega-in-wallet')
|
cy.getByTestId('vega-in-wallet')
|
||||||
|
@ -14,6 +14,7 @@ NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/m
|
|||||||
|
|
||||||
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
|
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
|
||||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||||
|
NX_DELEGATIONS_PAGINATION=50
|
||||||
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
|
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
|
||||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/main/announcements.json
|
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/main/announcements.json
|
||||||
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
||||||
@ -23,7 +24,7 @@ NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks
|
|||||||
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
|
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
|
||||||
|
|
||||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
|
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
|
||||||
|
|
||||||
#Test configuration variables
|
#Test configuration variables
|
||||||
CYPRESS_FAIRGROUND=false
|
CYPRESS_FAIRGROUND=false
|
||||||
@ -32,7 +33,7 @@ LC_ALL="en_US.UTF-8"
|
|||||||
# Cosmic elevator flags
|
# Cosmic elevator flags
|
||||||
NX_SUCCESSOR_MARKETS=true
|
NX_SUCCESSOR_MARKETS=true
|
||||||
NX_METAMASK_SNAPS=true
|
NX_METAMASK_SNAPS=true
|
||||||
NX_PRODUCT_PERPETUALS=true
|
NX_PRODUCT_PERPETUALS=false
|
||||||
NX_UPDATE_MARKET_STATE=true
|
NX_UPDATE_MARKET_STATE=false
|
||||||
NX_REFERRALS=true
|
NX_REFERRALS=true
|
||||||
NX_GOVERNANCE_TRANSFERS=true
|
NX_GOVERNANCE_TRANSFERS=false
|
||||||
|
@ -15,11 +15,12 @@ NX_ETH_WALLET_MNEMONIC=ozone access unlock valid olympic save include omit suppl
|
|||||||
NX_LOCAL_PROVIDER_URL=http://localhost:8545/
|
NX_LOCAL_PROVIDER_URL=http://localhost:8545/
|
||||||
NX_VEGA_WALLET_URL=http://localhost:1789
|
NX_VEGA_WALLET_URL=http://localhost:1789
|
||||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||||
|
NX_DELEGATIONS_PAGINATION=50
|
||||||
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
|
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
|
||||||
NX_VEGA_REST_URL=http://localhost:3008/api/v2/
|
NX_VEGA_REST_URL=http://localhost:3008/api/v2/
|
||||||
|
|
||||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
|
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
|
||||||
|
|
||||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||||
|
|
||||||
|
@ -8,13 +8,14 @@ NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
|||||||
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
||||||
NX_VEGA_EXPLORER_URL=#
|
NX_VEGA_EXPLORER_URL=#
|
||||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||||
|
NX_DELEGATIONS_PAGINATION=50
|
||||||
NX_TRANCHES_SERVICE_URL=https://tranches-devnet1-k8s.ops.vega.xyz
|
NX_TRANCHES_SERVICE_URL=https://tranches-devnet1-k8s.ops.vega.xyz
|
||||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||||
NX_VEGA_REST_URL=https://api.n00.devnet1.vega.xyz/api/v2/
|
NX_VEGA_REST_URL=https://api.n00.devnet1.vega.xyz/api/v2/
|
||||||
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
||||||
|
|
||||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
|
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
|
||||||
|
|
||||||
NX_TENDERMINT_URL=https://tm.be.devnet1.vega.xyz/
|
NX_TENDERMINT_URL=https://tm.be.devnet1.vega.xyz/
|
||||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.devnet1.vega.xyz/websocket
|
NX_TENDERMINT_WEBSOCKET_URL=wss://be.devnet1.vega.xyz/websocket
|
||||||
|
@ -10,6 +10,7 @@ NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/
|
|||||||
NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz
|
NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz
|
||||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/mainnet
|
NX_VEGA_DOCS_URL=https://docs.vega.xyz/mainnet
|
||||||
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40:87edc2605e544f888305d7fc4a9141bd@o286262.ingest.sentry.io/5882996
|
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40:87edc2605e544f888305d7fc4a9141bd@o286262.ingest.sentry.io/5882996
|
||||||
|
NX_DELEGATIONS_PAGINATION=50
|
||||||
NX_TRANCHES_SERVICE_URL=https://tranches-mainnet-k8s.ops.vega.xyz
|
NX_TRANCHES_SERVICE_URL=https://tranches-mainnet-k8s.ops.vega.xyz
|
||||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
|
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
|
||||||
NX_VEGA_REST_URL=https://api.vega.community/api/v2/
|
NX_VEGA_REST_URL=https://api.vega.community/api/v2/
|
||||||
|
@ -9,6 +9,7 @@ NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
|||||||
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
||||||
NX_VEGA_EXPLORER_URL=https://explorer.mainnet-mirror.vega.rocks
|
NX_VEGA_EXPLORER_URL=https://explorer.mainnet-mirror.vega.rocks
|
||||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/mainnet
|
NX_VEGA_DOCS_URL=https://docs.vega.xyz/mainnet
|
||||||
|
NX_DELEGATIONS_PAGINATION=50
|
||||||
NX_TRANCHES_SERVICE_URL=https://tranches-mainnet-mirror-k8s.ops.vega.xyz
|
NX_TRANCHES_SERVICE_URL=https://tranches-mainnet-mirror-k8s.ops.vega.xyz
|
||||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
|
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
|
||||||
NX_VEGA_REST_URL=https://api.mainnet-mirror.vega.rocks/api/v2/
|
NX_VEGA_REST_URL=https://api.mainnet-mirror.vega.rocks/api/v2/
|
||||||
|
@ -5,6 +5,7 @@ NX_VEGA_NETWORKS='{"DEVNET":"https://dev.governance.vega.xyz","STAGNET1":"https:
|
|||||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/stagnet1/vegawallet-stagnet1.toml
|
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/stagnet1/vegawallet-stagnet1.toml
|
||||||
NX_VEGA_EXPLORER_URL=https://explorer.stagnet1.vega.rocks
|
NX_VEGA_EXPLORER_URL=https://explorer.stagnet1.vega.rocks
|
||||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||||
|
NX_DELEGATIONS_PAGINATION=50
|
||||||
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
|
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
|
||||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||||
NX_VEGA_REST_URL=https://api.n00.stagnet1.vega.xyz/api/v2/
|
NX_VEGA_REST_URL=https://api.n00.stagnet1.vega.xyz/api/v2/
|
||||||
@ -12,7 +13,7 @@ NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/m
|
|||||||
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
||||||
|
|
||||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
|
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
|
||||||
|
|
||||||
NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks
|
NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks
|
||||||
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
|
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
|
||||||
|
@ -9,6 +9,7 @@ NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
|||||||
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
|
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
|
||||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||||
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
|
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
|
||||||
|
NX_DELEGATIONS_PAGINATION=50
|
||||||
NX_TRANCHES_SERVICE_URL=https://tranches-testnet-k8s.ops.vega.xyz
|
NX_TRANCHES_SERVICE_URL=https://tranches-testnet-k8s.ops.vega.xyz
|
||||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||||
NX_VEGA_REST_URL=https://api.n07.testnet.vega.xyz/api/v2/
|
NX_VEGA_REST_URL=https://api.n07.testnet.vega.xyz/api/v2/
|
||||||
@ -17,7 +18,7 @@ NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/m
|
|||||||
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
||||||
|
|
||||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
|
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
|
||||||
|
|
||||||
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
|
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
|
||||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
|
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
|
||||||
|
@ -14,7 +14,7 @@ NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/
|
|||||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||||
|
|
||||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
|
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
|
||||||
|
|
||||||
NX_TENDERMINT_URL=https://tm.be.validators-testnet.vega.rocks
|
NX_TENDERMINT_URL=https://tm.be.validators-testnet.vega.rocks
|
||||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.
|
NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import * as Sentry from '@sentry/react';
|
import * as Sentry from '@sentry/react';
|
||||||
import { toBigNum } from '@vegaprotocol/utils';
|
import { toBigNum } from '@vegaprotocol/utils';
|
||||||
import { Splash } from '@vegaprotocol/ui-toolkit';
|
import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||||
import { useVegaWallet, useEagerConnect } from '@vegaprotocol/wallet-react';
|
import { useVegaWallet, useEagerConnect } from '@vegaprotocol/wallet';
|
||||||
import { useFeatureFlags, useEnvironment } from '@vegaprotocol/environment';
|
import { useFeatureFlags, useEnvironment } from '@vegaprotocol/environment';
|
||||||
import { useWeb3React } from '@web3-react/core';
|
import { useWeb3React } from '@web3-react/core';
|
||||||
import React, { Suspense } from 'react';
|
import React, { Suspense } from 'react';
|
||||||
@ -15,6 +15,20 @@ import {
|
|||||||
} from './contexts/app-state/app-state-context';
|
} from './contexts/app-state/app-state-context';
|
||||||
import { useContracts } from './contexts/contracts/contracts-context';
|
import { useContracts } from './contexts/contracts/contracts-context';
|
||||||
import { useRefreshAssociatedBalances } from './hooks/use-refresh-associated-balances';
|
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 }) => {
|
export const AppLoader = ({ children }: { children: React.ReactElement }) => {
|
||||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||||
@ -26,9 +40,9 @@ export const AppLoader = ({ children }: { children: React.ReactElement }) => {
|
|||||||
const { token, staking, vesting } = useContracts();
|
const { token, staking, vesting } = useContracts();
|
||||||
const setAssociatedBalances = useRefreshAssociatedBalances();
|
const setAssociatedBalances = useRefreshAssociatedBalances();
|
||||||
const [balancesLoaded, setBalancesLoaded] = React.useState(false);
|
const [balancesLoaded, setBalancesLoaded] = React.useState(false);
|
||||||
const vegaWalletStatus = useEagerConnect();
|
const vegaConnecting = useVegaWalletEagerConnect();
|
||||||
|
|
||||||
const loaded = balancesLoaded && vegaWalletStatus !== 'connecting';
|
const loaded = balancesLoaded && !vegaConnecting;
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const run = async () => {
|
const run = async () => {
|
||||||
@ -169,5 +183,3 @@ export const AppLoader = ({ children }: { children: React.ReactElement }) => {
|
|||||||
}
|
}
|
||||||
return <Suspense fallback={loading}>{children}</Suspense>;
|
return <Suspense fallback={loading}>{children}</Suspense>;
|
||||||
};
|
};
|
||||||
|
|
||||||
AppLoader.displayName = 'AppLoader';
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import './i18n';
|
import './i18n';
|
||||||
|
|
||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
|
import * as Sentry from '@sentry/react';
|
||||||
import { BrowserRouter as Router, useLocation } from 'react-router-dom';
|
import { BrowserRouter as Router, useLocation } from 'react-router-dom';
|
||||||
import { AppLoader } from './app-loader';
|
import { AppLoader } from './app-loader';
|
||||||
import { NetworkInfo } from '@vegaprotocol/network-info';
|
import { NetworkInfo } from '@vegaprotocol/network-info';
|
||||||
@ -25,7 +26,7 @@ import {
|
|||||||
} from '@vegaprotocol/web3';
|
} from '@vegaprotocol/web3';
|
||||||
import { Web3Provider } from '@vegaprotocol/web3';
|
import { Web3Provider } from '@vegaprotocol/web3';
|
||||||
import { VegaWalletDialogs } from './components/vega-wallet-dialogs';
|
import { VegaWalletDialogs } from './components/vega-wallet-dialogs';
|
||||||
import { WalletProvider } from '@vegaprotocol/wallet-react';
|
import { VegaWalletProvider, useChainId } from '@vegaprotocol/wallet';
|
||||||
import {
|
import {
|
||||||
useVegaTransactionManager,
|
useVegaTransactionManager,
|
||||||
useVegaTransactionUpdater,
|
useVegaTransactionUpdater,
|
||||||
@ -35,30 +36,32 @@ import { useEthereumConfig } from '@vegaprotocol/web3';
|
|||||||
import {
|
import {
|
||||||
useEnvironment,
|
useEnvironment,
|
||||||
NetworkLoader,
|
NetworkLoader,
|
||||||
|
useInitializeEnv,
|
||||||
NodeGuard,
|
NodeGuard,
|
||||||
NodeSwitcherDialog,
|
NodeSwitcherDialog,
|
||||||
useNodeSwitcherStore,
|
useNodeSwitcherStore,
|
||||||
|
DocsLinks,
|
||||||
NodeFailure,
|
NodeFailure,
|
||||||
AppLoader as Loader,
|
AppLoader as Loader,
|
||||||
useInitializeEnv,
|
|
||||||
} from '@vegaprotocol/environment';
|
} from '@vegaprotocol/environment';
|
||||||
|
import { ENV } from './config';
|
||||||
import type { InMemoryCacheConfig } from '@apollo/client';
|
import type { InMemoryCacheConfig } from '@apollo/client';
|
||||||
import { CreateWithdrawalDialog } from '@vegaprotocol/withdraws';
|
import { CreateWithdrawalDialog } from '@vegaprotocol/withdraws';
|
||||||
import { SplashLoader } from './components/splash-loader';
|
import { SplashLoader } from './components/splash-loader';
|
||||||
import { ToastsManager } from './toasts-manager';
|
import { ToastsManager } from './toasts-manager';
|
||||||
import { TelemetryDialog } from './components/telemetry-dialog/telemetry-dialog';
|
import {
|
||||||
|
TelemetryDialog,
|
||||||
|
TELEMETRY_ON,
|
||||||
|
} from './components/telemetry-dialog/telemetry-dialog';
|
||||||
|
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useSentryInit } from './hooks/use-sentry-init';
|
import { isPartyNotFoundError } from './lib/party';
|
||||||
import { useVegaWalletConfig } from './hooks/use-vega-wallet-config';
|
|
||||||
|
|
||||||
const cache: InMemoryCacheConfig = {
|
const cache: InMemoryCacheConfig = {
|
||||||
typePolicies: {
|
typePolicies: {
|
||||||
Account: {
|
Account: {
|
||||||
keyFields: false,
|
keyFields: false,
|
||||||
},
|
},
|
||||||
Instrument: {
|
|
||||||
keyFields: ['code'],
|
|
||||||
},
|
|
||||||
Delegation: {
|
Delegation: {
|
||||||
keyFields: false,
|
keyFields: false,
|
||||||
// Only get full updates
|
// Only get full updates
|
||||||
@ -98,12 +101,32 @@ const Web3Container = ({
|
|||||||
/** Ethereum provider url */
|
/** Ethereum provider url */
|
||||||
providerUrl: string;
|
providerUrl: string;
|
||||||
}) => {
|
}) => {
|
||||||
|
const InitializeHandlers = () => {
|
||||||
|
useVegaTransactionManager();
|
||||||
|
useVegaTransactionUpdater();
|
||||||
|
useEthTransactionManager();
|
||||||
|
useEthTransactionUpdater();
|
||||||
|
useEthWithdrawApprovalsManager();
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
const [connectors, initializeConnectors] = useWeb3ConnectStore((store) => [
|
const [connectors, initializeConnectors] = useWeb3ConnectStore((store) => [
|
||||||
store.connectors,
|
store.connectors,
|
||||||
store.initialize,
|
store.initialize,
|
||||||
]);
|
]);
|
||||||
const { ETHEREUM_PROVIDER_URL, ETH_LOCAL_PROVIDER_URL, ETH_WALLET_MNEMONIC } =
|
const {
|
||||||
useEnvironment();
|
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);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (chainId) {
|
if (chainId) {
|
||||||
@ -124,31 +147,50 @@ const Web3Container = ({
|
|||||||
ETH_LOCAL_PROVIDER_URL,
|
ETH_LOCAL_PROVIDER_URL,
|
||||||
ETH_WALLET_MNEMONIC,
|
ETH_WALLET_MNEMONIC,
|
||||||
]);
|
]);
|
||||||
|
const sideBar = React.useMemo(() => {
|
||||||
|
return [<EthWallet />, <VegaWallet />];
|
||||||
|
}, []);
|
||||||
|
|
||||||
const vegaWalletConfig = useVegaWalletConfig();
|
if (connectors.length === 0) {
|
||||||
|
|
||||||
if (!vegaWalletConfig || connectors.length === 0) {
|
|
||||||
// Prevent loading when the connectors are not initialized
|
// Prevent loading when the connectors are not initialized
|
||||||
return <SplashLoader />;
|
return <SplashLoader />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!VEGA_URL ||
|
||||||
|
!VEGA_WALLET_URL ||
|
||||||
|
!VEGA_EXPLORER_URL ||
|
||||||
|
!DocsLinks ||
|
||||||
|
!CHROME_EXTENSION_URL ||
|
||||||
|
!MOZILLA_EXTENSION_URL ||
|
||||||
|
!vegaChainId
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Web3Provider connectors={connectors}>
|
<Web3Provider connectors={connectors}>
|
||||||
<Web3Connector connectors={connectors} chainId={Number(chainId)}>
|
<Web3Connector connectors={connectors} chainId={Number(chainId)}>
|
||||||
<WalletProvider config={vegaWalletConfig}>
|
<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,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
<ContractsProvider>
|
<ContractsProvider>
|
||||||
<AppLoader>
|
<AppLoader>
|
||||||
<BalanceManager>
|
<BalanceManager>
|
||||||
<>
|
<>
|
||||||
<AppLayout>
|
<AppLayout>
|
||||||
<TemplateSidebar
|
<TemplateSidebar sidebar={sideBar}>
|
||||||
sidebar={
|
|
||||||
<>
|
|
||||||
<EthWallet />
|
|
||||||
<VegaWallet />
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<AppRouter />
|
<AppRouter />
|
||||||
</TemplateSidebar>
|
</TemplateSidebar>
|
||||||
<footer className="p-4 break-all border-t border-neutral-700">
|
<footer className="p-4 break-all border-t border-neutral-700">
|
||||||
@ -166,7 +208,7 @@ const Web3Container = ({
|
|||||||
</BalanceManager>
|
</BalanceManager>
|
||||||
</AppLoader>
|
</AppLoader>
|
||||||
</ContractsProvider>
|
</ContractsProvider>
|
||||||
</WalletProvider>
|
</VegaWalletProvider>
|
||||||
</Web3Connector>
|
</Web3Connector>
|
||||||
</Web3Provider>
|
</Web3Provider>
|
||||||
);
|
);
|
||||||
@ -186,9 +228,20 @@ const ScrollToTop = () => {
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const removeQueryParams = (url: string) => {
|
||||||
|
return url.split('?')[0];
|
||||||
|
};
|
||||||
|
|
||||||
const AppContainer = () => {
|
const AppContainer = () => {
|
||||||
const { config, loading, error } = useEthereumConfig();
|
const { config, loading, error } = useEthereumConfig();
|
||||||
const { VEGA_URL, ETHEREUM_PROVIDER_URL } = useEnvironment();
|
const {
|
||||||
|
VEGA_ENV,
|
||||||
|
VEGA_URL,
|
||||||
|
GIT_COMMIT_HASH,
|
||||||
|
GIT_BRANCH,
|
||||||
|
ETHEREUM_PROVIDER_URL,
|
||||||
|
} = useEnvironment();
|
||||||
|
const [telemetryOn] = useLocalStorage(TELEMETRY_ON);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [nodeSwitcherOpen, setNodeSwitcher] = useNodeSwitcherStore((store) => [
|
const [nodeSwitcherOpen, setNodeSwitcher] = useNodeSwitcherStore((store) => [
|
||||||
store.dialogOpen,
|
store.dialogOpen,
|
||||||
@ -198,7 +251,70 @@ const AppContainer = () => {
|
|||||||
// Hacky skip all the loading & web3 init for geo restricted users
|
// Hacky skip all the loading & web3 init for geo restricted users
|
||||||
const isRestricted = document?.location?.pathname?.includes('/restricted');
|
const isRestricted = document?.location?.pathname?.includes('/restricted');
|
||||||
|
|
||||||
useSentryInit();
|
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]);
|
||||||
|
|
||||||
if (isRestricted) {
|
if (isRestricted) {
|
||||||
return (
|
return (
|
||||||
@ -240,15 +356,6 @@ const AppContainer = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const InitializeHandlers = () => {
|
|
||||||
useVegaTransactionManager();
|
|
||||||
useVegaTransactionUpdater();
|
|
||||||
useEthTransactionManager();
|
|
||||||
useEthTransactionUpdater();
|
|
||||||
useEthWithdrawApprovalsManager();
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
useInitializeEnv();
|
useInitializeEnv();
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ import { useGetAssociationBreakdown } from '../../hooks/use-get-association-brea
|
|||||||
import { useGetUserBalances } from '../../hooks/use-get-user-balances';
|
import { useGetUserBalances } from '../../hooks/use-get-user-balances';
|
||||||
import { useBalances } from '../../lib/balances/balances-store';
|
import { useBalances } from '../../lib/balances/balances-store';
|
||||||
import type { ReactElement } from 'react';
|
import type { ReactElement } from 'react';
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet-react';
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
import { useListenForStakingEvents as useListenForAssociationEvents } from '../../hooks/use-listen-for-staking-events';
|
import { useListenForStakingEvents as useListenForAssociationEvents } from '../../hooks/use-listen-for-staking-events';
|
||||||
import { useTranches } from '../../lib/tranches/tranches-store';
|
import { useTranches } from '../../lib/tranches/tranches-store';
|
||||||
import { useUserTrancheBalances } from '../../routes/redemption/hooks';
|
import { useUserTrancheBalances } from '../../routes/redemption/hooks';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
|
import { Icon } from '@vegaprotocol/ui-toolkit';
|
||||||
import type { Dispatch, SetStateAction, ReactNode } from 'react';
|
import type { Dispatch, SetStateAction, ReactNode } from 'react';
|
||||||
|
|
||||||
interface CollapsibleToggleProps {
|
interface CollapsibleToggleProps {
|
||||||
@ -15,19 +15,22 @@ export const CollapsibleToggle = ({
|
|||||||
dataTestId,
|
dataTestId,
|
||||||
children,
|
children,
|
||||||
}: CollapsibleToggleProps) => {
|
}: CollapsibleToggleProps) => {
|
||||||
const classes = classnames('transition-transform ease-in-out duration-300', {
|
const classes = classnames(
|
||||||
'rotate-180': toggleState,
|
'mb-4 transition-transform ease-in-out duration-300',
|
||||||
});
|
{
|
||||||
|
'rotate-180': toggleState,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
onClick={() => setToggleState(!toggleState)}
|
onClick={() => setToggleState(!toggleState)}
|
||||||
data-testid={dataTestId}
|
data-testid={dataTestId}
|
||||||
>
|
>
|
||||||
<div className="flex items-baseline gap-3">
|
<div className="flex items-center gap-3">
|
||||||
{children}
|
{children}
|
||||||
<div className={classes} data-testid="toggle-icon-wrapper">
|
<div className={classes} data-testid="toggle-icon-wrapper">
|
||||||
<VegaIcon name={VegaIconNames.CHEVRON_DOWN} size={20} />
|
<Icon name="chevron-down" size={8} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
@ -1,13 +1,18 @@
|
|||||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useDialogStore } from '@vegaprotocol/wallet-react';
|
import { useVegaWalletDialogStore } from '@vegaprotocol/wallet';
|
||||||
|
|
||||||
export const ConnectToVega = () => {
|
export const ConnectToVega = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const openVegaWalletDialog = useDialogStore((store) => store.open);
|
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
|
||||||
|
openVegaWalletDialog: store.openVegaWalletDialog,
|
||||||
|
}));
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
onClick={openVegaWalletDialog}
|
onClick={() => {
|
||||||
|
openVegaWalletDialog();
|
||||||
|
}}
|
||||||
data-testid="connect-to-vega-wallet-btn"
|
data-testid="connect-to-vega-wallet-btn"
|
||||||
variant="primary"
|
variant="primary"
|
||||||
>
|
>
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { type ReactNode } from 'react';
|
|
||||||
|
|
||||||
interface HeadingProps {
|
interface HeadingProps {
|
||||||
title?: ReactNode;
|
title?: string;
|
||||||
centerContent?: boolean;
|
centerContent?: boolean;
|
||||||
marginTop?: boolean;
|
marginTop?: boolean;
|
||||||
marginBottom?: boolean;
|
marginBottom?: boolean;
|
||||||
|
@ -1,44 +0,0 @@
|
|||||||
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 classNames from 'classnames';
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet-react';
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
import { AnnouncementBanner } from '@vegaprotocol/announcements';
|
import { AnnouncementBanner } from '@vegaprotocol/announcements';
|
||||||
import { Nav } from '../nav';
|
import { Nav } from '../nav';
|
||||||
@ -10,7 +10,6 @@ import {
|
|||||||
ProtocolUpgradeProposalNotification,
|
ProtocolUpgradeProposalNotification,
|
||||||
} from '@vegaprotocol/proposals';
|
} from '@vegaprotocol/proposals';
|
||||||
import { ViewingAsBanner } from '@vegaprotocol/ui-toolkit';
|
import { ViewingAsBanner } from '@vegaprotocol/ui-toolkit';
|
||||||
import { RewardsMovedNotification } from '../notifications/rewards-moved-notification';
|
|
||||||
|
|
||||||
interface AppLayoutProps {
|
interface AppLayoutProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
@ -46,10 +45,8 @@ export const AppLayout = ({ children }: AppLayoutProps) => {
|
|||||||
|
|
||||||
const NotificationsContainer = () => {
|
const NotificationsContainer = () => {
|
||||||
const { isReadOnly, pubKey, disconnect } = useVegaWallet();
|
const { isReadOnly, pubKey, disconnect } = useVegaWallet();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div data-testid="banners">
|
<div data-testid="banners">
|
||||||
<RewardsMovedNotification />
|
|
||||||
<ProtocolUpgradeProposalNotification
|
<ProtocolUpgradeProposalNotification
|
||||||
mode={ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING}
|
mode={ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING}
|
||||||
/>
|
/>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { Children, type ReactNode } from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
export interface TemplateSidebarProps {
|
export interface TemplateSidebarProps {
|
||||||
children: ReactNode;
|
children: React.ReactNode;
|
||||||
sidebar: ReactNode;
|
sidebar: React.ReactNode[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TemplateSidebar({ children, sidebar }: TemplateSidebarProps) {
|
export function TemplateSidebar({ children, sidebar }: TemplateSidebarProps) {
|
||||||
@ -12,9 +12,9 @@ export function TemplateSidebar({ children, sidebar }: TemplateSidebarProps) {
|
|||||||
{children}
|
{children}
|
||||||
</main>
|
</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">
|
<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">
|
||||||
{Children.map(sidebar, (child, i) => (
|
{sidebar.map((Component, i) => (
|
||||||
<section className="mb-4 last:mb-0" key={i}>
|
<section className="mb-4 last:mb-0" key={i}>
|
||||||
{child}
|
{Component}
|
||||||
</section>
|
</section>
|
||||||
))}
|
))}
|
||||||
</aside>
|
</aside>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||||
import { useVegaWallet, useDialogStore } from '@vegaprotocol/wallet-react';
|
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
@ -10,7 +10,9 @@ interface VegaWalletContainerProps {
|
|||||||
export const VegaWalletContainer = ({ children }: VegaWalletContainerProps) => {
|
export const VegaWalletContainer = ({ children }: VegaWalletContainerProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { pubKey } = useVegaWallet();
|
const { pubKey } = useVegaWallet();
|
||||||
const openVegaWalletDialog = useDialogStore((store) => store.open);
|
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
|
||||||
|
openVegaWalletDialog: store.openVegaWalletDialog,
|
||||||
|
}));
|
||||||
|
|
||||||
if (!pubKey) {
|
if (!pubKey) {
|
||||||
return (
|
return (
|
||||||
|
@ -9,7 +9,7 @@ import Routes from '../../routes/routes';
|
|||||||
export const RiskMessage = () => {
|
export const RiskMessage = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="bg-vega-light-100 dark:bg-vega-dark-100 p-6">
|
<div className="bg-vega-light-100 dark:bg-vega-dark-100 p-6 mb-6">
|
||||||
<ul className="list-[square] ml-4">
|
<ul className="list-[square] ml-4">
|
||||||
<li>
|
<li>
|
||||||
{t(
|
{t(
|
||||||
@ -23,7 +23,7 @@ export const RiskMessage = () => {
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<p>
|
<p className="mb-8">
|
||||||
{t(
|
{t(
|
||||||
'By using the Vega Governance App, you acknowledge that you have read and understood the'
|
'By using the Vega Governance App, you acknowledge that you have read and understood the'
|
||||||
)}{' '}
|
)}{' '}
|
||||||
|
@ -1,39 +1,25 @@
|
|||||||
import {
|
import {
|
||||||
ConnectDialogWithRiskAck,
|
VegaConnectDialog,
|
||||||
useDialogStore,
|
VegaManageDialog,
|
||||||
} from '@vegaprotocol/wallet-react';
|
ViewAsDialog,
|
||||||
|
} from '@vegaprotocol/wallet';
|
||||||
import {
|
import {
|
||||||
AppStateActionType,
|
AppStateActionType,
|
||||||
useAppState,
|
useAppState,
|
||||||
} from '../../contexts/app-state/app-state-context';
|
} from '../../contexts/app-state/app-state-context';
|
||||||
|
import { useConnectors } from '../../lib/vega-connectors';
|
||||||
import { RiskMessage } from './risk-message';
|
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 = () => {
|
export const VegaWalletDialogs = () => {
|
||||||
const { VEGA_ENV } = useEnvironment();
|
|
||||||
const { appState, appDispatch } = useAppState();
|
const { appState, appDispatch } = useAppState();
|
||||||
const [riskAccepted, setRiskAccepted] = useLocalStorage(
|
const connectors = useConnectors();
|
||||||
'vega_wallet_risk_accepted'
|
|
||||||
);
|
|
||||||
const vegaWalletDialogOpen = useDialogStore((store) => store.isOpen);
|
|
||||||
const setVegaWalletDialog = useDialogStore((store) => store.set);
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ConnectDialogWithRiskAck
|
<VegaConnectDialog
|
||||||
open={vegaWalletDialogOpen}
|
connectors={connectors}
|
||||||
onChange={setVegaWalletDialog}
|
riskMessage={<RiskMessage />}
|
||||||
riskAccepted={
|
|
||||||
VEGA_ENV === Networks.TESTNET ? riskAccepted === 'true' : true
|
|
||||||
}
|
|
||||||
riskAckContent={<RiskMessage />}
|
|
||||||
onRiskAccepted={() => setRiskAccepted('true')}
|
|
||||||
onRiskRejected={() => {
|
|
||||||
setRiskAccepted('false');
|
|
||||||
setVegaWalletDialog(false);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<VegaManageDialog
|
<VegaManageDialog
|
||||||
dialogOpen={appState.vegaWalletManageOverlay}
|
dialogOpen={appState.vegaWalletManageOverlay}
|
||||||
setDialogOpen={(open) =>
|
setDialogOpen={(open) =>
|
||||||
@ -43,6 +29,8 @@ export const VegaWalletDialogs = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<ViewAsDialog connector={connectors.view} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -4,13 +4,14 @@ import keyBy from 'lodash/keyBy';
|
|||||||
import uniq from 'lodash/uniq';
|
import uniq from 'lodash/uniq';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { ENV } from '../../config';
|
||||||
|
|
||||||
import noIcon from '../../images/token-no-icon.png';
|
import noIcon from '../../images/token-no-icon.png';
|
||||||
import vegaBlack from '../../images/vega_black.png';
|
import vegaBlack from '../../images/vega_black.png';
|
||||||
import vegaVesting from '../../images/vega_vesting.png';
|
import vegaVesting from '../../images/vega_vesting.png';
|
||||||
import { BigNumber } from '../../lib/bignumber';
|
import { BigNumber } from '../../lib/bignumber';
|
||||||
import { type WalletCardAssetProps } from '../wallet-card';
|
import { type WalletCardAssetProps } from '../wallet-card';
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet-react';
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
import { useContracts } from '../../contexts/contracts/contracts-context';
|
import { useContracts } from '../../contexts/contracts/contracts-context';
|
||||||
import * as Schema from '@vegaprotocol/types';
|
import * as Schema from '@vegaprotocol/types';
|
||||||
import {
|
import {
|
||||||
@ -36,6 +37,7 @@ export const usePollForDelegations = () => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { pubKey } = useVegaWallet();
|
const { pubKey } = useVegaWallet();
|
||||||
const client = useApolloClient();
|
const client = useApolloClient();
|
||||||
|
const { delegationsPagination } = ENV;
|
||||||
const [delegations, setDelegations] = React.useState<
|
const [delegations, setDelegations] = React.useState<
|
||||||
WalletDelegationFieldsFragment[]
|
WalletDelegationFieldsFragment[]
|
||||||
>([]);
|
>([]);
|
||||||
@ -66,9 +68,11 @@ export const usePollForDelegations = () => {
|
|||||||
query: DelegationsDocument,
|
query: DelegationsDocument,
|
||||||
variables: {
|
variables: {
|
||||||
partyId: pubKey,
|
partyId: pubKey,
|
||||||
delegationsPagination: {
|
delegationsPagination: delegationsPagination
|
||||||
first: 50,
|
? {
|
||||||
},
|
first: Number(delegationsPagination),
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
},
|
},
|
||||||
fetchPolicy: 'network-only',
|
fetchPolicy: 'network-only',
|
||||||
})
|
})
|
||||||
@ -232,14 +236,14 @@ export const usePollForDelegations = () => {
|
|||||||
// will just continue to fail
|
// will just continue to fail
|
||||||
clearInterval(interval);
|
clearInterval(interval);
|
||||||
});
|
});
|
||||||
}, 20000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
clearInterval(interval);
|
clearInterval(interval);
|
||||||
mounted = false;
|
mounted = false;
|
||||||
};
|
};
|
||||||
}, [client, decimals, pubKey, t, vegaToken.address]);
|
}, [delegationsPagination, client, decimals, pubKey, t, vegaToken.address]);
|
||||||
|
|
||||||
return { delegations, currentStakeAvailable, delegatedNodes, accounts };
|
return { delegations, currentStakeAvailable, delegatedNodes, accounts };
|
||||||
};
|
};
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { ButtonLink, Link } from '@vegaprotocol/ui-toolkit';
|
import { ButtonLink, Link } from '@vegaprotocol/ui-toolkit';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { ExternalLinks } from '@vegaprotocol/environment';
|
import { ExternalLinks } from '@vegaprotocol/environment';
|
||||||
import { useConnect } from '@vegaprotocol/wallet-react';
|
import { useViewAsDialog } from '@vegaprotocol/wallet';
|
||||||
|
|
||||||
export const VegaWalletPrompt = () => {
|
export const VegaWalletPrompt = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { connect } = useConnect();
|
const setViewAsDialog = useViewAsDialog((state) => state.setOpen);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h3 className="mt-4 mb-2">{t('getWallet')}</h3>
|
<h3 className="mt-4 mb-2">{t('getWallet')}</h3>
|
||||||
@ -16,7 +16,7 @@ export const VegaWalletPrompt = () => {
|
|||||||
<ButtonLink
|
<ButtonLink
|
||||||
className="text-neutral-500"
|
className="text-neutral-500"
|
||||||
data-testid="view-as-user"
|
data-testid="view-as-user"
|
||||||
onClick={() => connect('viewParty')}
|
onClick={() => setViewAsDialog(true)}
|
||||||
>
|
>
|
||||||
{t('viewAsParty')}
|
{t('viewAsParty')}
|
||||||
</ButtonLink>
|
</ButtonLink>
|
||||||
|
@ -25,7 +25,7 @@ import {
|
|||||||
} from '../wallet-card';
|
} from '../wallet-card';
|
||||||
import { VegaWalletPrompt } from './vega-wallet-prompt';
|
import { VegaWalletPrompt } from './vega-wallet-prompt';
|
||||||
import { usePollForDelegations } from './hooks';
|
import { usePollForDelegations } from './hooks';
|
||||||
import { useVegaWallet, useDialogStore } from '@vegaprotocol/wallet-react';
|
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
|
||||||
import { Button, ButtonLink } from '@vegaprotocol/ui-toolkit';
|
import { Button, ButtonLink } from '@vegaprotocol/ui-toolkit';
|
||||||
import { toBigNum } from '@vegaprotocol/utils';
|
import { toBigNum } from '@vegaprotocol/utils';
|
||||||
import { usePendingBalancesStore } from '../../hooks/use-pending-balances-manager';
|
import { usePendingBalancesStore } from '../../hooks/use-pending-balances-manager';
|
||||||
@ -34,17 +34,16 @@ import omit from 'lodash/omit';
|
|||||||
|
|
||||||
export const VegaWallet = () => {
|
export const VegaWallet = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { status, pubKey, pubKeys } = useVegaWallet();
|
const { pubKey, pubKeys } = useVegaWallet();
|
||||||
const pubKeyObj = useMemo(() => {
|
const pubKeyObj = useMemo(() => {
|
||||||
return pubKeys?.find((pk) => pk.publicKey === pubKey);
|
return pubKeys?.find((pk) => pk.publicKey === pubKey);
|
||||||
}, [pubKey, pubKeys]);
|
}, [pubKey, pubKeys]);
|
||||||
|
|
||||||
const child =
|
const child = !pubKeys ? (
|
||||||
status === 'connected' ? (
|
<VegaWalletNotConnected />
|
||||||
<VegaWalletConnected vegaKeys={pubKeys.map((pk) => pk.publicKey)} />
|
) : (
|
||||||
) : (
|
<VegaWalletConnected vegaKeys={pubKeys.map((pk) => pk.publicKey)} />
|
||||||
<VegaWalletNotConnected />
|
);
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="vega-wallet" data-testid="vega-wallet">
|
<section className="vega-wallet" data-testid="vega-wallet">
|
||||||
@ -76,7 +75,9 @@ export const VegaWallet = () => {
|
|||||||
|
|
||||||
const VegaWalletNotConnected = () => {
|
const VegaWalletNotConnected = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const openVegaWalletDialog = useDialogStore((store) => store.open);
|
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
|
||||||
|
openVegaWalletDialog: store.openVegaWalletDialog,
|
||||||
|
}));
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
|
@ -65,6 +65,7 @@ export const ENV = {
|
|||||||
docsUrl: windowOrDefault('NX_VEGA_DOCS_URL'),
|
docsUrl: windowOrDefault('NX_VEGA_DOCS_URL'),
|
||||||
ethWalletMnemonic: windowOrDefault('NX_ETH_WALLET_MNEMONIC'),
|
ethWalletMnemonic: windowOrDefault('NX_ETH_WALLET_MNEMONIC'),
|
||||||
localProviderUrl: windowOrDefault('NX_LOCAL_PROVIDER_URL'),
|
localProviderUrl: windowOrDefault('NX_LOCAL_PROVIDER_URL'),
|
||||||
|
delegationsPagination: windowOrDefault('NX_DELEGATIONS_PAGINATION'),
|
||||||
rest: windowOrDefault('NX_VEGA_REST_URL'),
|
rest: windowOrDefault('NX_VEGA_REST_URL'),
|
||||||
addresses:
|
addresses:
|
||||||
ContractAddresses[(envName === 'local' ? 'CUSTOM' : envName) as Networks],
|
ContractAddresses[(envName === 'local' ? 'CUSTOM' : envName) as Networks],
|
||||||
|
@ -111,4 +111,3 @@ export const ContractsProvider = ({ children }: { children: JSX.Element }) => {
|
|||||||
</ContractsContext.Provider>
|
</ContractsContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
ContractsProvider.displayName = 'ContractsProvider';
|
|
||||||
|
@ -7,7 +7,7 @@ import { useWeb3React } from '@web3-react/core';
|
|||||||
|
|
||||||
export const useListenForStakingEvents = (
|
export const useListenForStakingEvents = (
|
||||||
contract: Contract | undefined,
|
contract: Contract | undefined,
|
||||||
vegaPublicKey: string | undefined,
|
vegaPublicKey: string | null,
|
||||||
numberOfConfirmations: number
|
numberOfConfirmations: number
|
||||||
) => {
|
) => {
|
||||||
const { account } = useWeb3React();
|
const { account } = useWeb3React();
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import * as Sentry from '@sentry/react';
|
import * as Sentry from '@sentry/react';
|
||||||
import { toBigNum } from '@vegaprotocol/utils';
|
import { toBigNum } from '@vegaprotocol/utils';
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet-react';
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
import { useEthereumConfig } from '@vegaprotocol/web3';
|
import { useEthereumConfig } from '@vegaprotocol/web3';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
@ -1,81 +0,0 @@
|
|||||||
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];
|
|
||||||
};
|
|
@ -1,41 +0,0 @@
|
|||||||
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',
|
load: 'languageOnly',
|
||||||
debug: isInDev,
|
debug: isInDev,
|
||||||
// have a common namespace used around the full app
|
// have a common namespace used around the full app
|
||||||
ns: ['governance', 'wallet', 'wallet-react'],
|
ns: ['governance'],
|
||||||
defaultNS: 'governance',
|
defaultNS: 'governance',
|
||||||
keySeparator: false, // we use content as keys
|
keySeparator: false, // we use content as keys
|
||||||
nsSeparator: false,
|
nsSeparator: false,
|
||||||
|
@ -20,7 +20,6 @@ describe('getMultisigStatus', () => {
|
|||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
multisigStatus: MultisigStatus.noNodes,
|
multisigStatus: MultisigStatus.noNodes,
|
||||||
showMultisigStatusError: true,
|
showMultisigStatusError: true,
|
||||||
zeroScoreNodes: [],
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -36,7 +35,6 @@ describe('getMultisigStatus', () => {
|
|||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
multisigStatus: MultisigStatus.correct,
|
multisigStatus: MultisigStatus.correct,
|
||||||
showMultisigStatusError: false,
|
showMultisigStatusError: false,
|
||||||
zeroScoreNodes: [],
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -52,22 +50,6 @@ describe('getMultisigStatus', () => {
|
|||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
multisigStatus: MultisigStatus.nodeNeedsRemoving,
|
multisigStatus: MultisigStatus.nodeNeedsRemoving,
|
||||||
showMultisigStatusError: true,
|
showMultisigStatusError: true,
|
||||||
zeroScoreNodes: [
|
|
||||||
{
|
|
||||||
id: '1',
|
|
||||||
rewardScore: {
|
|
||||||
multisigScore: '0',
|
|
||||||
},
|
|
||||||
stakedTotal: '1000',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '2',
|
|
||||||
rewardScore: {
|
|
||||||
multisigScore: '0',
|
|
||||||
},
|
|
||||||
stakedTotal: '1000',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -83,15 +65,6 @@ describe('getMultisigStatus', () => {
|
|||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
multisigStatus: MultisigStatus.nodeNeedsAdding,
|
multisigStatus: MultisigStatus.nodeNeedsAdding,
|
||||||
showMultisigStatusError: true,
|
showMultisigStatusError: true,
|
||||||
zeroScoreNodes: [
|
|
||||||
{
|
|
||||||
id: '1',
|
|
||||||
rewardScore: {
|
|
||||||
multisigScore: '0',
|
|
||||||
},
|
|
||||||
stakedTotal: '1000',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
||||||
import type {
|
import type { PreviousEpochQuery } from '../routes/staking/__generated__/PreviousEpoch';
|
||||||
PreviousEpochQuery,
|
|
||||||
ValidatorNodeFragment,
|
|
||||||
} from '../routes/staking/__generated__/PreviousEpoch';
|
|
||||||
|
|
||||||
export enum MultisigStatus {
|
export enum MultisigStatus {
|
||||||
'correct' = 'correct',
|
'correct' = 'correct',
|
||||||
@ -20,15 +17,12 @@ export const getMultisigStatusInfo = (
|
|||||||
previousEpochData?.epoch.validatorsConnection?.edges
|
previousEpochData?.epoch.validatorsConnection?.edges
|
||||||
);
|
);
|
||||||
|
|
||||||
const zeroScore = (node: ValidatorNodeFragment) =>
|
const hasZero = allNodesInPreviousEpoch.some(
|
||||||
Number(node.rewardScore?.multisigScore) === 0;
|
(node) => Number(node?.rewardScore?.multisigScore) === 0
|
||||||
const oneScore = (node: ValidatorNodeFragment) =>
|
);
|
||||||
Number(node.rewardScore?.multisigScore) === 1;
|
const hasOne = allNodesInPreviousEpoch.some(
|
||||||
|
(node) => Number(node?.rewardScore?.multisigScore) === 1
|
||||||
const hasZero = allNodesInPreviousEpoch.some(zeroScore);
|
);
|
||||||
const hasOne = allNodesInPreviousEpoch.some(oneScore);
|
|
||||||
|
|
||||||
const zeroScoreNodes = allNodesInPreviousEpoch.filter(zeroScore);
|
|
||||||
|
|
||||||
if (hasZero && hasOne) {
|
if (hasZero && hasOne) {
|
||||||
// If any individual node has 0 it means that node is missing from the multisig and needs to be added
|
// If any individual node has 0 it means that node is missing from the multisig and needs to be added
|
||||||
@ -44,6 +38,5 @@ export const getMultisigStatusInfo = (
|
|||||||
return {
|
return {
|
||||||
showMultisigStatusError: status !== MultisigStatus.correct,
|
showMultisigStatusError: status !== MultisigStatus.correct,
|
||||||
multisigStatus: status,
|
multisigStatus: status,
|
||||||
zeroScoreNodes,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
30
apps/governance/src/lib/vega-connectors.ts
Normal file
30
apps/governance/src/lib/vega-connectors.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
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,4 +1,3 @@
|
|||||||
import compact from 'lodash/compact';
|
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
@ -13,10 +12,10 @@ import { useRefreshAfterEpoch } from '../../hooks/use-refresh-after-epoch';
|
|||||||
import { ProposalsListItem } from '../proposals/components/proposals-list-item';
|
import { ProposalsListItem } from '../proposals/components/proposals-list-item';
|
||||||
import { ProtocolUpgradeProposalsListItem } from '../proposals/components/protocol-upgrade-proposals-list-item/protocol-upgrade-proposals-list-item';
|
import { ProtocolUpgradeProposalsListItem } from '../proposals/components/protocol-upgrade-proposals-list-item/protocol-upgrade-proposals-list-item';
|
||||||
import Routes from '../routes';
|
import Routes from '../routes';
|
||||||
import { ExternalLinks } from '@vegaprotocol/environment';
|
import { ExternalLinks, useFeatureFlags } from '@vegaprotocol/environment';
|
||||||
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
||||||
import { useNodesQuery } from '../staking/home/__generated__/Nodes';
|
import { useNodesQuery } from '../staking/home/__generated__/Nodes';
|
||||||
import { useProposalsQuery } from '../proposals/__generated__/Proposals';
|
import { useProposalsQuery } from '../proposals/proposals/__generated__/Proposals';
|
||||||
import {
|
import {
|
||||||
getNotRejectedProposals,
|
getNotRejectedProposals,
|
||||||
getNotRejectedProtocolUpgradeProposals,
|
getNotRejectedProtocolUpgradeProposals,
|
||||||
@ -32,7 +31,7 @@ import {
|
|||||||
orderByUpgradeBlockHeight,
|
orderByUpgradeBlockHeight,
|
||||||
} from '../proposals/components/proposals-list/proposals-list';
|
} from '../proposals/components/proposals-list/proposals-list';
|
||||||
import { BigNumber } from '../../lib/bignumber';
|
import { BigNumber } from '../../lib/bignumber';
|
||||||
import { type Proposal, type BatchProposal } from '../proposals/types';
|
import { type Proposal } from '../proposals/types';
|
||||||
|
|
||||||
const nodesToShow = 6;
|
const nodesToShow = 6;
|
||||||
|
|
||||||
@ -40,7 +39,7 @@ const HomeProposals = ({
|
|||||||
proposals,
|
proposals,
|
||||||
protocolUpgradeProposals,
|
protocolUpgradeProposals,
|
||||||
}: {
|
}: {
|
||||||
proposals: Array<Proposal | BatchProposal>;
|
proposals: Proposal[];
|
||||||
protocolUpgradeProposals: ProtocolUpgradeProposalFieldsFragment[];
|
protocolUpgradeProposals: ProtocolUpgradeProposalFieldsFragment[];
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -61,9 +60,12 @@ const HomeProposals = ({
|
|||||||
<ProtocolUpgradeProposalsListItem key={index} proposal={proposal} />
|
<ProtocolUpgradeProposalsListItem key={index} proposal={proposal} />
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{compact(proposals).map((proposal) => {
|
{proposals.map(
|
||||||
return <ProposalsListItem key={proposal.id} proposal={proposal} />;
|
(proposal) =>
|
||||||
})}
|
proposal?.id && (
|
||||||
|
<ProposalsListItem key={proposal.id} proposal={proposal} />
|
||||||
|
)
|
||||||
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div className="mt-6">
|
<div className="mt-6">
|
||||||
@ -173,6 +175,7 @@ export const ValidatorDetailsLink = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const GovernanceHome = ({ name }: RouteChildProps) => {
|
const GovernanceHome = ({ name }: RouteChildProps) => {
|
||||||
|
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||||
useDocumentTitle(name);
|
useDocumentTitle(name);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const {
|
const {
|
||||||
@ -183,6 +186,11 @@ const GovernanceHome = ({ name }: RouteChildProps) => {
|
|||||||
pollInterval: 5000,
|
pollInterval: 5000,
|
||||||
fetchPolicy: 'network-only',
|
fetchPolicy: 'network-only',
|
||||||
errorPolicy: 'ignore',
|
errorPolicy: 'ignore',
|
||||||
|
variables: {
|
||||||
|
includeNewMarketProductFields: !!featureFlags.PRODUCT_PERPETUALS,
|
||||||
|
includeUpdateMarketStates: !!featureFlags.UPDATE_MARKET_STATE,
|
||||||
|
includeUpdateReferralPrograms: !!featureFlags.REFERRALS,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -204,18 +212,15 @@ const GovernanceHome = ({ name }: RouteChildProps) => {
|
|||||||
|
|
||||||
useRefreshAfterEpoch(validatorsData?.epoch.timestamps.expiry, refetch);
|
useRefreshAfterEpoch(validatorsData?.epoch.timestamps.expiry, refetch);
|
||||||
|
|
||||||
const proposals = useMemo(() => {
|
const proposals = useMemo(
|
||||||
if (!proposalsData?.proposalsConnection?.edges?.length) return [];
|
() =>
|
||||||
return proposalsData
|
proposalsData
|
||||||
? getNotRejectedProposals(
|
? getNotRejectedProposals(
|
||||||
compact(
|
removePaginationWrapper(proposalsData.proposalsConnection?.edges)
|
||||||
proposalsData.proposalsConnection.edges.map(
|
|
||||||
(edge) => edge?.proposalNode
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
: [],
|
||||||
: [];
|
[proposalsData]
|
||||||
}, [proposalsData]);
|
);
|
||||||
|
|
||||||
const sortedProposals = useMemo(
|
const sortedProposals = useMemo(
|
||||||
() => orderByDate(proposals).reverse(),
|
() => orderByDate(proposals).reverse(),
|
||||||
|
@ -1,493 +0,0 @@
|
|||||||
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
@ -3,13 +3,9 @@ import { ProposalState } from '@vegaprotocol/types';
|
|||||||
import { ProposalInfoLabel } from '../proposal-info-label';
|
import { ProposalInfoLabel } from '../proposal-info-label';
|
||||||
import { type ReactNode } from 'react';
|
import { type ReactNode } from 'react';
|
||||||
import { type ProposalInfoLabelVariant } from '../proposal-info-label';
|
import { type ProposalInfoLabelVariant } from '../proposal-info-label';
|
||||||
import { type Proposal, type BatchProposal } from '../../types';
|
import { type Proposal } from '../../types';
|
||||||
|
|
||||||
export const CurrentProposalState = ({
|
export const CurrentProposalState = ({ proposal }: { proposal: Proposal }) => {
|
||||||
proposal,
|
|
||||||
}: {
|
|
||||||
proposal: Proposal | BatchProposal;
|
|
||||||
}) => {
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
let proposalStatus: ReactNode;
|
let proposalStatus: ReactNode;
|
||||||
let variant = 'tertiary' as ProposalInfoLabelVariant;
|
let variant = 'tertiary' as ProposalInfoLabelVariant;
|
||||||
|
@ -79,22 +79,13 @@ export const ListAsset = ({
|
|||||||
) {
|
) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
if (data.asset.source.__typename !== 'ERC20') return null;
|
||||||
if (data.asset.source.__typename !== 'ERC20') {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.asset.status !== Schema.AssetStatus.STATUS_PENDING_LISTING) {
|
if (data.asset.status !== Schema.AssetStatus.STATUS_PENDING_LISTING) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
if (errorAsset || errorBundle) return null;
|
||||||
if (errorAsset || errorBundle) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { assetSource, signatures, vegaAssetId, nonce } =
|
const { assetSource, signatures, vegaAssetId, nonce } =
|
||||||
assetData.erc20ListAssetBundle;
|
assetData.erc20ListAssetBundle;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mb-8">
|
<div className="mb-8">
|
||||||
<h3 className="mb-2 text-xl">{t('ListAsset')}</h3>
|
<h3 className="mb-2 text-xl">{t('ListAsset')}</h3>
|
||||||
|
@ -2,53 +2,19 @@ import { useState } from 'react';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { SubHeading } from '../../../../components/heading';
|
import { SubHeading } from '../../../../components/heading';
|
||||||
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
|
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
|
||||||
import {
|
import { AssetDetail, AssetDetailsTable } from '@vegaprotocol/assets';
|
||||||
AssetDetail,
|
import type { AssetFieldsFragment } from '@vegaprotocol/assets';
|
||||||
AssetDetailsTable,
|
|
||||||
useAssetQuery,
|
|
||||||
} from '@vegaprotocol/assets';
|
|
||||||
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
|
||||||
import {
|
|
||||||
type INewAssetFieldsFragment,
|
|
||||||
type IUpdateAssetFieldsFragment,
|
|
||||||
} from '../../__generated__/Proposals';
|
|
||||||
|
|
||||||
export const ProposalAssetDetails = ({
|
export const ProposalAssetDetails = ({
|
||||||
change,
|
asset,
|
||||||
assetId,
|
originalAsset,
|
||||||
}: {
|
}: {
|
||||||
change: IUpdateAssetFieldsFragment | INewAssetFieldsFragment;
|
asset: AssetFieldsFragment;
|
||||||
assetId: string;
|
originalAsset?: AssetFieldsFragment;
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [showAssetDetails, setShowAssetDetails] = useState(false);
|
const [showAssetDetails, setShowAssetDetails] = useState(false);
|
||||||
|
|
||||||
const { data } = useAssetQuery({
|
|
||||||
fetchPolicy: 'network-only',
|
|
||||||
variables: {
|
|
||||||
assetId,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!data) return null;
|
|
||||||
|
|
||||||
let asset = removePaginationWrapper(data?.assetsConnection?.edges)[0];
|
|
||||||
|
|
||||||
const originalAsset = asset;
|
|
||||||
|
|
||||||
if (change.__typename === 'UpdateAsset') {
|
|
||||||
asset = {
|
|
||||||
...asset,
|
|
||||||
quantum: change.quantum,
|
|
||||||
source: { ...asset.source },
|
|
||||||
};
|
|
||||||
|
|
||||||
if (asset.source.__typename === 'ERC20') {
|
|
||||||
asset.source.lifetimeLimit = change.source.lifetimeLimit;
|
|
||||||
asset.source.withdrawThreshold = change.source.withdrawThreshold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section data-testid="proposal-asset-details">
|
<section data-testid="proposal-asset-details">
|
||||||
<CollapsibleToggle
|
<CollapsibleToggle
|
||||||
|
@ -6,46 +6,16 @@ import {
|
|||||||
KeyValueTableRow,
|
KeyValueTableRow,
|
||||||
RoundedWrapper,
|
RoundedWrapper,
|
||||||
} from '@vegaprotocol/ui-toolkit';
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
import { type Proposal, type BatchProposal } from '../../types';
|
import { type Proposal } from '../../types';
|
||||||
|
|
||||||
interface ProposalChangeTableProps {
|
interface ProposalChangeTableProps {
|
||||||
proposal: Proposal | BatchProposal;
|
proposal: Proposal;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ProposalChangeTable = ({ proposal }: ProposalChangeTableProps) => {
|
export const ProposalChangeTable = ({ proposal }: ProposalChangeTableProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const closingTimeRow =
|
const terms = proposal?.terms;
|
||||||
proposal.__typename === 'Proposal' ? (
|
|
||||||
<KeyValueTableRow>
|
|
||||||
{isFuture(new Date(proposal.terms?.closingDatetime))
|
|
||||||
? t('closesOn')
|
|
||||||
: t('closedOn')}
|
|
||||||
{formatDateWithLocalTimezone(new Date(proposal.terms?.closingDatetime))}
|
|
||||||
</KeyValueTableRow>
|
|
||||||
) : proposal.__typename === 'BatchProposal' ? (
|
|
||||||
<KeyValueTableRow>
|
|
||||||
{isFuture(new Date(proposal.batchTerms?.closingDatetime))
|
|
||||||
? t('closesOn')
|
|
||||||
: t('closedOn')}
|
|
||||||
{formatDateWithLocalTimezone(
|
|
||||||
new Date(proposal.batchTerms?.closingDatetime)
|
|
||||||
)}
|
|
||||||
</KeyValueTableRow>
|
|
||||||
) : null;
|
|
||||||
|
|
||||||
const enactmentRow =
|
|
||||||
proposal.__typename === 'Proposal' &&
|
|
||||||
proposal.terms.change.__typename !== 'NewFreeform' ? (
|
|
||||||
<KeyValueTableRow>
|
|
||||||
{isFuture(new Date(proposal.terms?.enactmentDatetime || 0))
|
|
||||||
? t('proposedEnactment')
|
|
||||||
: t('enactedOn')}
|
|
||||||
{formatDateWithLocalTimezone(
|
|
||||||
new Date(proposal.terms?.enactmentDatetime || 0)
|
|
||||||
)}
|
|
||||||
</KeyValueTableRow>
|
|
||||||
) : null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RoundedWrapper paddingBottom={true}>
|
<RoundedWrapper paddingBottom={true}>
|
||||||
@ -54,8 +24,22 @@ export const ProposalChangeTable = ({ proposal }: ProposalChangeTableProps) => {
|
|||||||
{t('id')}
|
{t('id')}
|
||||||
{proposal?.id}
|
{proposal?.id}
|
||||||
</KeyValueTableRow>
|
</KeyValueTableRow>
|
||||||
{closingTimeRow}
|
<KeyValueTableRow>
|
||||||
{enactmentRow}
|
{isFuture(new Date(terms?.closingDatetime))
|
||||||
|
? t('closesOn')
|
||||||
|
: t('closedOn')}
|
||||||
|
{formatDateWithLocalTimezone(new Date(terms?.closingDatetime))}
|
||||||
|
</KeyValueTableRow>
|
||||||
|
{terms?.change.__typename !== 'NewFreeform' ? (
|
||||||
|
<KeyValueTableRow>
|
||||||
|
{isFuture(new Date(terms?.enactmentDatetime || 0))
|
||||||
|
? t('proposedEnactment')
|
||||||
|
: t('enactedOn')}
|
||||||
|
{formatDateWithLocalTimezone(
|
||||||
|
new Date(terms?.enactmentDatetime || 0)
|
||||||
|
)}
|
||||||
|
</KeyValueTableRow>
|
||||||
|
) : null}
|
||||||
<KeyValueTableRow>
|
<KeyValueTableRow>
|
||||||
{t('proposedBy')}
|
{t('proposedBy')}
|
||||||
<span style={{ wordBreak: 'break-word' }}>{proposal?.party.id}</span>
|
<span style={{ wordBreak: 'break-word' }}>{proposal?.party.id}</span>
|
||||||
|
@ -5,6 +5,7 @@ import {
|
|||||||
ProposalState,
|
ProposalState,
|
||||||
VoteValue,
|
VoteValue,
|
||||||
} from '@vegaprotocol/types';
|
} from '@vegaprotocol/types';
|
||||||
|
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||||
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
||||||
import {
|
import {
|
||||||
generateNoVotes,
|
generateNoVotes,
|
||||||
@ -15,21 +16,22 @@ import { ProposalHeader, NewTransferSummary } from './proposal-header';
|
|||||||
import {
|
import {
|
||||||
lastWeek,
|
lastWeek,
|
||||||
nextWeek,
|
nextWeek,
|
||||||
|
mockWalletContext,
|
||||||
createUserVoteQueryMock,
|
createUserVoteQueryMock,
|
||||||
} from '../../test-helpers/mocks';
|
} from '../../test-helpers/mocks';
|
||||||
|
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||||
import { BrowserRouter } from 'react-router-dom';
|
import { BrowserRouter } from 'react-router-dom';
|
||||||
import { VoteState } from '../vote-details/use-user-vote';
|
import { VoteState } from '../vote-details/use-user-vote';
|
||||||
import {
|
import { useNewTransferProposalDetails } from '@vegaprotocol/proposals';
|
||||||
InstrumentDetailsDocument,
|
|
||||||
useNewTransferProposalDetails,
|
|
||||||
type InstrumentDetailsQuery,
|
|
||||||
type InstrumentDetailsQueryVariables,
|
|
||||||
} from '@vegaprotocol/proposals';
|
|
||||||
import { type MockedResponse } from '@apollo/client/testing';
|
import { type MockedResponse } from '@apollo/client/testing';
|
||||||
import { type Proposal } from '../../types';
|
import { type Proposal } from '../../types';
|
||||||
|
|
||||||
jest.mock('@vegaprotocol/proposals', () => ({
|
jest.mock('@vegaprotocol/proposals', () => ({
|
||||||
...jest.requireActual('@vegaprotocol/proposals'),
|
...jest.requireActual('@vegaprotocol/proposals'),
|
||||||
|
useSuccessorMarketProposalDetails: () => ({
|
||||||
|
code: 'PARENT_CODE',
|
||||||
|
parentMarketId: 'PARENT_ID',
|
||||||
|
}),
|
||||||
useNewTransferProposalDetails: jest.fn(),
|
useNewTransferProposalDetails: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -43,11 +45,13 @@ const renderComponent = (
|
|||||||
<AppStateProvider>
|
<AppStateProvider>
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<MockedProvider mocks={mocks}>
|
<MockedProvider mocks={mocks}>
|
||||||
<ProposalHeader
|
<VegaWalletContext.Provider value={mockWalletContext}>
|
||||||
proposal={proposal}
|
<ProposalHeader
|
||||||
isListItem={isListItem}
|
proposal={proposal}
|
||||||
voteState={voteState}
|
isListItem={isListItem}
|
||||||
/>
|
voteState={voteState}
|
||||||
|
/>
|
||||||
|
</VegaWalletContext.Provider>
|
||||||
</MockedProvider>
|
</MockedProvider>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
</AppStateProvider>
|
</AppStateProvider>
|
||||||
@ -57,39 +61,10 @@ describe('Proposal header', () => {
|
|||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
});
|
});
|
||||||
|
it('Renders New market proposal', () => {
|
||||||
it('Renders New market proposal', async () => {
|
useFeatureFlags.setState({ flags: { SUCCESSOR_MARKETS: true } });
|
||||||
const parentMarketId = 'parent-id';
|
|
||||||
const parentCode = 'parent-code';
|
|
||||||
const parentName = 'parent-name';
|
|
||||||
const mock: MockedResponse<
|
|
||||||
InstrumentDetailsQuery,
|
|
||||||
InstrumentDetailsQueryVariables
|
|
||||||
> = {
|
|
||||||
request: {
|
|
||||||
query: InstrumentDetailsDocument,
|
|
||||||
variables: {
|
|
||||||
marketId: parentMarketId,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
result: {
|
|
||||||
data: {
|
|
||||||
market: {
|
|
||||||
__typename: 'Market',
|
|
||||||
tradableInstrument: {
|
|
||||||
__typename: 'TradableInstrument',
|
|
||||||
instrument: {
|
|
||||||
__typename: 'Instrument',
|
|
||||||
code: parentCode,
|
|
||||||
name: parentName,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
renderComponent(
|
renderComponent(
|
||||||
|
// @ts-ignore we aren't using batch yet
|
||||||
generateProposal({
|
generateProposal({
|
||||||
rationale: {
|
rationale: {
|
||||||
title: 'New some market',
|
title: 'New some market',
|
||||||
@ -98,9 +73,6 @@ describe('Proposal header', () => {
|
|||||||
terms: {
|
terms: {
|
||||||
change: {
|
change: {
|
||||||
__typename: 'NewMarket',
|
__typename: 'NewMarket',
|
||||||
successorConfiguration: {
|
|
||||||
parentMarketId,
|
|
||||||
},
|
|
||||||
instrument: {
|
instrument: {
|
||||||
__typename: 'InstrumentConfiguration',
|
__typename: 'InstrumentConfiguration',
|
||||||
name: 'Some market',
|
name: 'Some market',
|
||||||
@ -115,9 +87,7 @@ describe('Proposal header', () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}),
|
})
|
||||||
undefined,
|
|
||||||
[mock]
|
|
||||||
);
|
);
|
||||||
expect(screen.getByTestId('proposal-title')).toHaveTextContent(
|
expect(screen.getByTestId('proposal-title')).toHaveTextContent(
|
||||||
'New some market'
|
'New some market'
|
||||||
@ -126,13 +96,14 @@ describe('Proposal header', () => {
|
|||||||
expect(screen.getByTestId('proposal-details')).toHaveTextContent(
|
expect(screen.getByTestId('proposal-details')).toHaveTextContent(
|
||||||
'tGBP settled future.'
|
'tGBP settled future.'
|
||||||
);
|
);
|
||||||
expect(
|
expect(screen.getByTestId('proposal-successor-info')).toHaveTextContent(
|
||||||
await screen.findByTestId('proposal-successor-info')
|
'PARENT_CODE'
|
||||||
).toHaveTextContent(parentCode);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Renders Update market proposal', () => {
|
it('Renders Update market proposal', () => {
|
||||||
renderComponent(
|
renderComponent(
|
||||||
|
// @ts-ignore we aren't using batch yet
|
||||||
generateProposal({
|
generateProposal({
|
||||||
rationale: {
|
rationale: {
|
||||||
title: 'New market id',
|
title: 'New market id',
|
||||||
@ -155,12 +126,13 @@ describe('Proposal header', () => {
|
|||||||
screen.queryByTestId('proposal-description')
|
screen.queryByTestId('proposal-description')
|
||||||
).not.toBeInTheDocument();
|
).not.toBeInTheDocument();
|
||||||
expect(screen.getByTestId('proposal-details')).toHaveTextContent(
|
expect(screen.getByTestId('proposal-details')).toHaveTextContent(
|
||||||
/Update to market: MarketId/
|
'Update to market ID: MarketId'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Renders New asset proposal - ERC20', () => {
|
it('Renders New asset proposal - ERC20', () => {
|
||||||
renderComponent(
|
renderComponent(
|
||||||
|
// @ts-ignore we aren't using batch yet
|
||||||
generateProposal({
|
generateProposal({
|
||||||
rationale: {
|
rationale: {
|
||||||
title: 'New asset: Fake currency',
|
title: 'New asset: Fake currency',
|
||||||
@ -190,10 +162,8 @@ describe('Proposal header', () => {
|
|||||||
|
|
||||||
it('Renders New asset proposal - BuiltInAsset', () => {
|
it('Renders New asset proposal - BuiltInAsset', () => {
|
||||||
renderComponent(
|
renderComponent(
|
||||||
|
// @ts-ignore we aren't using batch yet
|
||||||
generateProposal({
|
generateProposal({
|
||||||
rationale: {
|
|
||||||
title: 'New asset',
|
|
||||||
},
|
|
||||||
terms: {
|
terms: {
|
||||||
change: {
|
change: {
|
||||||
__typename: 'NewAsset',
|
__typename: 'NewAsset',
|
||||||
@ -207,7 +177,9 @@ describe('Proposal header', () => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
expect(screen.getByTestId('proposal-title')).toHaveTextContent('New asset');
|
expect(screen.getByTestId('proposal-title')).toHaveTextContent(
|
||||||
|
'New asset proposal'
|
||||||
|
);
|
||||||
expect(screen.getByTestId('proposal-type')).toHaveTextContent('New asset');
|
expect(screen.getByTestId('proposal-type')).toHaveTextContent('New asset');
|
||||||
expect(screen.getByTestId('proposal-details')).toHaveTextContent(
|
expect(screen.getByTestId('proposal-details')).toHaveTextContent(
|
||||||
'Symbol: BIA. Max faucet amount mint: 300'
|
'Symbol: BIA. Max faucet amount mint: 300'
|
||||||
@ -216,6 +188,7 @@ describe('Proposal header', () => {
|
|||||||
|
|
||||||
it('Renders Update network', () => {
|
it('Renders Update network', () => {
|
||||||
renderComponent(
|
renderComponent(
|
||||||
|
// @ts-ignore we aren't using batch yet
|
||||||
generateProposal({
|
generateProposal({
|
||||||
rationale: {
|
rationale: {
|
||||||
title: 'Network parameter',
|
title: 'Network parameter',
|
||||||
@ -245,6 +218,7 @@ describe('Proposal header', () => {
|
|||||||
|
|
||||||
it('Renders Freeform proposal - short rationale', () => {
|
it('Renders Freeform proposal - short rationale', () => {
|
||||||
renderComponent(
|
renderComponent(
|
||||||
|
// @ts-ignore we aren't using batch yet
|
||||||
generateProposal({
|
generateProposal({
|
||||||
id: 'short',
|
id: 'short',
|
||||||
rationale: {
|
rationale: {
|
||||||
@ -266,6 +240,7 @@ describe('Proposal header', () => {
|
|||||||
|
|
||||||
it('Renders Freeform proposal - long rationale (105 chars) - listing', () => {
|
it('Renders Freeform proposal - long rationale (105 chars) - listing', () => {
|
||||||
renderComponent(
|
renderComponent(
|
||||||
|
// @ts-ignore we aren't using batch yet
|
||||||
generateProposal({
|
generateProposal({
|
||||||
id: 'long',
|
id: 'long',
|
||||||
rationale: {
|
rationale: {
|
||||||
@ -291,6 +266,7 @@ describe('Proposal header', () => {
|
|||||||
// Remove once proposals have rationale and re-enable above tests
|
// Remove once proposals have rationale and re-enable above tests
|
||||||
it('Renders Freeform proposal - id for title', () => {
|
it('Renders Freeform proposal - id for title', () => {
|
||||||
renderComponent(
|
renderComponent(
|
||||||
|
// @ts-ignore we aren't using batch yet
|
||||||
generateProposal({
|
generateProposal({
|
||||||
id: 'freeform id',
|
id: 'freeform id',
|
||||||
rationale: {
|
rationale: {
|
||||||
@ -347,6 +323,7 @@ describe('Proposal header', () => {
|
|||||||
|
|
||||||
it('Renders proposal state: Enacted', () => {
|
it('Renders proposal state: Enacted', () => {
|
||||||
renderComponent(
|
renderComponent(
|
||||||
|
// @ts-ignore we aren't using batch yet
|
||||||
generateProposal({
|
generateProposal({
|
||||||
state: ProposalState.STATE_ENACTED,
|
state: ProposalState.STATE_ENACTED,
|
||||||
terms: {
|
terms: {
|
||||||
@ -359,6 +336,7 @@ describe('Proposal header', () => {
|
|||||||
|
|
||||||
it('Renders proposal state: Passed', () => {
|
it('Renders proposal state: Passed', () => {
|
||||||
renderComponent(
|
renderComponent(
|
||||||
|
// @ts-ignore we aren't using batch yet
|
||||||
generateProposal({
|
generateProposal({
|
||||||
state: ProposalState.STATE_PASSED,
|
state: ProposalState.STATE_PASSED,
|
||||||
terms: {
|
terms: {
|
||||||
@ -372,6 +350,7 @@ describe('Proposal header', () => {
|
|||||||
|
|
||||||
it('Renders proposal state: Waiting for node vote', () => {
|
it('Renders proposal state: Waiting for node vote', () => {
|
||||||
renderComponent(
|
renderComponent(
|
||||||
|
// @ts-ignore we aren't using batch yet
|
||||||
generateProposal({
|
generateProposal({
|
||||||
state: ProposalState.STATE_WAITING_FOR_NODE_VOTE,
|
state: ProposalState.STATE_WAITING_FOR_NODE_VOTE,
|
||||||
terms: {
|
terms: {
|
||||||
@ -386,6 +365,7 @@ describe('Proposal header', () => {
|
|||||||
|
|
||||||
it('Renders proposal state: Open', () => {
|
it('Renders proposal state: Open', () => {
|
||||||
renderComponent(
|
renderComponent(
|
||||||
|
// @ts-ignore we aren't using batch yet
|
||||||
generateProposal({
|
generateProposal({
|
||||||
state: ProposalState.STATE_OPEN,
|
state: ProposalState.STATE_OPEN,
|
||||||
votes: {
|
votes: {
|
||||||
@ -454,6 +434,8 @@ describe('Proposal header', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
jest.mock('@vegaprotocol/proposals');
|
||||||
|
|
||||||
describe('<NewTransferSummary />', () => {
|
describe('<NewTransferSummary />', () => {
|
||||||
it('renders null if no details are provided', () => {
|
it('renders null if no details are provided', () => {
|
||||||
(useNewTransferProposalDetails as jest.Mock).mockReturnValue(null);
|
(useNewTransferProposalDetails as jest.Mock).mockReturnValue(null);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Trans, useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
CopyWithTooltip,
|
CopyWithTooltip,
|
||||||
Lozenge,
|
Lozenge,
|
||||||
@ -8,13 +8,14 @@ import {
|
|||||||
} from '@vegaprotocol/ui-toolkit';
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
import { shorten } from '@vegaprotocol/utils';
|
import { shorten } from '@vegaprotocol/utils';
|
||||||
import { Heading, SubHeading } from '../../../../components/heading';
|
import { Heading, SubHeading } from '../../../../components/heading';
|
||||||
|
import { type ReactNode } from 'react';
|
||||||
import { truncateMiddle } from '../../../../lib/truncate-middle';
|
import { truncateMiddle } from '../../../../lib/truncate-middle';
|
||||||
import { CurrentProposalState } from '../current-proposal-state';
|
import { CurrentProposalState } from '../current-proposal-state';
|
||||||
import { ProposalInfoLabel } from '../proposal-info-label';
|
import { ProposalInfoLabel } from '../proposal-info-label';
|
||||||
import {
|
import {
|
||||||
useCancelTransferProposalDetails,
|
useCancelTransferProposalDetails,
|
||||||
useInstrumentDetailsQuery,
|
|
||||||
useNewTransferProposalDetails,
|
useNewTransferProposalDetails,
|
||||||
|
useSuccessorMarketProposalDetails,
|
||||||
} from '@vegaprotocol/proposals';
|
} from '@vegaprotocol/proposals';
|
||||||
import {
|
import {
|
||||||
CONSOLE_MARKET_PAGE,
|
CONSOLE_MARKET_PAGE,
|
||||||
@ -26,545 +27,217 @@ import Routes from '../../../routes';
|
|||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { type VoteState } from '../vote-details/use-user-vote';
|
import { type VoteState } from '../vote-details/use-user-vote';
|
||||||
import { VoteBreakdown } from '../vote-breakdown';
|
import { VoteBreakdown } from '../vote-breakdown';
|
||||||
import {
|
import { GovernanceTransferKindMapping } from '@vegaprotocol/types';
|
||||||
GovernanceTransferKindMapping,
|
import { type Proposal } from '../../types';
|
||||||
type ProposalRejectionReason,
|
|
||||||
ProposalRejectionReasonMapping,
|
|
||||||
ProposalState,
|
|
||||||
} from '@vegaprotocol/types';
|
|
||||||
import { type Proposal, type BatchProposal } from '../../types';
|
|
||||||
import { type ProposalTermsFieldsFragment } from '../../__generated__/Proposals';
|
|
||||||
import { differenceInHours, format, formatDistanceToNowStrict } from 'date-fns';
|
|
||||||
import { DATE_FORMAT_DETAILED } from '../../../../lib/date-formats';
|
|
||||||
import { MarketName } from '../proposal/market-name';
|
|
||||||
import { Indicator } from '../proposal/indicator';
|
|
||||||
import { type ProposalNode } from '../proposal/proposal-utils';
|
|
||||||
|
|
||||||
const ProposalTypeTags = ({
|
|
||||||
proposal,
|
|
||||||
}: {
|
|
||||||
proposal: Proposal | BatchProposal;
|
|
||||||
}) => {
|
|
||||||
if (proposal.__typename === 'Proposal') {
|
|
||||||
return (
|
|
||||||
<div data-testid="proposal-type">
|
|
||||||
<ProposalTypeTag terms={proposal.terms} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (proposal.__typename === 'BatchProposal') {
|
|
||||||
return (
|
|
||||||
<div data-testid="proposal-type">
|
|
||||||
<ProposalInfoLabel variant="secondary">BatchProposal</ProposalInfoLabel>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const ProposalTypeTag = ({ terms }: { terms: ProposalTermsFieldsFragment }) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
switch (terms.change.__typename) {
|
|
||||||
// Speical case for markets where we want to show the product type in the tag
|
|
||||||
case 'NewMarket': {
|
|
||||||
return (
|
|
||||||
<ProposalInfoLabel variant="secondary">
|
|
||||||
{t(
|
|
||||||
terms.change?.instrument?.product?.__typename
|
|
||||||
? `NewMarket${terms.change.instrument.product.__typename}`
|
|
||||||
: 'NewMarket'
|
|
||||||
)}
|
|
||||||
</ProposalInfoLabel>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
return (
|
|
||||||
<ProposalInfoLabel variant="secondary">
|
|
||||||
{t(terms.change.__typename)}
|
|
||||||
</ProposalInfoLabel>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const ProposalDetails = ({
|
|
||||||
proposal,
|
|
||||||
}: {
|
|
||||||
proposal: Proposal | BatchProposal;
|
|
||||||
}) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const featureFlags = useFeatureFlags((store) => store.flags);
|
|
||||||
const consoleLink = useLinks(DApp.Console);
|
|
||||||
|
|
||||||
const renderDetails = (terms: ProposalTermsFieldsFragment) => {
|
|
||||||
switch (terms.change?.__typename) {
|
|
||||||
case 'NewMarket': {
|
|
||||||
const getAsset = (terms: ProposalTermsFieldsFragment) => {
|
|
||||||
if (
|
|
||||||
terms?.change.__typename === 'NewMarket' &&
|
|
||||||
(terms.change.instrument.product?.__typename === 'FutureProduct' ||
|
|
||||||
terms.change.instrument.product?.__typename ===
|
|
||||||
'PerpetualProduct')
|
|
||||||
) {
|
|
||||||
return terms.change.instrument.product.settlementAsset;
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{terms.change.successorConfiguration && (
|
|
||||||
<ParentMarketCode
|
|
||||||
parentMarketId={
|
|
||||||
terms.change.successorConfiguration.parentMarketId
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<span>
|
|
||||||
{t('Code')}: {terms.change.instrument.code}.
|
|
||||||
</span>{' '}
|
|
||||||
{terms && getAsset(terms)?.symbol ? (
|
|
||||||
<>
|
|
||||||
<span className="font-semibold">{getAsset(terms)?.symbol}</span>{' '}
|
|
||||||
{t('settled future')}.
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
''
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case 'UpdateMarketState': {
|
|
||||||
const marketPageLink = consoleLink(
|
|
||||||
CONSOLE_MARKET_PAGE.replace(':marketId', terms.change.market.id)
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<span>
|
|
||||||
{featureFlags.UPDATE_MARKET_STATE &&
|
|
||||||
terms.change?.market?.id &&
|
|
||||||
terms.change.updateType ? (
|
|
||||||
<>
|
|
||||||
<span>{t(terms.change.updateType)}: </span>
|
|
||||||
<span className="inline-flex gap-2">
|
|
||||||
<span className="break-all">
|
|
||||||
<MarketName marketId={terms.change.market.id} />
|
|
||||||
</span>
|
|
||||||
<span className="inline-flex items-end gap-0">
|
|
||||||
<CopyWithTooltip
|
|
||||||
text={terms.change.market.id}
|
|
||||||
description={t('copyId')}
|
|
||||||
>
|
|
||||||
<button className="inline-block px-1">
|
|
||||||
<VegaIcon size={20} name={VegaIconNames.COPY} />
|
|
||||||
</button>
|
|
||||||
</CopyWithTooltip>
|
|
||||||
<Tooltip description={t('OpenInConsole')} align="center">
|
|
||||||
<Link
|
|
||||||
className="inline-block px-1"
|
|
||||||
to={marketPageLink}
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
<VegaIcon
|
|
||||||
size={20}
|
|
||||||
name={VegaIconNames.OPEN_EXTERNAL}
|
|
||||||
/>
|
|
||||||
</Link>
|
|
||||||
</Tooltip>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</>
|
|
||||||
) : null}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case 'UpdateMarket': {
|
|
||||||
const marketPageLink = consoleLink(
|
|
||||||
CONSOLE_MARKET_PAGE.replace(':marketId', terms.change.marketId)
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<span>{t('UpdateToMarket')}: </span>
|
|
||||||
<span className="inline-flex items-start gap-2">
|
|
||||||
<span className="break-all">
|
|
||||||
<MarketName marketId={terms.change.marketId} />
|
|
||||||
</span>
|
|
||||||
<span className="inline-flex items-end gap-0">
|
|
||||||
<CopyWithTooltip
|
|
||||||
text={terms.change.marketId}
|
|
||||||
description={t('copyId')}
|
|
||||||
>
|
|
||||||
<button className="inline-block px-1">
|
|
||||||
<VegaIcon size={20} name={VegaIconNames.COPY} />
|
|
||||||
</button>
|
|
||||||
</CopyWithTooltip>
|
|
||||||
<Tooltip description={t('OpenInConsole')} align="center">
|
|
||||||
<Link
|
|
||||||
className="inline-block px-1"
|
|
||||||
target="_blank"
|
|
||||||
to={marketPageLink}
|
|
||||||
>
|
|
||||||
<VegaIcon size={20} name={VegaIconNames.OPEN_EXTERNAL} />
|
|
||||||
</Link>
|
|
||||||
</Tooltip>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case 'UpdateReferralProgram': {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
case 'UpdateVolumeDiscountProgram': {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
case 'NewAsset': {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<span>{t('Symbol')}:</span>{' '}
|
|
||||||
<Lozenge>{terms.change.symbol}.</Lozenge>{' '}
|
|
||||||
{terms.change.source.__typename === 'ERC20' && (
|
|
||||||
<>
|
|
||||||
<span>{t('ERC20ContractAddress')}:</span>{' '}
|
|
||||||
<Lozenge>{terms.change.source.contractAddress}</Lozenge>
|
|
||||||
</>
|
|
||||||
)}{' '}
|
|
||||||
{terms.change.source.__typename === 'BuiltinAsset' && (
|
|
||||||
<>
|
|
||||||
<span>{t('MaxFaucetAmountMint')}:</span>{' '}
|
|
||||||
<Lozenge>{terms.change.source.maxFaucetAmountMint}</Lozenge>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case 'UpdateNetworkParameter': {
|
|
||||||
return (
|
|
||||||
<Trans
|
|
||||||
i18nKey="Change <lozenge>{{key}}</lozenge> to <lozenge>{{value}}</lozenge>"
|
|
||||||
values={{
|
|
||||||
key: terms.change.networkParameter.key,
|
|
||||||
value: terms.change.networkParameter.value,
|
|
||||||
}}
|
|
||||||
components={{
|
|
||||||
// @ts-ignore children passed by i18next
|
|
||||||
lozenge: <Lozenge className="text-xs" />,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case 'NewFreeform': {
|
|
||||||
return <span />;
|
|
||||||
}
|
|
||||||
case 'UpdateAsset': {
|
|
||||||
return (
|
|
||||||
<Trans
|
|
||||||
i18nKey="Asset ID: <lozenge>{{id}}</lozenge>"
|
|
||||||
values={{
|
|
||||||
id: truncateMiddle(terms.change.assetId),
|
|
||||||
}}
|
|
||||||
components={{
|
|
||||||
// @ts-ignore children passed by i18next
|
|
||||||
lozenge: <Lozenge />,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case 'NewTransfer':
|
|
||||||
return featureFlags.GOVERNANCE_TRANSFERS ? (
|
|
||||||
<NewTransferSummary proposalId={proposal?.id} />
|
|
||||||
) : null;
|
|
||||||
case 'CancelTransfer':
|
|
||||||
return featureFlags.GOVERNANCE_TRANSFERS ? (
|
|
||||||
<CancelTransferSummary proposalId={proposal?.id} />
|
|
||||||
) : null;
|
|
||||||
default: {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let details = null;
|
|
||||||
|
|
||||||
if (proposal.__typename === 'Proposal') {
|
|
||||||
details = (
|
|
||||||
<div>
|
|
||||||
<div>{renderDetails(proposal.terms)}</div>
|
|
||||||
<VoteStateText
|
|
||||||
state={proposal.state}
|
|
||||||
closingDatetime={proposal.terms.closingDatetime}
|
|
||||||
enactmentDatetime={proposal.terms.enactmentDatetime}
|
|
||||||
rejectionReason={proposal.rejectionReason}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (proposal.__typename === 'BatchProposal' && proposal.subProposals) {
|
|
||||||
details = (
|
|
||||||
<div>
|
|
||||||
<h3 className="text-xl border-b border-default pb-3 mb-3">
|
|
||||||
Proposals in batch
|
|
||||||
</h3>
|
|
||||||
<ul className="flex flex-col gap-2 border-b border-default pb-3 mb-3">
|
|
||||||
{proposal.subProposals.map((p, i) => {
|
|
||||||
if (!p?.terms) return null;
|
|
||||||
return (
|
|
||||||
<li
|
|
||||||
key={i}
|
|
||||||
className="grid grid-cols-[40px_minmax(0,1fr)] grid-rows-1 gap-3 items-center"
|
|
||||||
>
|
|
||||||
<Indicator indicator={i + 1} />
|
|
||||||
<span>
|
|
||||||
<div>{renderDetails(p.terms)}</div>
|
|
||||||
<SubProposalStateText
|
|
||||||
state={proposal.state}
|
|
||||||
enactmentDatetime={p.terms.enactmentDatetime}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</ul>
|
|
||||||
<BatchProposalStateText
|
|
||||||
state={proposal.state}
|
|
||||||
closingDatetime={proposal.batchTerms?.closingDatetime}
|
|
||||||
rejectionReason={proposal.rejectionReason}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
data-testid="proposal-details"
|
|
||||||
className="break-words mb-6 text-vega-light-200"
|
|
||||||
>
|
|
||||||
{details}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const VoteStateText = ({
|
|
||||||
state,
|
|
||||||
closingDatetime,
|
|
||||||
enactmentDatetime,
|
|
||||||
rejectionReason,
|
|
||||||
}: {
|
|
||||||
state: ProposalState;
|
|
||||||
closingDatetime: string;
|
|
||||||
enactmentDatetime: string;
|
|
||||||
rejectionReason: ProposalRejectionReason | null | undefined;
|
|
||||||
}) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const nowToCloseInHours = differenceInHours(
|
|
||||||
new Date(closingDatetime),
|
|
||||||
new Date()
|
|
||||||
);
|
|
||||||
|
|
||||||
const props = {
|
|
||||||
'data-testid': 'vote-details',
|
|
||||||
};
|
|
||||||
|
|
||||||
switch (state) {
|
|
||||||
case ProposalState.STATE_ENACTED: {
|
|
||||||
return (
|
|
||||||
<p {...props}>
|
|
||||||
{t('enactedOn{{date}}', {
|
|
||||||
enactmentDate:
|
|
||||||
enactmentDatetime &&
|
|
||||||
format(new Date(enactmentDatetime), DATE_FORMAT_DETAILED),
|
|
||||||
})}
|
|
||||||
</p>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case ProposalState.STATE_PASSED:
|
|
||||||
case ProposalState.STATE_WAITING_FOR_NODE_VOTE: {
|
|
||||||
return (
|
|
||||||
<p {...props}>
|
|
||||||
{t('enactsOn{{date}}', {
|
|
||||||
enactmentDate:
|
|
||||||
enactmentDatetime &&
|
|
||||||
format(new Date(enactmentDatetime), DATE_FORMAT_DETAILED),
|
|
||||||
})}
|
|
||||||
</p>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case ProposalState.STATE_OPEN: {
|
|
||||||
return (
|
|
||||||
<p {...props}>
|
|
||||||
<span className={nowToCloseInHours < 6 ? 'text-vega-orange' : ''}>
|
|
||||||
{t('{{time}} left to vote', {
|
|
||||||
time: formatDistanceToNowStrict(new Date(closingDatetime)),
|
|
||||||
})}
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case ProposalState.STATE_DECLINED: {
|
|
||||||
return <p {...props}>{t(state)}</p>;
|
|
||||||
}
|
|
||||||
case ProposalState.STATE_REJECTED: {
|
|
||||||
const props = { 'data-testid': 'vote-status' };
|
|
||||||
|
|
||||||
if (rejectionReason) {
|
|
||||||
return (
|
|
||||||
<p {...props}>{t(ProposalRejectionReasonMapping[rejectionReason])}</p>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return <p {...props}>{t('Proposal rejected')}</p>;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders state details relevant to the sub proposal, namely the enactment
|
|
||||||
* date and time
|
|
||||||
*/
|
|
||||||
const SubProposalStateText = ({
|
|
||||||
state,
|
|
||||||
enactmentDatetime,
|
|
||||||
}: {
|
|
||||||
state: ProposalState;
|
|
||||||
enactmentDatetime: string;
|
|
||||||
}) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
const props = {
|
|
||||||
'data-testid': 'vote-details',
|
|
||||||
className: 'm-0',
|
|
||||||
};
|
|
||||||
|
|
||||||
switch (state) {
|
|
||||||
case ProposalState.STATE_ENACTED: {
|
|
||||||
return (
|
|
||||||
<p {...props}>
|
|
||||||
{t('enactedOn{{date}}', {
|
|
||||||
enactmentDate:
|
|
||||||
enactmentDatetime &&
|
|
||||||
format(new Date(enactmentDatetime), DATE_FORMAT_DETAILED),
|
|
||||||
})}
|
|
||||||
</p>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case ProposalState.STATE_OPEN:
|
|
||||||
case ProposalState.STATE_PASSED:
|
|
||||||
case ProposalState.STATE_WAITING_FOR_NODE_VOTE: {
|
|
||||||
return (
|
|
||||||
<p {...props}>
|
|
||||||
{t('enactsOn{{date}}', {
|
|
||||||
enactmentDate:
|
|
||||||
enactmentDatetime &&
|
|
||||||
format(new Date(enactmentDatetime), DATE_FORMAT_DETAILED),
|
|
||||||
})}
|
|
||||||
</p>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case ProposalState.STATE_REJECTED:
|
|
||||||
case ProposalState.STATE_DECLINED: {
|
|
||||||
// If voting is still open we render a single clost time for all sub proposals
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders state details relevant for the entire batch. IE. if the proposal was
|
|
||||||
* rejected or declined, or the vote close time. Does not render enactment times as
|
|
||||||
* those are relevant to the sub proposal
|
|
||||||
*/
|
|
||||||
const BatchProposalStateText = ({
|
|
||||||
state,
|
|
||||||
closingDatetime,
|
|
||||||
rejectionReason,
|
|
||||||
}: {
|
|
||||||
state: ProposalState;
|
|
||||||
closingDatetime: string;
|
|
||||||
rejectionReason: ProposalRejectionReason | null | undefined;
|
|
||||||
}) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const nowToCloseInHours = differenceInHours(
|
|
||||||
new Date(closingDatetime),
|
|
||||||
new Date()
|
|
||||||
);
|
|
||||||
|
|
||||||
const props = {
|
|
||||||
'data-testid': 'vote-details',
|
|
||||||
};
|
|
||||||
|
|
||||||
switch (state) {
|
|
||||||
case ProposalState.STATE_ENACTED:
|
|
||||||
case ProposalState.STATE_PASSED:
|
|
||||||
case ProposalState.STATE_WAITING_FOR_NODE_VOTE: {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
case ProposalState.STATE_OPEN: {
|
|
||||||
return (
|
|
||||||
<p {...props}>
|
|
||||||
<span className={nowToCloseInHours < 6 ? 'text-vega-orange' : ''}>
|
|
||||||
{t('{{time}} left to vote', {
|
|
||||||
time: formatDistanceToNowStrict(new Date(closingDatetime)),
|
|
||||||
})}
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case ProposalState.STATE_DECLINED: {
|
|
||||||
return <p {...props}>{t(state)}</p>;
|
|
||||||
}
|
|
||||||
case ProposalState.STATE_REJECTED: {
|
|
||||||
const props = { 'data-testid': 'vote-status' };
|
|
||||||
|
|
||||||
if (rejectionReason) {
|
|
||||||
return (
|
|
||||||
<p {...props}>{t(ProposalRejectionReasonMapping[rejectionReason])}</p>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return <p {...props}>{t('Proposal rejected')}</p>;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ProposalHeader = ({
|
export const ProposalHeader = ({
|
||||||
proposal,
|
proposal,
|
||||||
restData,
|
|
||||||
isListItem = true,
|
isListItem = true,
|
||||||
voteState,
|
voteState,
|
||||||
}: {
|
}: {
|
||||||
proposal: Proposal | BatchProposal;
|
proposal: Proposal;
|
||||||
restData?: ProposalNode | null;
|
|
||||||
isListItem?: boolean;
|
isListItem?: boolean;
|
||||||
voteState?: VoteState | null;
|
voteState?: VoteState | null;
|
||||||
}) => {
|
}) => {
|
||||||
|
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const change = proposal?.terms.change;
|
||||||
|
|
||||||
|
const consoleLink = useLinks(DApp.Console);
|
||||||
|
|
||||||
|
let details: ReactNode;
|
||||||
|
let proposalType = '';
|
||||||
|
let fallbackTitle = '';
|
||||||
|
|
||||||
const title = proposal?.rationale.title.trim();
|
const title = proposal?.rationale.title.trim();
|
||||||
|
|
||||||
const fallbackTitle = t(
|
|
||||||
proposal.__typename === 'Proposal'
|
|
||||||
? 'Unknown proposal'
|
|
||||||
: 'Unknown batch proposal'
|
|
||||||
);
|
|
||||||
const titleContent = shorten(title ?? '', 100);
|
const titleContent = shorten(title ?? '', 100);
|
||||||
|
|
||||||
|
const getAsset = (proposal: Proposal) => {
|
||||||
|
const terms = proposal?.terms;
|
||||||
|
if (
|
||||||
|
terms?.change.__typename === 'NewMarket' &&
|
||||||
|
(terms.change.instrument.product?.__typename === 'FutureProduct' ||
|
||||||
|
terms.change.instrument.product?.__typename === 'PerpetualProduct')
|
||||||
|
) {
|
||||||
|
return terms.change.instrument.product.settlementAsset;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (change?.__typename) {
|
||||||
|
case 'NewMarket': {
|
||||||
|
proposalType =
|
||||||
|
featureFlags.PRODUCT_PERPETUALS &&
|
||||||
|
change?.instrument?.product?.__typename
|
||||||
|
? `NewMarket${change?.instrument?.product?.__typename}`
|
||||||
|
: 'NewMarket';
|
||||||
|
fallbackTitle = t('NewMarketProposal');
|
||||||
|
details = (
|
||||||
|
<>
|
||||||
|
{featureFlags.SUCCESSOR_MARKETS && (
|
||||||
|
<SuccessorCode proposalId={proposal?.id} />
|
||||||
|
)}
|
||||||
|
<span>
|
||||||
|
{t('Code')}: {change.instrument.code}.
|
||||||
|
</span>{' '}
|
||||||
|
{proposal?.terms && getAsset(proposal)?.symbol ? (
|
||||||
|
<>
|
||||||
|
<span className="font-semibold">
|
||||||
|
{getAsset(proposal)?.symbol}
|
||||||
|
</span>{' '}
|
||||||
|
{t('settled future')}.
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
''
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'UpdateMarketState': {
|
||||||
|
proposalType =
|
||||||
|
featureFlags.UPDATE_MARKET_STATE && change?.updateType
|
||||||
|
? t(change.updateType)
|
||||||
|
: 'UpdateMarketState';
|
||||||
|
fallbackTitle = t('UpdateMarketStateProposal');
|
||||||
|
details = (
|
||||||
|
<span>
|
||||||
|
{featureFlags.UPDATE_MARKET_STATE &&
|
||||||
|
change?.market?.id &&
|
||||||
|
change.updateType ? (
|
||||||
|
<>
|
||||||
|
{t(change.updateType)}: {truncateMiddle(change.market.id)}
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'UpdateMarket': {
|
||||||
|
proposalType = 'UpdateMarket';
|
||||||
|
fallbackTitle = t('UpdateMarketProposal');
|
||||||
|
details = (
|
||||||
|
<>
|
||||||
|
<span>{t('UpdateToMarket')}:</span>{' '}
|
||||||
|
<span className="inline-flex items-start gap-2">
|
||||||
|
<span className="break-all">{change.marketId} </span>
|
||||||
|
<span className="inline-flex items-end gap-0">
|
||||||
|
<CopyWithTooltip
|
||||||
|
text={change.marketId}
|
||||||
|
description={t('copyToClipboard')}
|
||||||
|
>
|
||||||
|
<button className="inline-block px-1">
|
||||||
|
<VegaIcon size={20} name={VegaIconNames.COPY} />
|
||||||
|
</button>
|
||||||
|
</CopyWithTooltip>
|
||||||
|
<Tooltip description={t('OpenInConsole')} align="center">
|
||||||
|
<button
|
||||||
|
className="inline-block px-1"
|
||||||
|
onClick={() => {
|
||||||
|
const marketPageLink = consoleLink(
|
||||||
|
CONSOLE_MARKET_PAGE.replace(':marketId', change.marketId)
|
||||||
|
);
|
||||||
|
window.open(marketPageLink, '_blank');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<VegaIcon size={20} name={VegaIconNames.OPEN_EXTERNAL} />
|
||||||
|
</button>
|
||||||
|
</Tooltip>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'UpdateReferralProgram': {
|
||||||
|
proposalType = 'UpdateReferralProgram';
|
||||||
|
fallbackTitle = t('UpdateReferralProgramProposal');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'UpdateVolumeDiscountProgram': {
|
||||||
|
proposalType = 'UpdateVolumeDiscountProgram';
|
||||||
|
fallbackTitle = t('UpdateVolumeDiscountProgramProposal');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'NewAsset': {
|
||||||
|
proposalType = 'NewAsset';
|
||||||
|
fallbackTitle = t('NewAssetProposal');
|
||||||
|
details = (
|
||||||
|
<>
|
||||||
|
<span>{t('Symbol')}:</span> <Lozenge>{change.symbol}.</Lozenge>{' '}
|
||||||
|
{change.source.__typename === 'ERC20' && (
|
||||||
|
<>
|
||||||
|
<span>{t('ERC20ContractAddress')}:</span>{' '}
|
||||||
|
<Lozenge>{change.source.contractAddress}</Lozenge>
|
||||||
|
</>
|
||||||
|
)}{' '}
|
||||||
|
{change.source.__typename === 'BuiltinAsset' && (
|
||||||
|
<>
|
||||||
|
<span>{t('MaxFaucetAmountMint')}:</span>{' '}
|
||||||
|
<Lozenge>{change.source.maxFaucetAmountMint}</Lozenge>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'UpdateNetworkParameter': {
|
||||||
|
proposalType = 'NetworkParameter';
|
||||||
|
fallbackTitle = t('NetworkParameterProposal');
|
||||||
|
details = (
|
||||||
|
<>
|
||||||
|
<span>{t('Change')}:</span>{' '}
|
||||||
|
<Lozenge>{change.networkParameter.key}</Lozenge>{' '}
|
||||||
|
<span>{t('to')}</span>{' '}
|
||||||
|
<span className="whitespace-nowrap">
|
||||||
|
<Lozenge>{change.networkParameter.value}</Lozenge>
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'NewFreeform': {
|
||||||
|
proposalType = 'Freeform';
|
||||||
|
fallbackTitle = t('FreeformProposal');
|
||||||
|
details = <span />;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'UpdateAsset': {
|
||||||
|
proposalType = 'UpdateAsset';
|
||||||
|
fallbackTitle = t('UpdateAssetProposal');
|
||||||
|
details = (
|
||||||
|
<>
|
||||||
|
<span>{t('AssetID')}:</span>{' '}
|
||||||
|
<Lozenge>{truncateMiddle(change.assetId)}</Lozenge>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'NewTransfer':
|
||||||
|
proposalType = 'NewTransfer';
|
||||||
|
fallbackTitle = t('NewTransferProposal');
|
||||||
|
details = featureFlags.GOVERNANCE_TRANSFERS ? (
|
||||||
|
<NewTransferSummary proposalId={proposal?.id} />
|
||||||
|
) : null;
|
||||||
|
break;
|
||||||
|
case 'CancelTransfer':
|
||||||
|
proposalType = 'CancelTransfer';
|
||||||
|
fallbackTitle = t('CancelTransferProposal');
|
||||||
|
details = featureFlags.GOVERNANCE_TRANSFERS ? (
|
||||||
|
<CancelTransferSummary proposalId={proposal?.id} />
|
||||||
|
) : null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="flex items-center justify-between gap-4 mb-6 text-sm">
|
<div className="flex items-center justify-between gap-4 mb-6 text-sm">
|
||||||
<ProposalTypeTags proposal={proposal} />
|
<div data-testid="proposal-type">
|
||||||
|
<ProposalInfoLabel variant="secondary">
|
||||||
|
{t(`${proposalType}`)}
|
||||||
|
</ProposalInfoLabel>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-6">
|
<div className="flex items-center gap-6">
|
||||||
{(voteState === 'Yes' || voteState === 'No') && (
|
{(voteState === 'Yes' || voteState === 'No') && (
|
||||||
@ -591,43 +264,50 @@ export const ProposalHeader = ({
|
|||||||
<div data-testid="proposal-title" className="break-all">
|
<div data-testid="proposal-title" className="break-all">
|
||||||
{isListItem ? (
|
{isListItem ? (
|
||||||
<header>
|
<header>
|
||||||
<SubHeading title={titleContent || fallbackTitle} />
|
<SubHeading
|
||||||
|
title={titleContent || fallbackTitle || t('Unknown proposal')}
|
||||||
|
/>
|
||||||
</header>
|
</header>
|
||||||
) : (
|
) : (
|
||||||
<Heading title={titleContent || fallbackTitle} />
|
<Heading
|
||||||
|
title={titleContent || fallbackTitle || t('Unknown proposal')}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<ProposalDetails proposal={proposal} />
|
|
||||||
<VoteBreakdown proposal={proposal} restData={restData} />
|
{details && (
|
||||||
|
<div
|
||||||
|
data-testid="proposal-details"
|
||||||
|
className="break-words mb-6 text-vega-light-200"
|
||||||
|
>
|
||||||
|
{details}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<VoteBreakdown proposal={proposal} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ParentMarketCode = ({
|
export const SuccessorCode = ({
|
||||||
parentMarketId,
|
proposalId,
|
||||||
}: {
|
}: {
|
||||||
parentMarketId: string;
|
proposalId?: string | null;
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { data } = useInstrumentDetailsQuery({
|
const successor = useSuccessorMarketProposalDetails(proposalId);
|
||||||
variables: {
|
|
||||||
marketId: parentMarketId,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!data?.market?.tradableInstrument.instrument.code) return null;
|
return successor.parentMarketId || successor.code ? (
|
||||||
|
|
||||||
return (
|
|
||||||
<span className="block" data-testid="proposal-successor-info">
|
<span className="block" data-testid="proposal-successor-info">
|
||||||
{t('Successor market to')}:{' '}
|
{t('Successor market to')}:{' '}
|
||||||
<Link
|
<Link
|
||||||
to={`${Routes.PROPOSALS}/${parentMarketId}`}
|
to={`${Routes.PROPOSALS}/${successor.parentMarketId}`}
|
||||||
className="hover:underline"
|
className="hover:underline"
|
||||||
>
|
>
|
||||||
{data.market.tradableInstrument.instrument.code}
|
{successor.code || successor.parentMarketId}
|
||||||
</Link>
|
</Link>
|
||||||
</span>
|
</span>
|
||||||
);
|
) : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const NewTransferSummary = ({
|
export const NewTransferSummary = ({
|
||||||
|
@ -3,8 +3,14 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
|
import { SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
|
||||||
import { SubHeading } from '../../../../components/heading';
|
import { SubHeading } from '../../../../components/heading';
|
||||||
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
|
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
|
||||||
|
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||||
|
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||||
|
|
||||||
export const ProposalJson = ({ proposal }: { proposal?: unknown }) => {
|
export const ProposalJson = ({
|
||||||
|
proposal,
|
||||||
|
}: {
|
||||||
|
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||||
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [showDetails, setShowDetails] = useState(false);
|
const [showDetails, setShowDetails] = useState(false);
|
||||||
|
|
||||||
@ -18,7 +24,7 @@ export const ProposalJson = ({ proposal }: { proposal?: unknown }) => {
|
|||||||
<SubHeading title={t('proposalJson')} />
|
<SubHeading title={t('proposalJson')} />
|
||||||
</CollapsibleToggle>
|
</CollapsibleToggle>
|
||||||
|
|
||||||
{showDetails && <SyntaxHighlighter size="smaller" data={proposal} />}
|
{showDetails && <SyntaxHighlighter data={proposal} />}
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -5,11 +5,6 @@ import {
|
|||||||
} from './proposal-market-changes';
|
} from './proposal-market-changes';
|
||||||
import type { JsonValue } from '../../../../components/json-diff';
|
import type { JsonValue } from '../../../../components/json-diff';
|
||||||
|
|
||||||
jest.mock('../proposal/market-name.tsx', () => ({
|
|
||||||
...jest.requireActual('../proposal/market-name.tsx'),
|
|
||||||
MarketName: jest.fn(),
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('applyImmutableKeysFromEarlierVersion', () => {
|
describe('applyImmutableKeysFromEarlierVersion', () => {
|
||||||
it('returns an empty object if any argument is not an object or null', () => {
|
it('returns an empty object if any argument is not an object or null', () => {
|
||||||
const earlierVersion: JsonValue = null;
|
const earlierVersion: JsonValue = null;
|
||||||
@ -62,21 +57,33 @@ describe('applyImmutableKeysFromEarlierVersion', () => {
|
|||||||
describe('ProposalMarketChanges', () => {
|
describe('ProposalMarketChanges', () => {
|
||||||
it('renders correctly', () => {
|
it('renders correctly', () => {
|
||||||
const { getByTestId } = render(
|
const { getByTestId } = render(
|
||||||
<ProposalMarketChanges marketId="market-id" updateProposalNode={null} />
|
<ProposalMarketChanges
|
||||||
|
originalProposal={{}}
|
||||||
|
latestEnactedProposal={{}}
|
||||||
|
updatedProposal={{}}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
expect(getByTestId('proposal-market-changes')).toBeInTheDocument();
|
expect(getByTestId('proposal-market-changes')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('JsonDiff is not visible when showChanges is false', () => {
|
it('JsonDiff is not visible when showChanges is false', () => {
|
||||||
const { queryByTestId } = render(
|
const { queryByTestId } = render(
|
||||||
<ProposalMarketChanges marketId="market-id" updateProposalNode={null} />
|
<ProposalMarketChanges
|
||||||
|
originalProposal={{}}
|
||||||
|
latestEnactedProposal={{}}
|
||||||
|
updatedProposal={{}}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
expect(queryByTestId('json-diff')).not.toBeInTheDocument();
|
expect(queryByTestId('json-diff')).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('JsonDiff is visible when showChanges is true', async () => {
|
it('JsonDiff is visible when showChanges is true', async () => {
|
||||||
const { getByTestId } = render(
|
const { getByTestId } = render(
|
||||||
<ProposalMarketChanges marketId="market-id" updateProposalNode={null} />
|
<ProposalMarketChanges
|
||||||
|
originalProposal={{}}
|
||||||
|
latestEnactedProposal={{}}
|
||||||
|
updatedProposal={{}}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
fireEvent.click(getByTestId('proposal-market-changes-toggle'));
|
fireEvent.click(getByTestId('proposal-market-changes-toggle'));
|
||||||
expect(getByTestId('json-diff')).toBeInTheDocument();
|
expect(getByTestId('json-diff')).toBeInTheDocument();
|
||||||
|
@ -1,24 +1,12 @@
|
|||||||
import cloneDeep from 'lodash/cloneDeep';
|
import cloneDeep from 'lodash/cloneDeep';
|
||||||
import set from 'lodash/set';
|
import set from 'lodash/set';
|
||||||
import get from 'lodash/get';
|
import get from 'lodash/get';
|
||||||
import compact from 'lodash/compact';
|
import { JsonDiff } from '../../../../components/json-diff';
|
||||||
import orderBy from 'lodash/orderBy';
|
|
||||||
import { JsonDiff, type JsonValue } from '../../../../components/json-diff';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
|
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
|
||||||
import { SubHeading } from '../../../../components/heading';
|
import { SubHeading } from '../../../../components/heading';
|
||||||
import {
|
import type { JsonValue } from '../../../../components/json-diff';
|
||||||
useFetchProposal,
|
|
||||||
useFetchProposals,
|
|
||||||
flatten,
|
|
||||||
isBatchProposalNode,
|
|
||||||
isSingleProposalNode,
|
|
||||||
type ProposalNode,
|
|
||||||
type SingleProposalData,
|
|
||||||
type SubProposalData,
|
|
||||||
} from '../proposal/proposal-utils';
|
|
||||||
import { MarketName } from '../proposal/market-name';
|
|
||||||
|
|
||||||
const immutableKeys = [
|
const immutableKeys = [
|
||||||
'decimalPlaces',
|
'decimalPlaces',
|
||||||
@ -28,8 +16,8 @@ const immutableKeys = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export const applyImmutableKeysFromEarlierVersion = (
|
export const applyImmutableKeysFromEarlierVersion = (
|
||||||
earlierVersion: unknown,
|
earlierVersion: JsonValue,
|
||||||
updatedVersion: unknown
|
updatedVersion: JsonValue
|
||||||
) => {
|
) => {
|
||||||
if (
|
if (
|
||||||
typeof earlierVersion !== 'object' ||
|
typeof earlierVersion !== 'object' ||
|
||||||
@ -45,94 +33,26 @@ export const applyImmutableKeysFromEarlierVersion = (
|
|||||||
|
|
||||||
// Overwrite the immutable keys in the updatedVersionCopy with the earlier values
|
// Overwrite the immutable keys in the updatedVersionCopy with the earlier values
|
||||||
immutableKeys.forEach((key) => {
|
immutableKeys.forEach((key) => {
|
||||||
const earlier = get(earlierVersion, key);
|
set(updatedVersionCopy, key, get(earlierVersion, key));
|
||||||
if (earlier) set(updatedVersionCopy, key, earlier);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return updatedVersionCopy;
|
return updatedVersionCopy;
|
||||||
};
|
};
|
||||||
|
|
||||||
interface ProposalMarketChangesProps {
|
interface ProposalMarketChangesProps {
|
||||||
marketId: string;
|
originalProposal: JsonValue;
|
||||||
/** This are the changes from proposal */
|
latestEnactedProposal: JsonValue | undefined;
|
||||||
updateProposalNode: ProposalNode | null;
|
updatedProposal: JsonValue;
|
||||||
indicator?: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ProposalMarketChanges = ({
|
export const ProposalMarketChanges = ({
|
||||||
marketId,
|
originalProposal,
|
||||||
updateProposalNode,
|
latestEnactedProposal,
|
||||||
indicator,
|
updatedProposal,
|
||||||
}: ProposalMarketChangesProps) => {
|
}: ProposalMarketChangesProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [showChanges, setShowChanges] = useState(false);
|
const [showChanges, setShowChanges] = useState(false);
|
||||||
|
|
||||||
const { data: originalProposalData } = useFetchProposal({
|
|
||||||
proposalId: marketId,
|
|
||||||
});
|
|
||||||
|
|
||||||
const { data: enactedProposalsData } = useFetchProposals({
|
|
||||||
proposalState: 'STATE_ENACTED',
|
|
||||||
proposalType: 'TYPE_UPDATE_MARKET',
|
|
||||||
});
|
|
||||||
|
|
||||||
let updateProposal: SingleProposalData | SubProposalData | undefined;
|
|
||||||
if (isBatchProposalNode(updateProposalNode)) {
|
|
||||||
updateProposal = updateProposalNode.proposals.find(
|
|
||||||
(p, i) =>
|
|
||||||
p.terms.updateMarket?.marketId === marketId &&
|
|
||||||
(indicator != null ? i === indicator - 1 : true)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (isSingleProposalNode(updateProposalNode)) {
|
|
||||||
updateProposal = updateProposalNode.proposal;
|
|
||||||
}
|
|
||||||
|
|
||||||
// this should get the proposal before the current one
|
|
||||||
const enactedUpdateMarketProposals = orderBy(
|
|
||||||
compact(
|
|
||||||
flatten(enactedProposalsData).filter((enacted) => {
|
|
||||||
const related = enacted.terms.updateMarket?.marketId === marketId;
|
|
||||||
const notCurrent =
|
|
||||||
enacted.id !== updateProposal?.id ||
|
|
||||||
('batchId' in enacted && enacted.batchId !== updateProposal.id);
|
|
||||||
const beforeCurrent =
|
|
||||||
Number(enacted.terms.enactmentTimestamp) <
|
|
||||||
Number(updateProposal?.terms.enactmentTimestamp);
|
|
||||||
return related && notCurrent && beforeCurrent;
|
|
||||||
})
|
|
||||||
),
|
|
||||||
[(proposal) => Number(proposal.terms.enactmentTimestamp)],
|
|
||||||
'desc'
|
|
||||||
);
|
|
||||||
|
|
||||||
const latestEnactedProposal =
|
|
||||||
enactedUpdateMarketProposals.length > 0
|
|
||||||
? enactedUpdateMarketProposals[0]
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
let originalProposal;
|
|
||||||
if (isBatchProposalNode(originalProposalData)) {
|
|
||||||
originalProposal = originalProposalData.proposals.find(
|
|
||||||
(proposal) => proposal.id === marketId && proposal.terms.newMarket != null
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (isSingleProposalNode(originalProposalData)) {
|
|
||||||
originalProposal = originalProposalData.proposal;
|
|
||||||
}
|
|
||||||
|
|
||||||
// LEFT SIDE: update market proposal enacted just before this one
|
|
||||||
// or original new market proposal
|
|
||||||
const left =
|
|
||||||
latestEnactedProposal?.terms.updateMarket?.changes ||
|
|
||||||
originalProposal?.terms.newMarket?.changes;
|
|
||||||
|
|
||||||
// RIGHT SIDE: this update market proposal
|
|
||||||
const right = applyImmutableKeysFromEarlierVersion(
|
|
||||||
left,
|
|
||||||
updateProposal?.terms.updateMarket?.changes
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section data-testid="proposal-market-changes">
|
<section data-testid="proposal-market-changes">
|
||||||
<CollapsibleToggle
|
<CollapsibleToggle
|
||||||
@ -140,18 +60,25 @@ export const ProposalMarketChanges = ({
|
|||||||
setToggleState={setShowChanges}
|
setToggleState={setShowChanges}
|
||||||
dataTestId={'proposal-market-changes-toggle'}
|
dataTestId={'proposal-market-changes-toggle'}
|
||||||
>
|
>
|
||||||
<SubHeading
|
<SubHeading title={t('updatesToMarket')} />
|
||||||
title={
|
|
||||||
<>
|
|
||||||
{t('UpdateToMarket')}: <MarketName marketId={marketId} truncate />
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</CollapsibleToggle>
|
</CollapsibleToggle>
|
||||||
|
|
||||||
{showChanges && (
|
{showChanges && (
|
||||||
<div className="mb-6 bg-vega-cdark-900 p-2 rounded-lg">
|
<div className="mb-6">
|
||||||
<JsonDiff left={left as JsonValue} right={right as JsonValue} />
|
<JsonDiff
|
||||||
|
left={latestEnactedProposal || originalProposal}
|
||||||
|
right={
|
||||||
|
latestEnactedProposal
|
||||||
|
? applyImmutableKeysFromEarlierVersion(
|
||||||
|
latestEnactedProposal,
|
||||||
|
updatedProposal
|
||||||
|
)
|
||||||
|
: applyImmutableKeysFromEarlierVersion(
|
||||||
|
originalProposal,
|
||||||
|
updatedProposal
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
|
@ -18,7 +18,6 @@ import {
|
|||||||
getDataSourceSpecForTradingTermination,
|
getDataSourceSpecForTradingTermination,
|
||||||
getSigners,
|
getSigners,
|
||||||
MarginScalingFactorsPanel,
|
MarginScalingFactorsPanel,
|
||||||
marketInfoProvider,
|
|
||||||
} from '@vegaprotocol/markets';
|
} from '@vegaprotocol/markets';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
@ -29,8 +28,8 @@ import {
|
|||||||
} from '@vegaprotocol/ui-toolkit';
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
import { SubHeading } from '../../../../components/heading';
|
import { SubHeading } from '../../../../components/heading';
|
||||||
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
|
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
|
||||||
|
import type { MarketInfo } from '@vegaprotocol/markets';
|
||||||
import { create } from 'zustand';
|
import { create } from 'zustand';
|
||||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
|
||||||
|
|
||||||
type MarketDataDialogState = {
|
type MarketDataDialogState = {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
@ -49,29 +48,18 @@ export const useMarketDataDialogStore = create<MarketDataDialogState>(
|
|||||||
const marketDataHeaderStyles =
|
const marketDataHeaderStyles =
|
||||||
'font-alpha calt text-base border-b border-vega-dark-200 mt-2 py-2';
|
'font-alpha calt text-base border-b border-vega-dark-200 mt-2 py-2';
|
||||||
|
|
||||||
export const ProposalMarketData = ({ proposalId }: { proposalId: string }) => {
|
export const ProposalMarketData = ({
|
||||||
|
marketData,
|
||||||
|
parentMarketData,
|
||||||
|
}: {
|
||||||
|
marketData: MarketInfo;
|
||||||
|
parentMarketData?: MarketInfo;
|
||||||
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { isOpen, open, close } = useMarketDataDialogStore();
|
const { isOpen, open, close } = useMarketDataDialogStore();
|
||||||
const [showDetails, setShowDetails] = useState(false);
|
const [showDetails, setShowDetails] = useState(false);
|
||||||
|
|
||||||
const { data: marketData } = useDataProvider({
|
if (!marketData) {
|
||||||
dataProvider: marketInfoProvider,
|
|
||||||
skipUpdates: true,
|
|
||||||
variables: {
|
|
||||||
marketId: proposalId,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const { data: parentMarketData } = useDataProvider({
|
|
||||||
dataProvider: marketInfoProvider,
|
|
||||||
skipUpdates: true,
|
|
||||||
skip: !marketData?.parentMarketID,
|
|
||||||
variables: {
|
|
||||||
marketId: marketData?.parentMarketID || '',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!marketData || !parentMarketData) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user