Feat/1446: Add UpdateAsset proposal form (#1558)
* 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 <bglownia@gmail.com> Co-authored-by: Dexter <dexter.edwards93@gmail.com>
This commit is contained in:
parent
b2619eac7c
commit
46d03826cc
@ -636,6 +636,7 @@
|
|||||||
"NewMarketProposal": "New market proposal",
|
"NewMarketProposal": "New market proposal",
|
||||||
"UpdateMarketProposal": "Update market proposal",
|
"UpdateMarketProposal": "Update market proposal",
|
||||||
"NewAssetProposal": "New asset proposal",
|
"NewAssetProposal": "New asset proposal",
|
||||||
|
"UpdateAssetProposal": "Update asset proposal",
|
||||||
"NewFreeformProposal": "New freeform proposal",
|
"NewFreeformProposal": "New freeform proposal",
|
||||||
"NewRawProposal": "New raw proposal",
|
"NewRawProposal": "New raw proposal",
|
||||||
"MinProposalRequirements": "You must have at least {{value}} VEGA associated to make a proposal",
|
"MinProposalRequirements": "You must have at least {{value}} VEGA associated to make a proposal",
|
||||||
@ -647,6 +648,7 @@
|
|||||||
"NewMarket": "New market",
|
"NewMarket": "New market",
|
||||||
"UpdateMarket": "Update market",
|
"UpdateMarket": "Update market",
|
||||||
"NewAsset": "New asset",
|
"NewAsset": "New asset",
|
||||||
|
"UpdateAsset": "Update asset",
|
||||||
"Freeform": "Freeform",
|
"Freeform": "Freeform",
|
||||||
"RawProposal": "Let me choose (raw proposal)",
|
"RawProposal": "Let me choose (raw proposal)",
|
||||||
"UseMin": "Use minimum",
|
"UseMin": "Use minimum",
|
||||||
|
@ -26,7 +26,7 @@ import {
|
|||||||
lastWeek,
|
lastWeek,
|
||||||
nextWeek,
|
nextWeek,
|
||||||
} from '../../test-helpers/mocks';
|
} 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 = (
|
const renderComponent = (
|
||||||
proposal: ProposalNode,
|
proposal: ProposalNode,
|
||||||
@ -172,7 +172,7 @@ describe('Proposals list item details', () => {
|
|||||||
datetime: lastWeek.toString(),
|
datetime: lastWeek.toString(),
|
||||||
party: {
|
party: {
|
||||||
__typename: 'Party',
|
__typename: 'Party',
|
||||||
id: mockPubkey,
|
id: mockPubkey.publicKey,
|
||||||
stakingSummary: {
|
stakingSummary: {
|
||||||
__typename: 'StakingSummary',
|
__typename: 'StakingSummary',
|
||||||
currentStakeAvailable: '1000',
|
currentStakeAvailable: '1000',
|
||||||
@ -210,7 +210,7 @@ describe('Proposals list item details', () => {
|
|||||||
datetime: lastWeek.toString(),
|
datetime: lastWeek.toString(),
|
||||||
party: {
|
party: {
|
||||||
__typename: 'Party',
|
__typename: 'Party',
|
||||||
id: mockPubkey,
|
id: mockPubkey.publicKey,
|
||||||
stakingSummary: {
|
stakingSummary: {
|
||||||
__typename: 'StakingSummary',
|
__typename: 'StakingSummary',
|
||||||
currentStakeAvailable: '1000',
|
currentStakeAvailable: '1000',
|
||||||
|
@ -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 { ProposeFreeform } from './propose-freeform';
|
||||||
import { MockedProvider } from '@apollo/client/testing';
|
import { MockedProvider } from '@apollo/client/testing';
|
||||||
import { mockWalletContext } from '../../test-helpers/mocks';
|
import { mockWalletContext } from '../../test-helpers/mocks';
|
||||||
@ -81,16 +81,14 @@ describe('Propose Freeform', () => {
|
|||||||
|
|
||||||
it('should render the title', async () => {
|
it('should render the title', async () => {
|
||||||
renderComponent();
|
renderComponent();
|
||||||
await waitFor(() =>
|
expect(
|
||||||
expect(screen.getByText('New freeform proposal')).toBeInTheDocument()
|
await screen.findByText('New freeform proposal')
|
||||||
);
|
).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render the form components', async () => {
|
it('should render the form components', async () => {
|
||||||
renderComponent();
|
renderComponent();
|
||||||
await waitFor(() =>
|
expect(await screen.findByTestId('freeform-proposal-form')).toBeTruthy();
|
||||||
expect(screen.getByTestId('freeform-proposal-form')).toBeTruthy()
|
|
||||||
);
|
|
||||||
expect(screen.getByTestId('min-proposal-requirements')).toBeTruthy();
|
expect(screen.getByTestId('min-proposal-requirements')).toBeTruthy();
|
||||||
expect(screen.getByTestId('proposal-docs-link')).toBeTruthy();
|
expect(screen.getByTestId('proposal-docs-link')).toBeTruthy();
|
||||||
expect(screen.getByTestId('proposal-title')).toBeTruthy();
|
expect(screen.getByTestId('proposal-title')).toBeTruthy();
|
||||||
|
@ -27,7 +27,7 @@ export interface FreeformProposalFormFields {
|
|||||||
proposalReference: string;
|
proposalReference: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const docsLink = 'freeform-proposal';
|
const DOCS_LINK = 'freeform-proposal';
|
||||||
|
|
||||||
export const ProposeFreeform = () => {
|
export const ProposeFreeform = () => {
|
||||||
const { params, loading, error } = useNetworkParams([
|
const { params, loading, error } = useNetworkParams([
|
||||||
@ -76,9 +76,9 @@ export const ProposeFreeform = () => {
|
|||||||
<p className="text-sm" data-testid="proposal-docs-link">
|
<p className="text-sm" data-testid="proposal-docs-link">
|
||||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||||
<Link
|
<Link
|
||||||
href={`${VEGA_DOCS_URL}/tutorials/proposals/${docsLink}`}
|
href={`${VEGA_DOCS_URL}/tutorials/proposals/${DOCS_LINK}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>{`${VEGA_DOCS_URL}/tutorials/proposals/${docsLink}`}</Link>
|
>{`${VEGA_DOCS_URL}/tutorials/proposals/${DOCS_LINK}`}</Link>
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@ -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 { ProposeNetworkParameter } from './propose-network-parameter';
|
||||||
import { MockedProvider } from '@apollo/client/testing';
|
import { MockedProvider } from '@apollo/client/testing';
|
||||||
import { mockWalletContext } from '../../test-helpers/mocks';
|
import { mockWalletContext } from '../../test-helpers/mocks';
|
||||||
@ -81,16 +81,16 @@ describe('Propose Network Parameter', () => {
|
|||||||
|
|
||||||
it('should render the correct title', async () => {
|
it('should render the correct title', async () => {
|
||||||
renderComponent();
|
renderComponent();
|
||||||
await waitFor(() =>
|
expect(
|
||||||
expect(screen.getByText('Update network parameter proposal')).toBeTruthy()
|
await screen.findByText('Update network parameter proposal')
|
||||||
);
|
).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render the form components', async () => {
|
it('should render the form components', async () => {
|
||||||
renderComponent();
|
renderComponent();
|
||||||
await waitFor(() =>
|
expect(
|
||||||
expect(screen.getByTestId('network-parameter-proposal-form')).toBeTruthy()
|
await screen.findByTestId('network-parameter-proposal-form')
|
||||||
);
|
).toBeTruthy();
|
||||||
expect(screen.getByTestId('min-proposal-requirements')).toBeTruthy();
|
expect(screen.getByTestId('min-proposal-requirements')).toBeTruthy();
|
||||||
expect(screen.getByTestId('proposal-docs-link')).toBeTruthy();
|
expect(screen.getByTestId('proposal-docs-link')).toBeTruthy();
|
||||||
expect(screen.getByTestId('proposal-title')).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 () => {
|
it('should render the network param select element with no initial value', async () => {
|
||||||
renderComponent();
|
renderComponent();
|
||||||
await waitFor(() =>
|
expect(await screen.findByTestId('proposal-parameter-select')).toHaveValue(
|
||||||
expect(screen.getByTestId('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 () => {
|
it('should render the current param value and a new value input when the network param select element is changed', async () => {
|
||||||
renderComponent();
|
renderComponent();
|
||||||
await waitFor(() =>
|
expect(await screen.findByTestId('proposal-parameter-select')).toHaveValue(
|
||||||
expect(screen.getByTestId('proposal-parameter-select')).toHaveValue('')
|
''
|
||||||
);
|
);
|
||||||
|
|
||||||
fireEvent.change(screen.getByTestId('proposal-parameter-select'), {
|
fireEvent.change(screen.getByTestId('proposal-parameter-select'), {
|
||||||
|
@ -69,7 +69,7 @@ export interface NetworkParameterProposalFormFields {
|
|||||||
proposalReference: string;
|
proposalReference: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const docsLink = '/network-parameter-proposal';
|
const DOCS_LINK = '/network-parameter-proposal';
|
||||||
|
|
||||||
export const ProposeNetworkParameter = () => {
|
export const ProposeNetworkParameter = () => {
|
||||||
const [selectedNetworkParam, setSelectedNetworkParam] = useState<
|
const [selectedNetworkParam, setSelectedNetworkParam] = useState<
|
||||||
@ -141,9 +141,9 @@ export const ProposeNetworkParameter = () => {
|
|||||||
<p className="text-sm" data-testid="proposal-docs-link">
|
<p className="text-sm" data-testid="proposal-docs-link">
|
||||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||||
<Link
|
<Link
|
||||||
href={`${VEGA_DOCS_URL}/tutorials/proposals${docsLink}`}
|
href={`${VEGA_DOCS_URL}/tutorials/proposals${DOCS_LINK}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>{`${VEGA_DOCS_URL}/tutorials/proposals${docsLink}`}</Link>
|
>{`${VEGA_DOCS_URL}/tutorials/proposals${DOCS_LINK}`}</Link>
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { MockedProvider } from '@apollo/client/testing';
|
import { MockedProvider } from '@apollo/client/testing';
|
||||||
import { MemoryRouter as Router } from 'react-router-dom';
|
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 { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||||
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
||||||
import { mockWalletContext } from '../../test-helpers/mocks';
|
import { mockWalletContext } from '../../test-helpers/mocks';
|
||||||
@ -81,16 +81,12 @@ describe('Propose New Asset', () => {
|
|||||||
|
|
||||||
it('should render the title', async () => {
|
it('should render the title', async () => {
|
||||||
renderComponent();
|
renderComponent();
|
||||||
await waitFor(() =>
|
expect(await screen.findByText('New asset proposal')).toBeTruthy();
|
||||||
expect(screen.getByText('New asset proposal')).toBeTruthy()
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render the form components', async () => {
|
it('should render the form components', async () => {
|
||||||
renderComponent();
|
renderComponent();
|
||||||
await waitFor(() =>
|
expect(await screen.findByTestId('new-asset-proposal-form')).toBeTruthy();
|
||||||
expect(screen.getByTestId('new-asset-proposal-form')).toBeTruthy()
|
|
||||||
);
|
|
||||||
expect(screen.getByTestId('min-proposal-requirements')).toBeTruthy();
|
expect(screen.getByTestId('min-proposal-requirements')).toBeTruthy();
|
||||||
expect(screen.getByTestId('proposal-docs-link')).toBeTruthy();
|
expect(screen.getByTestId('proposal-docs-link')).toBeTruthy();
|
||||||
expect(screen.getByTestId('proposal-title')).toBeTruthy();
|
expect(screen.getByTestId('proposal-title')).toBeTruthy();
|
||||||
|
@ -7,6 +7,7 @@ import {
|
|||||||
getValidationTimestamp,
|
getValidationTimestamp,
|
||||||
} from '@vegaprotocol/governance';
|
} from '@vegaprotocol/governance';
|
||||||
import { useEnvironment } from '@vegaprotocol/environment';
|
import { useEnvironment } from '@vegaprotocol/environment';
|
||||||
|
import { validateJson } from '@vegaprotocol/react-helpers';
|
||||||
import {
|
import {
|
||||||
ProposalFormMinRequirements,
|
ProposalFormMinRequirements,
|
||||||
ProposalFormTitle,
|
ProposalFormTitle,
|
||||||
@ -32,7 +33,7 @@ export interface NewAssetProposalFormFields {
|
|||||||
proposalReference: string;
|
proposalReference: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const docsLink = '/new-asset-proposal';
|
const DOCS_LINK = '/new-asset-proposal';
|
||||||
|
|
||||||
export const ProposeNewAsset = () => {
|
export const ProposeNewAsset = () => {
|
||||||
const {
|
const {
|
||||||
@ -100,9 +101,9 @@ export const ProposeNewAsset = () => {
|
|||||||
<p className="text-sm" data-testid="proposal-docs-link">
|
<p className="text-sm" data-testid="proposal-docs-link">
|
||||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||||
<Link
|
<Link
|
||||||
href={`${VEGA_DOCS_URL}/tutorials/proposals${docsLink}`}
|
href={`${VEGA_DOCS_URL}/tutorials/proposals${DOCS_LINK}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>{`${VEGA_DOCS_URL}/tutorials/proposals${docsLink}`}</Link>
|
>{`${VEGA_DOCS_URL}/tutorials/proposals${DOCS_LINK}`}</Link>
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -141,20 +142,11 @@ export const ProposeNewAsset = () => {
|
|||||||
<ProposalFormTerms
|
<ProposalFormTerms
|
||||||
registerField={register('proposalTerms', {
|
registerField={register('proposalTerms', {
|
||||||
required: t('Required'),
|
required: t('Required'),
|
||||||
validate: {
|
validate: (value) => validateJson(value),
|
||||||
validateJson: (value) => {
|
|
||||||
try {
|
|
||||||
JSON.parse(value);
|
|
||||||
return true;
|
|
||||||
} catch (e) {
|
|
||||||
return t('Must be valid JSON');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})}
|
})}
|
||||||
labelOverride={'Terms.newAsset (JSON format)'}
|
labelOverride={'Terms.newAsset (JSON format)'}
|
||||||
errorMessage={errors?.proposalTerms?.message}
|
errorMessage={errors?.proposalTerms?.message}
|
||||||
customDocLink={docsLink}
|
customDocLink={DOCS_LINK}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ProposalFormVoteAndEnactmentDeadline
|
<ProposalFormVoteAndEnactmentDeadline
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { render, screen, waitFor } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import { ProposeNewMarket } from './propose-new-market';
|
import { ProposeNewMarket } from './propose-new-market';
|
||||||
import { MockedProvider } from '@apollo/client/testing';
|
import { MockedProvider } from '@apollo/client/testing';
|
||||||
import { mockWalletContext } from '../../test-helpers/mocks';
|
import { mockWalletContext } from '../../test-helpers/mocks';
|
||||||
@ -81,9 +81,7 @@ describe('Propose New Market', () => {
|
|||||||
|
|
||||||
it('should render the form components', async () => {
|
it('should render the form components', async () => {
|
||||||
renderComponent();
|
renderComponent();
|
||||||
await waitFor(() =>
|
expect(await screen.findByTestId('new-market-proposal-form')).toBeTruthy();
|
||||||
expect(screen.getByTestId('new-market-proposal-form')).toBeTruthy()
|
|
||||||
);
|
|
||||||
expect(screen.getByTestId('min-proposal-requirements')).toBeTruthy();
|
expect(screen.getByTestId('min-proposal-requirements')).toBeTruthy();
|
||||||
expect(screen.getByTestId('proposal-docs-link')).toBeTruthy();
|
expect(screen.getByTestId('proposal-docs-link')).toBeTruthy();
|
||||||
expect(screen.getByTestId('proposal-title')).toBeTruthy();
|
expect(screen.getByTestId('proposal-title')).toBeTruthy();
|
||||||
|
@ -6,6 +6,7 @@ import {
|
|||||||
getEnactmentTimestamp,
|
getEnactmentTimestamp,
|
||||||
} from '@vegaprotocol/governance';
|
} from '@vegaprotocol/governance';
|
||||||
import { useEnvironment } from '@vegaprotocol/environment';
|
import { useEnvironment } from '@vegaprotocol/environment';
|
||||||
|
import { validateJson } from '@vegaprotocol/react-helpers';
|
||||||
import {
|
import {
|
||||||
ProposalFormMinRequirements,
|
ProposalFormMinRequirements,
|
||||||
ProposalFormTitle,
|
ProposalFormTitle,
|
||||||
@ -30,7 +31,7 @@ export interface NewMarketProposalFormFields {
|
|||||||
proposalReference: string;
|
proposalReference: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const docsLink = '/new-market-proposal';
|
const DOCS_LINK = '/new-market-proposal';
|
||||||
|
|
||||||
export const ProposeNewMarket = () => {
|
export const ProposeNewMarket = () => {
|
||||||
const {
|
const {
|
||||||
@ -95,9 +96,9 @@ export const ProposeNewMarket = () => {
|
|||||||
<p className="text-sm" data-testid="proposal-docs-link">
|
<p className="text-sm" data-testid="proposal-docs-link">
|
||||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||||
<Link
|
<Link
|
||||||
href={`${VEGA_DOCS_URL}/tutorials/proposals/${docsLink}`}
|
href={`${VEGA_DOCS_URL}/tutorials/proposals${DOCS_LINK}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>{`${VEGA_DOCS_URL}/tutorials/proposals/${docsLink}`}</Link>
|
>{`${VEGA_DOCS_URL}/tutorials/proposals${DOCS_LINK}`}</Link>
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -136,20 +137,11 @@ export const ProposeNewMarket = () => {
|
|||||||
<ProposalFormTerms
|
<ProposalFormTerms
|
||||||
registerField={register('proposalTerms', {
|
registerField={register('proposalTerms', {
|
||||||
required: t('Required'),
|
required: t('Required'),
|
||||||
validate: {
|
validate: (value) => validateJson(value),
|
||||||
validateJson: (value) => {
|
|
||||||
try {
|
|
||||||
JSON.parse(value);
|
|
||||||
return true;
|
|
||||||
} catch (e) {
|
|
||||||
return t('Must be valid JSON');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})}
|
})}
|
||||||
labelOverride={'Terms.newMarket (JSON format)'}
|
labelOverride={'Terms.newMarket (JSON format)'}
|
||||||
errorMessage={errors?.proposalTerms?.message}
|
errorMessage={errors?.proposalTerms?.message}
|
||||||
customDocLink={docsLink}
|
customDocLink={DOCS_LINK}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ProposalFormVoteAndEnactmentDeadline
|
<ProposalFormVoteAndEnactmentDeadline
|
||||||
|
@ -24,6 +24,7 @@ describe('Propose', () => {
|
|||||||
expect(screen.getByText('New market')).toBeTruthy();
|
expect(screen.getByText('New market')).toBeTruthy();
|
||||||
expect(screen.getByText('Update market')).toBeTruthy();
|
expect(screen.getByText('Update market')).toBeTruthy();
|
||||||
expect(screen.getByText('New asset')).toBeTruthy();
|
expect(screen.getByText('New asset')).toBeTruthy();
|
||||||
|
expect(screen.getByText('Update asset')).toBeTruthy();
|
||||||
expect(screen.getByText('Freeform')).toBeTruthy();
|
expect(screen.getByText('Freeform')).toBeTruthy();
|
||||||
expect(screen.getByText('Let me choose (raw proposal)')).toBeTruthy();
|
expect(screen.getByText('Let me choose (raw proposal)')).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
@ -79,6 +79,16 @@ export const Propose = () => {
|
|||||||
</Link>
|
</Link>
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<Link
|
||||||
|
className="underline"
|
||||||
|
to={`${Routes.GOVERNANCE}/propose/update-asset`}
|
||||||
|
>
|
||||||
|
{t('UpdateAsset')}
|
||||||
|
</Link>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<p>
|
<p>
|
||||||
<Link
|
<Link
|
||||||
|
@ -9,6 +9,7 @@ import {
|
|||||||
Link,
|
Link,
|
||||||
TextArea,
|
TextArea,
|
||||||
} from '@vegaprotocol/ui-toolkit';
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
|
import { validateJson } from '@vegaprotocol/react-helpers';
|
||||||
import { useProposalSubmit } from '@vegaprotocol/governance';
|
import { useProposalSubmit } from '@vegaprotocol/governance';
|
||||||
import {
|
import {
|
||||||
ProposalFormSubmit,
|
ProposalFormSubmit,
|
||||||
@ -74,16 +75,7 @@ export const ProposeRaw = () => {
|
|||||||
data-testid="proposal-data"
|
data-testid="proposal-data"
|
||||||
{...register('rawProposalData', {
|
{...register('rawProposalData', {
|
||||||
required: t('Required'),
|
required: t('Required'),
|
||||||
validate: {
|
validate: (value) => validateJson(value),
|
||||||
validateJson: (value) => {
|
|
||||||
try {
|
|
||||||
JSON.parse(value);
|
|
||||||
return true;
|
|
||||||
} catch (e) {
|
|
||||||
return t('Must be valid JSON');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
{errors.rawProposalData?.message && (
|
{errors.rawProposalData?.message && (
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
export {
|
||||||
|
ProposeUpdateAsset,
|
||||||
|
ProposeUpdateAsset as default,
|
||||||
|
} from './propose-update-asset';
|
@ -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<NetworkParamsQuery> = {
|
||||||
|
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(
|
||||||
|
<Router>
|
||||||
|
<MockedProvider mocks={[updateAssetNetworkParamsQueryMock]}>
|
||||||
|
<AppStateProvider>
|
||||||
|
<VegaWalletContext.Provider value={mockWalletContext}>
|
||||||
|
<ProposeUpdateAsset />
|
||||||
|
</VegaWalletContext.Provider>
|
||||||
|
</AppStateProvider>
|
||||||
|
</MockedProvider>
|
||||||
|
</Router>
|
||||||
|
);
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
});
|
||||||
|
});
|
@ -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<UpdateAssetProposalFormFields>();
|
||||||
|
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 (
|
||||||
|
<AsyncRenderer
|
||||||
|
loading={networkParamsLoading}
|
||||||
|
error={networkParamsError}
|
||||||
|
data={params}
|
||||||
|
>
|
||||||
|
<Heading title={t('UpdateAssetProposal')} />
|
||||||
|
<VegaWalletContainer>
|
||||||
|
{() => (
|
||||||
|
<>
|
||||||
|
<ProposalFormMinRequirements
|
||||||
|
minProposerBalance={
|
||||||
|
params.governance_proposal_updateAsset_minProposerBalance
|
||||||
|
}
|
||||||
|
spamProtectionMin={params.spam_protection_proposal_min_tokens}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{VEGA_DOCS_URL && (
|
||||||
|
<p className="text-sm" data-testid="proposal-docs-link">
|
||||||
|
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||||
|
<Link
|
||||||
|
href={`${VEGA_DOCS_URL}/tutorials/proposals${DOCS_LINK}`}
|
||||||
|
target="_blank"
|
||||||
|
>{`${VEGA_DOCS_URL}/tutorials/proposals${DOCS_LINK}`}</Link>
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{VEGA_EXPLORER_URL && (
|
||||||
|
<p className="text-sm">
|
||||||
|
{t('MoreAssetsInfo')}{' '}
|
||||||
|
<Link
|
||||||
|
href={`${VEGA_EXPLORER_URL}/assets`}
|
||||||
|
target="_blank"
|
||||||
|
>{`${VEGA_EXPLORER_URL}/assets`}</Link>
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div data-testid="update-asset-proposal-form">
|
||||||
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
|
<ProposalFormSubheader>
|
||||||
|
{t('ProposalRationale')}
|
||||||
|
</ProposalFormSubheader>
|
||||||
|
|
||||||
|
<ProposalFormTitle
|
||||||
|
registerField={register('proposalTitle', {
|
||||||
|
required: t('Required'),
|
||||||
|
})}
|
||||||
|
errorMessage={errors?.proposalTitle?.message}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ProposalFormDescription
|
||||||
|
registerField={register('proposalDescription', {
|
||||||
|
required: t('Required'),
|
||||||
|
})}
|
||||||
|
errorMessage={errors?.proposalDescription?.message}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ProposalFormSubheader>
|
||||||
|
{t('UpdateAsset')}
|
||||||
|
</ProposalFormSubheader>
|
||||||
|
|
||||||
|
<ProposalFormTerms
|
||||||
|
registerField={register('proposalTerms', {
|
||||||
|
required: t('Required'),
|
||||||
|
validate: (value) => validateJson(value),
|
||||||
|
})}
|
||||||
|
labelOverride={'Terms.updateAsset (JSON format)'}
|
||||||
|
errorMessage={errors?.proposalTerms?.message}
|
||||||
|
customDocLink={DOCS_LINK}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ProposalFormVoteAndEnactmentDeadline
|
||||||
|
voteRegister={register('proposalVoteDeadline', {
|
||||||
|
required: t('Required'),
|
||||||
|
})}
|
||||||
|
voteErrorMessage={errors?.proposalVoteDeadline?.message}
|
||||||
|
voteMinClose={params.governance_proposal_updateAsset_minClose}
|
||||||
|
voteMaxClose={params.governance_proposal_updateAsset_maxClose}
|
||||||
|
enactmentRegister={register('proposalEnactmentDeadline', {
|
||||||
|
required: t('Required'),
|
||||||
|
})}
|
||||||
|
enactmentErrorMessage={
|
||||||
|
errors?.proposalEnactmentDeadline?.message
|
||||||
|
}
|
||||||
|
enactmentMinClose={
|
||||||
|
params.governance_proposal_updateAsset_minEnact
|
||||||
|
}
|
||||||
|
enactmentMaxClose={
|
||||||
|
params.governance_proposal_updateAsset_maxEnact
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ProposalFormSubmit isSubmitting={isSubmitting} />
|
||||||
|
<ProposalFormTransactionDialog
|
||||||
|
finalizedProposal={finalizedProposal}
|
||||||
|
TransactionDialog={Dialog}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</VegaWalletContainer>
|
||||||
|
</AsyncRenderer>
|
||||||
|
);
|
||||||
|
};
|
@ -178,17 +178,17 @@ describe('Propose Update Market', () => {
|
|||||||
|
|
||||||
it('should render the select element with no initial value', async () => {
|
it('should render the select element with no initial value', async () => {
|
||||||
renderComponent();
|
renderComponent();
|
||||||
await waitFor(() =>
|
expect(
|
||||||
expect(screen.getByText('Update market proposal')).toBeInTheDocument()
|
await screen.findByText('Update market proposal')
|
||||||
);
|
).toBeInTheDocument();
|
||||||
expect(screen.getByTestId('proposal-market-select')).toHaveValue('');
|
expect(screen.getByTestId('proposal-market-select')).toHaveValue('');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render the correct market details when the market select is used', async () => {
|
it('should render the correct market details when the market select is used', async () => {
|
||||||
renderComponent();
|
renderComponent();
|
||||||
await waitFor(() =>
|
expect(
|
||||||
expect(screen.getByText('Update market proposal')).toBeInTheDocument()
|
await screen.findByText('Update market proposal')
|
||||||
);
|
).toBeInTheDocument();
|
||||||
fireEvent.change(screen.getByTestId('proposal-market-select'), {
|
fireEvent.change(screen.getByTestId('proposal-market-select'), {
|
||||||
target: {
|
target: {
|
||||||
value:
|
value:
|
||||||
|
@ -8,6 +8,7 @@ import {
|
|||||||
getEnactmentTimestamp,
|
getEnactmentTimestamp,
|
||||||
} from '@vegaprotocol/governance';
|
} from '@vegaprotocol/governance';
|
||||||
import { useEnvironment } from '@vegaprotocol/environment';
|
import { useEnvironment } from '@vegaprotocol/environment';
|
||||||
|
import { validateJson } from '@vegaprotocol/react-helpers';
|
||||||
import {
|
import {
|
||||||
ProposalFormSubheader,
|
ProposalFormSubheader,
|
||||||
ProposalFormMinRequirements,
|
ProposalFormMinRequirements,
|
||||||
@ -60,7 +61,7 @@ export interface UpdateMarketProposalFormFields {
|
|||||||
proposalReference: string;
|
proposalReference: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const docsLink = '/update-market-proposal';
|
const DOCS_LINK = '/update-market-proposal';
|
||||||
|
|
||||||
export const ProposeUpdateMarket = () => {
|
export const ProposeUpdateMarket = () => {
|
||||||
const {
|
const {
|
||||||
@ -158,9 +159,9 @@ export const ProposeUpdateMarket = () => {
|
|||||||
<p className="text-sm" data-testid="proposal-docs-link">
|
<p className="text-sm" data-testid="proposal-docs-link">
|
||||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||||
<Link
|
<Link
|
||||||
href={`${VEGA_DOCS_URL}/tutorials/proposals${docsLink}`}
|
href={`${VEGA_DOCS_URL}/tutorials/proposals${DOCS_LINK}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>{`${VEGA_DOCS_URL}/tutorials/proposals${docsLink}`}</Link>
|
>{`${VEGA_DOCS_URL}/tutorials/proposals${DOCS_LINK}`}</Link>
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -255,20 +256,11 @@ export const ProposeUpdateMarket = () => {
|
|||||||
<ProposalFormTerms
|
<ProposalFormTerms
|
||||||
registerField={register('proposalTerms', {
|
registerField={register('proposalTerms', {
|
||||||
required: t('Required'),
|
required: t('Required'),
|
||||||
validate: {
|
validate: (value) => validateJson(value),
|
||||||
validateJson: (value) => {
|
|
||||||
try {
|
|
||||||
JSON.parse(value);
|
|
||||||
return true;
|
|
||||||
} catch (e) {
|
|
||||||
return t('Must be valid JSON');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})}
|
})}
|
||||||
labelOverride={t('ProposeUpdateMarketTerms')}
|
labelOverride={t('ProposeUpdateMarketTerms')}
|
||||||
errorMessage={errors?.proposalTerms?.message}
|
errorMessage={errors?.proposalTerms?.message}
|
||||||
customDocLink={docsLink}
|
customDocLink={DOCS_LINK}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ProposalFormVoteAndEnactmentDeadline
|
<ProposalFormVoteAndEnactmentDeadline
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
import { NETWORK_PARAMS_QUERY } from '@vegaprotocol/web3';
|
import { NETWORK_PARAMS_QUERY } from '@vegaprotocol/web3';
|
||||||
import type { MockedResponse } from '@apollo/client/testing';
|
import type { MockedResponse } from '@apollo/client/testing';
|
||||||
import type { NetworkParamsQuery } from '@vegaprotocol/web3';
|
import type { NetworkParamsQuery } from '@vegaprotocol/web3';
|
||||||
|
import type { PubKey } from '@vegaprotocol/wallet';
|
||||||
|
|
||||||
export const mockPubkey = '0x123';
|
export const mockPubkey: PubKey = {
|
||||||
|
publicKey: '0x123',
|
||||||
|
name: 'test key 1',
|
||||||
|
};
|
||||||
|
|
||||||
export const mockWalletContext = {
|
export const mockWalletContext = {
|
||||||
pubKey: mockPubkey,
|
pubKey: mockPubkey.publicKey,
|
||||||
pubKeys: [mockPubkey],
|
pubKeys: [mockPubkey],
|
||||||
sendTx: jest.fn().mockReturnValue(Promise.resolve(null)),
|
sendTx: jest.fn().mockReturnValue(Promise.resolve(null)),
|
||||||
connect: jest.fn(),
|
connect: jest.fn(),
|
||||||
|
@ -158,6 +158,13 @@ const LazyGovernanceProposeNewAsset = React.lazy(
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const LazyGovernanceProposeUpdateAsset = React.lazy(
|
||||||
|
() =>
|
||||||
|
import(
|
||||||
|
/* webpackChunkName: "route-governance-propose-update-asset", webpackPrefetch: true */ './governance/propose/update-asset'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
const LazyGovernanceProposeFreeform = React.lazy(
|
const LazyGovernanceProposeFreeform = React.lazy(
|
||||||
() =>
|
() =>
|
||||||
import(
|
import(
|
||||||
@ -273,6 +280,10 @@ const routerConfig = [
|
|||||||
element: <LazyGovernanceProposeUpdateMarket />,
|
element: <LazyGovernanceProposeUpdateMarket />,
|
||||||
},
|
},
|
||||||
{ path: 'new-asset', element: <LazyGovernanceProposeNewAsset /> },
|
{ path: 'new-asset', element: <LazyGovernanceProposeNewAsset /> },
|
||||||
|
{
|
||||||
|
path: 'update-asset',
|
||||||
|
element: <LazyGovernanceProposeUpdateAsset />,
|
||||||
|
},
|
||||||
{ path: 'freeform', element: <LazyGovernanceProposeFreeform /> },
|
{ path: 'freeform', element: <LazyGovernanceProposeFreeform /> },
|
||||||
{ path: 'raw', element: <LazyGovernanceProposeRaw /> },
|
{ path: 'raw', element: <LazyGovernanceProposeRaw /> },
|
||||||
],
|
],
|
||||||
|
@ -43,6 +43,14 @@ export const NetworkParams = {
|
|||||||
governance_proposal_asset_maxClose: 'governance_proposal_asset_maxClose',
|
governance_proposal_asset_maxClose: 'governance_proposal_asset_maxClose',
|
||||||
governance_proposal_asset_minEnact: 'governance_proposal_asset_minEnact',
|
governance_proposal_asset_minEnact: 'governance_proposal_asset_minEnact',
|
||||||
governance_proposal_asset_maxEnact: 'governance_proposal_asset_maxEnact',
|
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_minClose',
|
'governance_proposal_updateNetParam_minClose',
|
||||||
governance_proposal_updateNetParam_maxClose:
|
governance_proposal_updateNetParam_maxClose:
|
||||||
@ -71,6 +79,8 @@ export const NetworkParams = {
|
|||||||
'governance_proposal_asset_requiredParticipation',
|
'governance_proposal_asset_requiredParticipation',
|
||||||
governance_proposal_asset_minProposerBalance:
|
governance_proposal_asset_minProposerBalance:
|
||||||
'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_requiredMajority',
|
'governance_proposal_updateNetParam_requiredMajority',
|
||||||
governance_proposal_updateNetParam_requiredParticipation:
|
governance_proposal_updateNetParam_requiredParticipation:
|
||||||
|
@ -45,3 +45,12 @@ export const suitableForSyntaxHighlighter = (str: string) => {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const validateJson = (value: string) => {
|
||||||
|
try {
|
||||||
|
JSON.parse(value);
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
return t('Must be valid JSON');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@ -168,6 +168,21 @@ interface ProposalNewAssetTerms {
|
|||||||
validationTimestamp: number;
|
validationTimestamp: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ProposalUpdateAssetTerms {
|
||||||
|
updateAsset: {
|
||||||
|
assetId: string;
|
||||||
|
changes: {
|
||||||
|
quantum: string;
|
||||||
|
erc20: {
|
||||||
|
withdrawThreshold: string;
|
||||||
|
lifetimeLimit: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
closingTimestamp: number;
|
||||||
|
enactmentTimestamp: number;
|
||||||
|
}
|
||||||
|
|
||||||
interface OracleSpecBinding {
|
interface OracleSpecBinding {
|
||||||
settlementPriceProperty: string;
|
settlementPriceProperty: string;
|
||||||
tradingTerminationProperty: string;
|
tradingTerminationProperty: string;
|
||||||
@ -227,7 +242,8 @@ export interface ProposalSubmission {
|
|||||||
| ProposalNewMarketTerms
|
| ProposalNewMarketTerms
|
||||||
| ProposalUpdateMarketTerms
|
| ProposalUpdateMarketTerms
|
||||||
| ProposalNetworkParameterTerms
|
| ProposalNetworkParameterTerms
|
||||||
| ProposalNewAssetTerms;
|
| ProposalNewAssetTerms
|
||||||
|
| ProposalUpdateAssetTerms;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ProposalSubmissionBody {
|
export interface ProposalSubmissionBody {
|
||||||
|
Loading…
Reference in New Issue
Block a user