From 46d03826cc127f19e410e717069de4bed1a9fe87 Mon Sep 17 00:00:00 2001
From: Sam Keen
Date: Wed, 5 Oct 2022 16:07:42 +0100
Subject: [PATCH] Feat/1446: Add UpdateAsset proposal form (#1558)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* Feat/1446: Add UpdateAsset proposal form
* Feat/1446: Formatting fix
* Feat/1446: changes from PR comments
* Feat/1446: Fix for mockWalletContext shape and lint fix
* Feat/1446: Fix more tests reliant on the updated mockPubkey shape change
* Feat/1446: Adding the update asset proposal terms interface into the new location
Co-authored-by: Bartłomiej Głownia
Co-authored-by: Dexter
---
apps/token/src/i18n/translations/dev.json | 2 +
.../proposals-list-item-details.spec.tsx | 6 +-
.../freeform/propose-freeform.spec.tsx | 12 +-
.../propose/freeform/propose-freeform.tsx | 6 +-
.../propose-network-parameter.spec.tsx | 22 +--
.../propose-network-parameter.tsx | 6 +-
.../new-asset/propose-new-asset.spec.tsx | 10 +-
.../propose/new-asset/propose-new-asset.tsx | 20 +-
.../new-market/propose-new-market.spec.tsx | 6 +-
.../propose/new-market/propose-new-market.tsx | 20 +-
.../governance/propose/propose.spec.tsx | 1 +
.../src/routes/governance/propose/propose.tsx | 10 +
.../governance/propose/raw/propose-raw.tsx | 12 +-
.../governance/propose/update-asset/index.tsx | 4 +
.../propose-update-asset.spec.tsx | 102 ++++++++++
.../update-asset/propose-update-asset.tsx | 182 ++++++++++++++++++
.../propose-update-market.spec.tsx | 12 +-
.../update-market/propose-update-market.tsx | 20 +-
.../routes/governance/test-helpers/mocks.ts | 8 +-
apps/token/src/routes/router-config.tsx | 11 ++
.../src/hooks/use-network-params.ts | 10 +
libs/react-helpers/src/lib/validate/index.ts | 9 +
libs/wallet/src/connectors/vega-connector.ts | 18 +-
23 files changed, 410 insertions(+), 99 deletions(-)
create mode 100644 apps/token/src/routes/governance/propose/update-asset/index.tsx
create mode 100644 apps/token/src/routes/governance/propose/update-asset/propose-update-asset.spec.tsx
create mode 100644 apps/token/src/routes/governance/propose/update-asset/propose-update-asset.tsx
diff --git a/apps/token/src/i18n/translations/dev.json b/apps/token/src/i18n/translations/dev.json
index 5f96f96fa..077b19080 100644
--- a/apps/token/src/i18n/translations/dev.json
+++ b/apps/token/src/i18n/translations/dev.json
@@ -636,6 +636,7 @@
"NewMarketProposal": "New market proposal",
"UpdateMarketProposal": "Update market proposal",
"NewAssetProposal": "New asset proposal",
+ "UpdateAssetProposal": "Update asset proposal",
"NewFreeformProposal": "New freeform proposal",
"NewRawProposal": "New raw proposal",
"MinProposalRequirements": "You must have at least {{value}} VEGA associated to make a proposal",
@@ -647,6 +648,7 @@
"NewMarket": "New market",
"UpdateMarket": "Update market",
"NewAsset": "New asset",
+ "UpdateAsset": "Update asset",
"Freeform": "Freeform",
"RawProposal": "Let me choose (raw proposal)",
"UseMin": "Use minimum",
diff --git a/apps/token/src/routes/governance/components/proposals-list-item/proposals-list-item-details.spec.tsx b/apps/token/src/routes/governance/components/proposals-list-item/proposals-list-item-details.spec.tsx
index c5e5545dd..1aed2d962 100644
--- a/apps/token/src/routes/governance/components/proposals-list-item/proposals-list-item-details.spec.tsx
+++ b/apps/token/src/routes/governance/components/proposals-list-item/proposals-list-item-details.spec.tsx
@@ -26,7 +26,7 @@ import {
lastWeek,
nextWeek,
} from '../../test-helpers/mocks';
-import type { ProposalsConnection_proposalsConnection_edges_node as ProposalNode } from '@vegaprotocol/governance';
+import type { Proposals_proposalsConnection_edges_node as ProposalNode } from '../../proposals/__generated__/Proposals';
const renderComponent = (
proposal: ProposalNode,
@@ -172,7 +172,7 @@ describe('Proposals list item details', () => {
datetime: lastWeek.toString(),
party: {
__typename: 'Party',
- id: mockPubkey,
+ id: mockPubkey.publicKey,
stakingSummary: {
__typename: 'StakingSummary',
currentStakeAvailable: '1000',
@@ -210,7 +210,7 @@ describe('Proposals list item details', () => {
datetime: lastWeek.toString(),
party: {
__typename: 'Party',
- id: mockPubkey,
+ id: mockPubkey.publicKey,
stakingSummary: {
__typename: 'StakingSummary',
currentStakeAvailable: '1000',
diff --git a/apps/token/src/routes/governance/propose/freeform/propose-freeform.spec.tsx b/apps/token/src/routes/governance/propose/freeform/propose-freeform.spec.tsx
index de6b8ad57..4bf639d83 100644
--- a/apps/token/src/routes/governance/propose/freeform/propose-freeform.spec.tsx
+++ b/apps/token/src/routes/governance/propose/freeform/propose-freeform.spec.tsx
@@ -1,4 +1,4 @@
-import { render, screen, waitFor } from '@testing-library/react';
+import { render, screen } from '@testing-library/react';
import { ProposeFreeform } from './propose-freeform';
import { MockedProvider } from '@apollo/client/testing';
import { mockWalletContext } from '../../test-helpers/mocks';
@@ -81,16 +81,14 @@ describe('Propose Freeform', () => {
it('should render the title', async () => {
renderComponent();
- await waitFor(() =>
- expect(screen.getByText('New freeform proposal')).toBeInTheDocument()
- );
+ expect(
+ await screen.findByText('New freeform proposal')
+ ).toBeInTheDocument();
});
it('should render the form components', async () => {
renderComponent();
- await waitFor(() =>
- expect(screen.getByTestId('freeform-proposal-form')).toBeTruthy()
- );
+ expect(await screen.findByTestId('freeform-proposal-form')).toBeTruthy();
expect(screen.getByTestId('min-proposal-requirements')).toBeTruthy();
expect(screen.getByTestId('proposal-docs-link')).toBeTruthy();
expect(screen.getByTestId('proposal-title')).toBeTruthy();
diff --git a/apps/token/src/routes/governance/propose/freeform/propose-freeform.tsx b/apps/token/src/routes/governance/propose/freeform/propose-freeform.tsx
index 75b598b64..6f82bd335 100644
--- a/apps/token/src/routes/governance/propose/freeform/propose-freeform.tsx
+++ b/apps/token/src/routes/governance/propose/freeform/propose-freeform.tsx
@@ -27,7 +27,7 @@ export interface FreeformProposalFormFields {
proposalReference: string;
}
-const docsLink = 'freeform-proposal';
+const DOCS_LINK = 'freeform-proposal';
export const ProposeFreeform = () => {
const { params, loading, error } = useNetworkParams([
@@ -76,9 +76,9 @@ export const ProposeFreeform = () => {
{t('ProposalTermsText')}
{`${VEGA_DOCS_URL}/tutorials/proposals/${docsLink}`}
+ >{`${VEGA_DOCS_URL}/tutorials/proposals/${DOCS_LINK}`}
)}
diff --git a/apps/token/src/routes/governance/propose/network-parameter/propose-network-parameter.spec.tsx b/apps/token/src/routes/governance/propose/network-parameter/propose-network-parameter.spec.tsx
index d6f9fe915..146466482 100644
--- a/apps/token/src/routes/governance/propose/network-parameter/propose-network-parameter.spec.tsx
+++ b/apps/token/src/routes/governance/propose/network-parameter/propose-network-parameter.spec.tsx
@@ -1,4 +1,4 @@
-import { fireEvent, render, screen, waitFor } from '@testing-library/react';
+import { fireEvent, render, screen } from '@testing-library/react';
import { ProposeNetworkParameter } from './propose-network-parameter';
import { MockedProvider } from '@apollo/client/testing';
import { mockWalletContext } from '../../test-helpers/mocks';
@@ -81,16 +81,16 @@ describe('Propose Network Parameter', () => {
it('should render the correct title', async () => {
renderComponent();
- await waitFor(() =>
- expect(screen.getByText('Update network parameter proposal')).toBeTruthy()
- );
+ expect(
+ await screen.findByText('Update network parameter proposal')
+ ).toBeTruthy();
});
it('should render the form components', async () => {
renderComponent();
- await waitFor(() =>
- expect(screen.getByTestId('network-parameter-proposal-form')).toBeTruthy()
- );
+ expect(
+ await screen.findByTestId('network-parameter-proposal-form')
+ ).toBeTruthy();
expect(screen.getByTestId('min-proposal-requirements')).toBeTruthy();
expect(screen.getByTestId('proposal-docs-link')).toBeTruthy();
expect(screen.getByTestId('proposal-title')).toBeTruthy();
@@ -103,15 +103,15 @@ describe('Propose Network Parameter', () => {
it('should render the network param select element with no initial value', async () => {
renderComponent();
- await waitFor(() =>
- expect(screen.getByTestId('proposal-parameter-select')).toHaveValue('')
+ expect(await screen.findByTestId('proposal-parameter-select')).toHaveValue(
+ ''
);
});
it('should render the current param value and a new value input when the network param select element is changed', async () => {
renderComponent();
- await waitFor(() =>
- expect(screen.getByTestId('proposal-parameter-select')).toHaveValue('')
+ expect(await screen.findByTestId('proposal-parameter-select')).toHaveValue(
+ ''
);
fireEvent.change(screen.getByTestId('proposal-parameter-select'), {
diff --git a/apps/token/src/routes/governance/propose/network-parameter/propose-network-parameter.tsx b/apps/token/src/routes/governance/propose/network-parameter/propose-network-parameter.tsx
index d07090e89..73fd0caec 100644
--- a/apps/token/src/routes/governance/propose/network-parameter/propose-network-parameter.tsx
+++ b/apps/token/src/routes/governance/propose/network-parameter/propose-network-parameter.tsx
@@ -69,7 +69,7 @@ export interface NetworkParameterProposalFormFields {
proposalReference: string;
}
-const docsLink = '/network-parameter-proposal';
+const DOCS_LINK = '/network-parameter-proposal';
export const ProposeNetworkParameter = () => {
const [selectedNetworkParam, setSelectedNetworkParam] = useState<
@@ -141,9 +141,9 @@ export const ProposeNetworkParameter = () => {
{t('ProposalTermsText')}
{`${VEGA_DOCS_URL}/tutorials/proposals${docsLink}`}
+ >{`${VEGA_DOCS_URL}/tutorials/proposals${DOCS_LINK}`}
)}
diff --git a/apps/token/src/routes/governance/propose/new-asset/propose-new-asset.spec.tsx b/apps/token/src/routes/governance/propose/new-asset/propose-new-asset.spec.tsx
index 1ccd0f05e..e5d747ded 100644
--- a/apps/token/src/routes/governance/propose/new-asset/propose-new-asset.spec.tsx
+++ b/apps/token/src/routes/governance/propose/new-asset/propose-new-asset.spec.tsx
@@ -1,6 +1,6 @@
import { MockedProvider } from '@apollo/client/testing';
import { MemoryRouter as Router } from 'react-router-dom';
-import { render, screen, waitFor } from '@testing-library/react';
+import { render, screen } from '@testing-library/react';
import { VegaWalletContext } from '@vegaprotocol/wallet';
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
import { mockWalletContext } from '../../test-helpers/mocks';
@@ -81,16 +81,12 @@ describe('Propose New Asset', () => {
it('should render the title', async () => {
renderComponent();
- await waitFor(() =>
- expect(screen.getByText('New asset proposal')).toBeTruthy()
- );
+ expect(await screen.findByText('New asset proposal')).toBeTruthy();
});
it('should render the form components', async () => {
renderComponent();
- await waitFor(() =>
- expect(screen.getByTestId('new-asset-proposal-form')).toBeTruthy()
- );
+ expect(await screen.findByTestId('new-asset-proposal-form')).toBeTruthy();
expect(screen.getByTestId('min-proposal-requirements')).toBeTruthy();
expect(screen.getByTestId('proposal-docs-link')).toBeTruthy();
expect(screen.getByTestId('proposal-title')).toBeTruthy();
diff --git a/apps/token/src/routes/governance/propose/new-asset/propose-new-asset.tsx b/apps/token/src/routes/governance/propose/new-asset/propose-new-asset.tsx
index 07ddf9461..6e63d3ace 100644
--- a/apps/token/src/routes/governance/propose/new-asset/propose-new-asset.tsx
+++ b/apps/token/src/routes/governance/propose/new-asset/propose-new-asset.tsx
@@ -7,6 +7,7 @@ import {
getValidationTimestamp,
} from '@vegaprotocol/governance';
import { useEnvironment } from '@vegaprotocol/environment';
+import { validateJson } from '@vegaprotocol/react-helpers';
import {
ProposalFormMinRequirements,
ProposalFormTitle,
@@ -32,7 +33,7 @@ export interface NewAssetProposalFormFields {
proposalReference: string;
}
-const docsLink = '/new-asset-proposal';
+const DOCS_LINK = '/new-asset-proposal';
export const ProposeNewAsset = () => {
const {
@@ -100,9 +101,9 @@ export const ProposeNewAsset = () => {
{t('ProposalTermsText')}
{`${VEGA_DOCS_URL}/tutorials/proposals${docsLink}`}
+ >{`${VEGA_DOCS_URL}/tutorials/proposals${DOCS_LINK}`}
)}
@@ -141,20 +142,11 @@ export const ProposeNewAsset = () => {
{
- try {
- JSON.parse(value);
- return true;
- } catch (e) {
- return t('Must be valid JSON');
- }
- },
- },
+ validate: (value) => validateJson(value),
})}
labelOverride={'Terms.newAsset (JSON format)'}
errorMessage={errors?.proposalTerms?.message}
- customDocLink={docsLink}
+ customDocLink={DOCS_LINK}
/>
{
it('should render the form components', async () => {
renderComponent();
- await waitFor(() =>
- expect(screen.getByTestId('new-market-proposal-form')).toBeTruthy()
- );
+ expect(await screen.findByTestId('new-market-proposal-form')).toBeTruthy();
expect(screen.getByTestId('min-proposal-requirements')).toBeTruthy();
expect(screen.getByTestId('proposal-docs-link')).toBeTruthy();
expect(screen.getByTestId('proposal-title')).toBeTruthy();
diff --git a/apps/token/src/routes/governance/propose/new-market/propose-new-market.tsx b/apps/token/src/routes/governance/propose/new-market/propose-new-market.tsx
index 89c0f419c..4aa0bf0fa 100644
--- a/apps/token/src/routes/governance/propose/new-market/propose-new-market.tsx
+++ b/apps/token/src/routes/governance/propose/new-market/propose-new-market.tsx
@@ -6,6 +6,7 @@ import {
getEnactmentTimestamp,
} from '@vegaprotocol/governance';
import { useEnvironment } from '@vegaprotocol/environment';
+import { validateJson } from '@vegaprotocol/react-helpers';
import {
ProposalFormMinRequirements,
ProposalFormTitle,
@@ -30,7 +31,7 @@ export interface NewMarketProposalFormFields {
proposalReference: string;
}
-const docsLink = '/new-market-proposal';
+const DOCS_LINK = '/new-market-proposal';
export const ProposeNewMarket = () => {
const {
@@ -95,9 +96,9 @@ export const ProposeNewMarket = () => {
{t('ProposalTermsText')}
{`${VEGA_DOCS_URL}/tutorials/proposals/${docsLink}`}
+ >{`${VEGA_DOCS_URL}/tutorials/proposals${DOCS_LINK}`}
)}
@@ -136,20 +137,11 @@ export const ProposeNewMarket = () => {
{
- try {
- JSON.parse(value);
- return true;
- } catch (e) {
- return t('Must be valid JSON');
- }
- },
- },
+ validate: (value) => validateJson(value),
})}
labelOverride={'Terms.newMarket (JSON format)'}
errorMessage={errors?.proposalTerms?.message}
- customDocLink={docsLink}
+ customDocLink={DOCS_LINK}
/>
{
expect(screen.getByText('New market')).toBeTruthy();
expect(screen.getByText('Update market')).toBeTruthy();
expect(screen.getByText('New asset')).toBeTruthy();
+ expect(screen.getByText('Update asset')).toBeTruthy();
expect(screen.getByText('Freeform')).toBeTruthy();
expect(screen.getByText('Let me choose (raw proposal)')).toBeTruthy();
});
diff --git a/apps/token/src/routes/governance/propose/propose.tsx b/apps/token/src/routes/governance/propose/propose.tsx
index 7b0ba9070..47a1d5372 100644
--- a/apps/token/src/routes/governance/propose/propose.tsx
+++ b/apps/token/src/routes/governance/propose/propose.tsx
@@ -79,6 +79,16 @@ export const Propose = () => {
+
+
+
+ {t('UpdateAsset')}
+
+
+
{
data-testid="proposal-data"
{...register('rawProposalData', {
required: t('Required'),
- validate: {
- validateJson: (value) => {
- try {
- JSON.parse(value);
- return true;
- } catch (e) {
- return t('Must be valid JSON');
- }
- },
- },
+ validate: (value) => validateJson(value),
})}
/>
{errors.rawProposalData?.message && (
diff --git a/apps/token/src/routes/governance/propose/update-asset/index.tsx b/apps/token/src/routes/governance/propose/update-asset/index.tsx
new file mode 100644
index 000000000..06b069cbd
--- /dev/null
+++ b/apps/token/src/routes/governance/propose/update-asset/index.tsx
@@ -0,0 +1,4 @@
+export {
+ ProposeUpdateAsset,
+ ProposeUpdateAsset as default,
+} from './propose-update-asset';
diff --git a/apps/token/src/routes/governance/propose/update-asset/propose-update-asset.spec.tsx b/apps/token/src/routes/governance/propose/update-asset/propose-update-asset.spec.tsx
new file mode 100644
index 000000000..fd5e453e0
--- /dev/null
+++ b/apps/token/src/routes/governance/propose/update-asset/propose-update-asset.spec.tsx
@@ -0,0 +1,102 @@
+import { MockedProvider } from '@apollo/client/testing';
+import { MemoryRouter as Router } from 'react-router-dom';
+import { render, screen } from '@testing-library/react';
+import { VegaWalletContext } from '@vegaprotocol/wallet';
+import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
+import { mockWalletContext } from '../../test-helpers/mocks';
+import { ProposeUpdateAsset } from './propose-update-asset';
+import type { NetworkParamsQuery } from '@vegaprotocol/web3';
+import type { MockedResponse } from '@apollo/client/testing';
+import { NETWORK_PARAMETERS_QUERY } from '@vegaprotocol/react-helpers';
+
+jest.mock('@vegaprotocol/environment', () => ({
+ useEnvironment: () => ({
+ VEGA_DOCS_URL: 'https://docs.vega.xyz',
+ }),
+}));
+
+const updateAssetNetworkParamsQueryMock: MockedResponse = {
+ request: {
+ query: NETWORK_PARAMETERS_QUERY,
+ },
+ result: {
+ data: {
+ networkParameters: [
+ {
+ __typename: 'NetworkParameter',
+ key: 'governance.proposal.updateAsset.maxClose',
+ value: '8760h0m0s',
+ },
+ {
+ __typename: 'NetworkParameter',
+ key: 'governance.proposal.updateAsset.maxEnact',
+ value: '8760h0m0s',
+ },
+ {
+ __typename: 'NetworkParameter',
+ key: 'governance.proposal.updateAsset.minClose',
+ value: '1h0m0s',
+ },
+ {
+ __typename: 'NetworkParameter',
+ key: 'governance.proposal.updateAsset.minEnact',
+ value: '2h0m0s',
+ },
+ {
+ __typename: 'NetworkParameter',
+ key: 'governance.proposal.updateAsset.minProposerBalance',
+ value: '1',
+ },
+ {
+ __typename: 'NetworkParameter',
+ key: 'spam.protection.proposal.min.tokens',
+ value: '1000000000000000000',
+ },
+ ],
+ },
+ },
+};
+
+const renderComponent = () =>
+ render(
+
+
+
+
+
+
+
+
+
+ );
+
+// Note: form submission is tested in propose-raw.spec.tsx. Reusable form
+// components are tested in their own directory.
+
+describe('Propose Update Asset', () => {
+ it('should render successfully', async () => {
+ const { baseElement } = renderComponent();
+ await expect(baseElement).toBeTruthy();
+ });
+
+ it('should render the title', async () => {
+ renderComponent();
+ expect(await screen.findByText('Update asset proposal')).toBeTruthy();
+ });
+
+ it('should render the form components', async () => {
+ renderComponent();
+ expect(
+ await screen.findByTestId('update-asset-proposal-form')
+ ).toBeTruthy();
+ expect(screen.getByTestId('min-proposal-requirements')).toBeTruthy();
+ expect(screen.getByTestId('proposal-docs-link')).toBeTruthy();
+ expect(screen.getByTestId('proposal-title')).toBeTruthy();
+ expect(screen.getByTestId('proposal-description')).toBeTruthy();
+ expect(screen.getByTestId('proposal-terms')).toBeTruthy();
+ expect(screen.getByTestId('proposal-vote-deadline')).toBeTruthy();
+ expect(screen.getByTestId('proposal-enactment-deadline')).toBeTruthy();
+ expect(screen.getByTestId('proposal-submit')).toBeTruthy();
+ expect(screen.getByTestId('proposal-transaction-dialog')).toBeTruthy();
+ });
+});
diff --git a/apps/token/src/routes/governance/propose/update-asset/propose-update-asset.tsx b/apps/token/src/routes/governance/propose/update-asset/propose-update-asset.tsx
new file mode 100644
index 000000000..ad9009f43
--- /dev/null
+++ b/apps/token/src/routes/governance/propose/update-asset/propose-update-asset.tsx
@@ -0,0 +1,182 @@
+import { useForm } from 'react-hook-form';
+import { useTranslation } from 'react-i18next';
+import {
+ useProposalSubmit,
+ getClosingTimestamp,
+ getEnactmentTimestamp,
+} from '@vegaprotocol/governance';
+import { useEnvironment } from '@vegaprotocol/environment';
+import { validateJson } from '@vegaprotocol/react-helpers';
+import {
+ ProposalFormMinRequirements,
+ ProposalFormTitle,
+ ProposalFormDescription,
+ ProposalFormTerms,
+ ProposalFormSubmit,
+ ProposalFormTransactionDialog,
+ ProposalFormSubheader,
+ ProposalFormVoteAndEnactmentDeadline,
+} from '../../components/propose';
+import { AsyncRenderer, Link } from '@vegaprotocol/ui-toolkit';
+import { Heading } from '../../../../components/heading';
+import { VegaWalletContainer } from '../../../../components/vega-wallet-container';
+import { NetworkParams, useNetworkParams } from '@vegaprotocol/react-helpers';
+
+export interface UpdateAssetProposalFormFields {
+ proposalVoteDeadline: string;
+ proposalEnactmentDeadline: string;
+ proposalTitle: string;
+ proposalDescription: string;
+ proposalTerms: string;
+ proposalReference: string;
+}
+
+const DOCS_LINK = '/update-asset-proposal';
+
+export const ProposeUpdateAsset = () => {
+ const {
+ params,
+ loading: networkParamsLoading,
+ error: networkParamsError,
+ } = useNetworkParams([
+ NetworkParams.governance_proposal_updateAsset_minClose,
+ NetworkParams.governance_proposal_updateAsset_maxClose,
+ NetworkParams.governance_proposal_updateAsset_minEnact,
+ NetworkParams.governance_proposal_updateAsset_maxEnact,
+ NetworkParams.governance_proposal_updateAsset_minProposerBalance,
+ NetworkParams.spam_protection_proposal_min_tokens,
+ ]);
+
+ const { VEGA_EXPLORER_URL, VEGA_DOCS_URL } = useEnvironment();
+ const { t } = useTranslation();
+ const {
+ register,
+ handleSubmit,
+ formState: { isSubmitting, errors },
+ } = useForm();
+ const { finalizedProposal, submit, Dialog } = useProposalSubmit();
+
+ const onSubmit = async (fields: UpdateAssetProposalFormFields) => {
+ await submit({
+ rationale: {
+ title: fields.proposalTitle,
+ description: fields.proposalDescription,
+ },
+ terms: {
+ updateAsset: {
+ ...JSON.parse(fields.proposalTerms),
+ },
+ closingTimestamp: getClosingTimestamp(fields.proposalVoteDeadline),
+ enactmentTimestamp: getEnactmentTimestamp(
+ fields.proposalVoteDeadline,
+ fields.proposalEnactmentDeadline
+ ),
+ },
+ });
+ };
+
+ return (
+
+
+
+ {() => (
+ <>
+
+
+ {VEGA_DOCS_URL && (
+
+ {t('ProposalTermsText')}
+ {`${VEGA_DOCS_URL}/tutorials/proposals${DOCS_LINK}`}
+
+ )}
+
+ {VEGA_EXPLORER_URL && (
+
+ {t('MoreAssetsInfo')}{' '}
+ {`${VEGA_EXPLORER_URL}/assets`}
+
+ )}
+
+
+ >
+ )}
+
+
+ );
+};
diff --git a/apps/token/src/routes/governance/propose/update-market/propose-update-market.spec.tsx b/apps/token/src/routes/governance/propose/update-market/propose-update-market.spec.tsx
index cd5a2c5ef..ab88929a6 100644
--- a/apps/token/src/routes/governance/propose/update-market/propose-update-market.spec.tsx
+++ b/apps/token/src/routes/governance/propose/update-market/propose-update-market.spec.tsx
@@ -178,17 +178,17 @@ describe('Propose Update Market', () => {
it('should render the select element with no initial value', async () => {
renderComponent();
- await waitFor(() =>
- expect(screen.getByText('Update market proposal')).toBeInTheDocument()
- );
+ expect(
+ await screen.findByText('Update market proposal')
+ ).toBeInTheDocument();
expect(screen.getByTestId('proposal-market-select')).toHaveValue('');
});
it('should render the correct market details when the market select is used', async () => {
renderComponent();
- await waitFor(() =>
- expect(screen.getByText('Update market proposal')).toBeInTheDocument()
- );
+ expect(
+ await screen.findByText('Update market proposal')
+ ).toBeInTheDocument();
fireEvent.change(screen.getByTestId('proposal-market-select'), {
target: {
value:
diff --git a/apps/token/src/routes/governance/propose/update-market/propose-update-market.tsx b/apps/token/src/routes/governance/propose/update-market/propose-update-market.tsx
index c8e32652d..303101da5 100644
--- a/apps/token/src/routes/governance/propose/update-market/propose-update-market.tsx
+++ b/apps/token/src/routes/governance/propose/update-market/propose-update-market.tsx
@@ -8,6 +8,7 @@ import {
getEnactmentTimestamp,
} from '@vegaprotocol/governance';
import { useEnvironment } from '@vegaprotocol/environment';
+import { validateJson } from '@vegaprotocol/react-helpers';
import {
ProposalFormSubheader,
ProposalFormMinRequirements,
@@ -60,7 +61,7 @@ export interface UpdateMarketProposalFormFields {
proposalReference: string;
}
-const docsLink = '/update-market-proposal';
+const DOCS_LINK = '/update-market-proposal';
export const ProposeUpdateMarket = () => {
const {
@@ -158,9 +159,9 @@ export const ProposeUpdateMarket = () => {
{t('ProposalTermsText')}
{`${VEGA_DOCS_URL}/tutorials/proposals${docsLink}`}
+ >{`${VEGA_DOCS_URL}/tutorials/proposals${DOCS_LINK}`}
)}
@@ -255,20 +256,11 @@ export const ProposeUpdateMarket = () => {
{
- try {
- JSON.parse(value);
- return true;
- } catch (e) {
- return t('Must be valid JSON');
- }
- },
- },
+ validate: (value) => validateJson(value),
})}
labelOverride={t('ProposeUpdateMarketTerms')}
errorMessage={errors?.proposalTerms?.message}
- customDocLink={docsLink}
+ customDocLink={DOCS_LINK}
/>
+ import(
+ /* webpackChunkName: "route-governance-propose-update-asset", webpackPrefetch: true */ './governance/propose/update-asset'
+ )
+);
+
const LazyGovernanceProposeFreeform = React.lazy(
() =>
import(
@@ -273,6 +280,10 @@ const routerConfig = [
element: ,
},
{ path: 'new-asset', element: },
+ {
+ path: 'update-asset',
+ element: ,
+ },
{ path: 'freeform', element: },
{ path: 'raw', element: },
],
diff --git a/libs/react-helpers/src/hooks/use-network-params.ts b/libs/react-helpers/src/hooks/use-network-params.ts
index 5bc3f59d4..a4c2e14d0 100644
--- a/libs/react-helpers/src/hooks/use-network-params.ts
+++ b/libs/react-helpers/src/hooks/use-network-params.ts
@@ -43,6 +43,14 @@ export const NetworkParams = {
governance_proposal_asset_maxClose: 'governance_proposal_asset_maxClose',
governance_proposal_asset_minEnact: 'governance_proposal_asset_minEnact',
governance_proposal_asset_maxEnact: 'governance_proposal_asset_maxEnact',
+ governance_proposal_updateAsset_minClose:
+ 'governance_proposal_updateAsset_minClose',
+ governance_proposal_updateAsset_maxClose:
+ 'governance_proposal_updateAsset_maxClose',
+ governance_proposal_updateAsset_minEnact:
+ 'governance_proposal_updateAsset_minEnact',
+ governance_proposal_updateAsset_maxEnact:
+ 'governance_proposal_updateAsset_maxEnact',
governance_proposal_updateNetParam_minClose:
'governance_proposal_updateNetParam_minClose',
governance_proposal_updateNetParam_maxClose:
@@ -71,6 +79,8 @@ export const NetworkParams = {
'governance_proposal_asset_requiredParticipation',
governance_proposal_asset_minProposerBalance:
'governance_proposal_asset_minProposerBalance',
+ governance_proposal_updateAsset_minProposerBalance:
+ 'governance_proposal_updateAsset_minProposerBalance',
governance_proposal_updateNetParam_requiredMajority:
'governance_proposal_updateNetParam_requiredMajority',
governance_proposal_updateNetParam_requiredParticipation:
diff --git a/libs/react-helpers/src/lib/validate/index.ts b/libs/react-helpers/src/lib/validate/index.ts
index cae0791c4..acbdeb957 100644
--- a/libs/react-helpers/src/lib/validate/index.ts
+++ b/libs/react-helpers/src/lib/validate/index.ts
@@ -45,3 +45,12 @@ export const suitableForSyntaxHighlighter = (str: string) => {
return false;
}
};
+
+export const validateJson = (value: string) => {
+ try {
+ JSON.parse(value);
+ return true;
+ } catch (e) {
+ return t('Must be valid JSON');
+ }
+};
diff --git a/libs/wallet/src/connectors/vega-connector.ts b/libs/wallet/src/connectors/vega-connector.ts
index a83a7c495..d428cce9b 100644
--- a/libs/wallet/src/connectors/vega-connector.ts
+++ b/libs/wallet/src/connectors/vega-connector.ts
@@ -168,6 +168,21 @@ interface ProposalNewAssetTerms {
validationTimestamp: number;
}
+interface ProposalUpdateAssetTerms {
+ updateAsset: {
+ assetId: string;
+ changes: {
+ quantum: string;
+ erc20: {
+ withdrawThreshold: string;
+ lifetimeLimit: string;
+ };
+ };
+ };
+ closingTimestamp: number;
+ enactmentTimestamp: number;
+}
+
interface OracleSpecBinding {
settlementPriceProperty: string;
tradingTerminationProperty: string;
@@ -227,7 +242,8 @@ export interface ProposalSubmission {
| ProposalNewMarketTerms
| ProposalUpdateMarketTerms
| ProposalNetworkParameterTerms
- | ProposalNewAssetTerms;
+ | ProposalNewAssetTerms
+ | ProposalUpdateAssetTerms;
}
export interface ProposalSubmissionBody {