Fix/1622 proposal vote deadline fixes (#1730)
* Fix/1622: Partial progress * Fix/1622: Proposal forms fields now correctly updating when vote deadline 'use min' and 'use max' are clicked * Fix/1622: Removing some unused imports * Fix/1622: WIP commit for generics for deadline component * Fix/1622: WIP commit for generics for deadline component * Fix/1622: Separate minMax functions * Fix/1622: Updated unit tests * Update apps/token/src/routes/governance/components/propose/proposal-form-vote-and-enactment-deadline.spec.tsx Co-authored-by: Dexter Edwards <dexter.edwards93@gmail.com> * Fix/1622: Tweaks from PR comments * Fix/1622: Tests fixes * chore: fix min proposer change for tests * frontend-monorepo-1622 Removed unused value in governance-flow.cy.js Co-authored-by: Dexter Edwards <dexter.edwards93@gmail.com> Co-authored-by: Joe <joe@vega.xyz>
This commit is contained in:
parent
2fa640a467
commit
269d3820dc
@ -61,7 +61,7 @@ context(
|
||||
cy.verify_page_header('The $VEGA token');
|
||||
cy.get_network_parameters().then((network_parameters) => {
|
||||
cy.wrap(
|
||||
network_parameters['spam.protection.proposal.min.tokens'] /
|
||||
network_parameters['spam.protection.voting.min.tokens'] /
|
||||
1000000000000000000
|
||||
).as('minProposerBalance');
|
||||
cy.wrap(
|
||||
@ -115,11 +115,6 @@ context(
|
||||
0.00001,
|
||||
'Asserting that value is at least 0.00001 for network parameter minProposerBalance'
|
||||
);
|
||||
assert.isAtLeast(
|
||||
parseInt(this.minVoterBalance),
|
||||
0.00001,
|
||||
'Asserting that value is at least 0.00001 for network parameter minVoterBalance'
|
||||
);
|
||||
assert.isAtLeast(
|
||||
parseFloat(this.requiredParticipation),
|
||||
0.00001,
|
||||
|
@ -23,16 +23,20 @@ const expectedDate = (expected: string) => new Date(expected).toLocaleString();
|
||||
|
||||
const renderComponent = () => {
|
||||
const register = jest.fn();
|
||||
const setValue = jest.fn();
|
||||
render(
|
||||
<ProposalFormVoteAndEnactmentDeadline
|
||||
onVoteMinMax={setValue}
|
||||
voteRegister={register('proposalVoteDeadline')}
|
||||
voteErrorMessage={undefined}
|
||||
voteMinClose={minVoteDeadline}
|
||||
voteMaxClose={maxVoteDeadline}
|
||||
onEnactMinMax={setValue}
|
||||
enactmentRegister={register('proposalEnactmentDeadline')}
|
||||
enactmentErrorMessage={undefined}
|
||||
enactmentMinClose={minEnactDeadline}
|
||||
enactmentMaxClose={maxEnactDeadline}
|
||||
onValidationMinMax={setValue}
|
||||
validationRequired={true}
|
||||
validationRegister={register('proposalValidationDeadline')}
|
||||
validationErrorMessage={undefined}
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { useEffect, useState, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { parse as ISO8601Parse, toSeconds } from 'iso8601-duration';
|
||||
import {
|
||||
ButtonLink,
|
||||
FormGroup,
|
||||
@ -8,22 +7,29 @@ import {
|
||||
InputError,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { addHours, addMinutes } from 'date-fns';
|
||||
import {
|
||||
deadlineToSeconds,
|
||||
secondsToRoundedHours,
|
||||
} from '@vegaprotocol/governance';
|
||||
import { ProposalFormSubheader } from './proposal-form-subheader';
|
||||
import type { UseFormRegisterReturn } from 'react-hook-form';
|
||||
|
||||
interface DeadlineProps {
|
||||
vote: number;
|
||||
enactment: number;
|
||||
enactment: number | undefined;
|
||||
validation: number;
|
||||
}
|
||||
|
||||
interface DeadlineDatesProps {
|
||||
vote: Date;
|
||||
enactment: Date;
|
||||
enactment: Date | undefined;
|
||||
validation: Date;
|
||||
}
|
||||
|
||||
interface ValidationFormProps {
|
||||
onValidationMinMax:
|
||||
| ((field: 'proposalValidationDeadline', value: string) => void)
|
||||
| undefined;
|
||||
validationRegister:
|
||||
| UseFormRegisterReturn<'proposalValidationDeadline'>
|
||||
| undefined;
|
||||
@ -34,6 +40,7 @@ interface ValidationFormProps {
|
||||
}
|
||||
|
||||
const ValidationForm = ({
|
||||
onValidationMinMax,
|
||||
validationRegister,
|
||||
deadlines,
|
||||
deadlineDates,
|
||||
@ -69,13 +76,24 @@ const ValidationForm = ({
|
||||
<div className="flex items-center gap-4 text-sm">
|
||||
<ButtonLink
|
||||
data-testid="min-validation"
|
||||
onClick={() => updateValidationDeadlineAndDate(0)}
|
||||
onClick={() => {
|
||||
onValidationMinMax &&
|
||||
onValidationMinMax('proposalValidationDeadline', '0');
|
||||
updateValidationDeadlineAndDate(0);
|
||||
}}
|
||||
>
|
||||
{t('UseMin')}
|
||||
</ButtonLink>
|
||||
<ButtonLink
|
||||
data-testid="max-validation"
|
||||
onClick={() => updateValidationDeadlineAndDate(deadlines.vote)}
|
||||
onClick={() => {
|
||||
onValidationMinMax &&
|
||||
onValidationMinMax(
|
||||
'proposalValidationDeadline',
|
||||
deadlines.vote.toString()
|
||||
);
|
||||
updateValidationDeadlineAndDate(deadlines.vote);
|
||||
}}
|
||||
>
|
||||
{t('UseMax')}
|
||||
</ButtonLink>
|
||||
@ -108,6 +126,9 @@ const ValidationForm = ({
|
||||
};
|
||||
|
||||
interface EnactmentFormProps {
|
||||
onEnactMinMax:
|
||||
| ((field: 'proposalEnactmentDeadline', value: string) => void)
|
||||
| undefined;
|
||||
enactmentRegister:
|
||||
| UseFormRegisterReturn<'proposalEnactmentDeadline'>
|
||||
| undefined;
|
||||
@ -120,6 +141,7 @@ interface EnactmentFormProps {
|
||||
}
|
||||
|
||||
const EnactmentForm = ({
|
||||
onEnactMinMax,
|
||||
enactmentRegister,
|
||||
deadlines,
|
||||
deadlineDates,
|
||||
@ -157,13 +179,27 @@ const EnactmentForm = ({
|
||||
<div className="flex items-center gap-4 text-sm">
|
||||
<ButtonLink
|
||||
data-testid="min-enactment"
|
||||
onClick={() => updateEnactmentDeadlineAndDate(minEnactmentHours)}
|
||||
onClick={() => {
|
||||
onEnactMinMax &&
|
||||
onEnactMinMax(
|
||||
'proposalEnactmentDeadline',
|
||||
minEnactmentHours.toString()
|
||||
);
|
||||
updateEnactmentDeadlineAndDate(minEnactmentHours);
|
||||
}}
|
||||
>
|
||||
{t('UseMin')}
|
||||
</ButtonLink>
|
||||
<ButtonLink
|
||||
data-testid="max-enactment"
|
||||
onClick={() => updateEnactmentDeadlineAndDate(maxEnactmentHours)}
|
||||
onClick={() => {
|
||||
onEnactMinMax &&
|
||||
onEnactMinMax(
|
||||
'proposalEnactmentDeadline',
|
||||
maxEnactmentHours.toString()
|
||||
);
|
||||
updateEnactmentDeadlineAndDate(maxEnactmentHours);
|
||||
}}
|
||||
>
|
||||
{t('UseMax')}
|
||||
</ButtonLink>
|
||||
@ -188,33 +224,42 @@ const EnactmentForm = ({
|
||||
};
|
||||
|
||||
export interface ProposalFormVoteAndEnactmentDeadlineProps {
|
||||
onVoteMinMax: (field: 'proposalVoteDeadline', value: string) => void;
|
||||
voteRegister: UseFormRegisterReturn<'proposalVoteDeadline'>;
|
||||
voteErrorMessage: string | undefined;
|
||||
voteMinClose: string;
|
||||
voteMaxClose: string;
|
||||
enactmentRequired?: boolean;
|
||||
onEnactMinMax?: (field: 'proposalEnactmentDeadline', value: string) => void;
|
||||
enactmentRegister?: UseFormRegisterReturn<'proposalEnactmentDeadline'>;
|
||||
enactmentErrorMessage?: string;
|
||||
enactmentMinClose?: string;
|
||||
enactmentMaxClose?: string;
|
||||
validationRequired?: boolean;
|
||||
onValidationMinMax?: (
|
||||
field: 'proposalValidationDeadline',
|
||||
value: string
|
||||
) => void;
|
||||
validationRegister?: UseFormRegisterReturn<'proposalValidationDeadline'>;
|
||||
validationErrorMessage?: string;
|
||||
}
|
||||
|
||||
export const ProposalFormVoteAndEnactmentDeadline = ({
|
||||
export function ProposalFormVoteAndEnactmentDeadline({
|
||||
onVoteMinMax,
|
||||
voteRegister,
|
||||
voteErrorMessage,
|
||||
voteMinClose,
|
||||
voteMaxClose,
|
||||
onEnactMinMax,
|
||||
enactmentRegister,
|
||||
enactmentErrorMessage,
|
||||
enactmentMinClose,
|
||||
enactmentMaxClose,
|
||||
validationRequired,
|
||||
onValidationMinMax,
|
||||
validationRegister,
|
||||
validationErrorMessage,
|
||||
}: ProposalFormVoteAndEnactmentDeadlineProps) => {
|
||||
}: ProposalFormVoteAndEnactmentDeadlineProps) {
|
||||
const {
|
||||
minVoteSeconds,
|
||||
maxVoteSeconds,
|
||||
@ -222,18 +267,12 @@ export const ProposalFormVoteAndEnactmentDeadline = ({
|
||||
maxEnactmentSeconds,
|
||||
} = useMemo(
|
||||
() => ({
|
||||
minVoteSeconds: toSeconds(
|
||||
ISO8601Parse(`PT${voteMinClose.toUpperCase()}`)
|
||||
),
|
||||
maxVoteSeconds: toSeconds(
|
||||
ISO8601Parse(`PT${voteMaxClose.toUpperCase()}`)
|
||||
),
|
||||
minVoteSeconds: deadlineToSeconds(voteMinClose),
|
||||
maxVoteSeconds: deadlineToSeconds(voteMaxClose),
|
||||
minEnactmentSeconds:
|
||||
enactmentMinClose &&
|
||||
toSeconds(ISO8601Parse(`PT${enactmentMinClose.toUpperCase()}`)),
|
||||
enactmentMinClose && deadlineToSeconds(enactmentMinClose),
|
||||
maxEnactmentSeconds:
|
||||
enactmentMaxClose &&
|
||||
toSeconds(ISO8601Parse(`PT${enactmentMaxClose.toUpperCase()}`)),
|
||||
enactmentMaxClose && deadlineToSeconds(enactmentMaxClose),
|
||||
}),
|
||||
[voteMinClose, voteMaxClose, enactmentMinClose, enactmentMaxClose]
|
||||
);
|
||||
@ -243,17 +282,14 @@ export const ProposalFormVoteAndEnactmentDeadline = ({
|
||||
const { minVoteHours, maxVoteHours, minEnactmentHours, maxEnactmentHours } =
|
||||
useMemo(
|
||||
() => ({
|
||||
minVoteHours:
|
||||
Math.floor(minVoteSeconds / 3600) > 1
|
||||
? Math.floor(minVoteSeconds / 3600)
|
||||
: 1,
|
||||
maxVoteHours: Math.floor(maxVoteSeconds / 3600),
|
||||
minEnactmentHours:
|
||||
minEnactmentSeconds && Math.floor(minEnactmentSeconds / 3600) > 1
|
||||
? Math.floor(minEnactmentSeconds / 3600)
|
||||
: 1,
|
||||
maxEnactmentHours:
|
||||
maxEnactmentSeconds && Math.floor(maxEnactmentSeconds / 3600),
|
||||
minVoteHours: secondsToRoundedHours(minVoteSeconds),
|
||||
maxVoteHours: secondsToRoundedHours(maxVoteSeconds),
|
||||
minEnactmentHours: minEnactmentSeconds
|
||||
? secondsToRoundedHours(minEnactmentSeconds)
|
||||
: undefined,
|
||||
maxEnactmentHours: maxEnactmentSeconds
|
||||
? secondsToRoundedHours(maxEnactmentSeconds)
|
||||
: undefined,
|
||||
}),
|
||||
[minVoteSeconds, maxVoteSeconds, minEnactmentSeconds, maxEnactmentSeconds]
|
||||
);
|
||||
@ -269,7 +305,9 @@ export const ProposalFormVoteAndEnactmentDeadline = ({
|
||||
deadlines.vote === minVoteHours
|
||||
? addHours(addMinutes(new Date(), 2), deadlines.vote)
|
||||
: addHours(new Date(), deadlines.vote),
|
||||
enactment: addHours(new Date(), deadlines.vote + deadlines.enactment),
|
||||
enactment: deadlines.enactment
|
||||
? addHours(new Date(), deadlines.vote + deadlines.enactment)
|
||||
: undefined,
|
||||
validation:
|
||||
deadlines.validation === 0
|
||||
? addHours(addMinutes(new Date(), 2), deadlines.validation)
|
||||
@ -284,7 +322,9 @@ export const ProposalFormVoteAndEnactmentDeadline = ({
|
||||
deadlines.vote === minVoteHours
|
||||
? addHours(addMinutes(new Date(), 2), deadlines.vote)
|
||||
: addHours(new Date(), deadlines.vote),
|
||||
enactment: addHours(new Date(), deadlines.vote + deadlines.enactment),
|
||||
enactment: deadlines.enactment
|
||||
? addHours(new Date(), deadlines.vote + deadlines.enactment)
|
||||
: undefined,
|
||||
validation:
|
||||
deadlines.validation === 0
|
||||
? addHours(addMinutes(new Date(), 2), deadlines.validation)
|
||||
@ -301,7 +341,7 @@ export const ProposalFormVoteAndEnactmentDeadline = ({
|
||||
setDeadlines((prev) => ({
|
||||
...prev,
|
||||
vote: hours,
|
||||
validation: Math.min(prev.validation, hours),
|
||||
validation: prev.validation && Math.min(prev.validation, hours),
|
||||
}));
|
||||
|
||||
// If the vote deadline is set to minimum, add 2 mins to the date as we do
|
||||
@ -317,7 +357,9 @@ export const ProposalFormVoteAndEnactmentDeadline = ({
|
||||
hours === minVoteHours
|
||||
? addHours(addMinutes(new Date(), 2), hours)
|
||||
: addHours(new Date(), hours),
|
||||
enactment: addHours(new Date(), hours + deadlines.enactment),
|
||||
enactment: deadlines.enactment
|
||||
? addHours(new Date(), hours + deadlines.enactment)
|
||||
: undefined,
|
||||
validation: addHours(new Date(), Math.min(hours, deadlines.validation)),
|
||||
}));
|
||||
};
|
||||
@ -384,13 +426,19 @@ export const ProposalFormVoteAndEnactmentDeadline = ({
|
||||
<div className="flex items-center gap-4 text-sm">
|
||||
<ButtonLink
|
||||
data-testid="min-vote"
|
||||
onClick={() => updateVoteDeadlineAndDate(minVoteHours)}
|
||||
onClick={() => {
|
||||
onVoteMinMax('proposalVoteDeadline', minVoteHours.toString());
|
||||
updateVoteDeadlineAndDate(minVoteHours);
|
||||
}}
|
||||
>
|
||||
{t('UseMin')}
|
||||
</ButtonLink>
|
||||
<ButtonLink
|
||||
data-testid="max-vote"
|
||||
onClick={() => updateVoteDeadlineAndDate(maxVoteHours)}
|
||||
onClick={() => {
|
||||
onVoteMinMax('proposalVoteDeadline', maxVoteHours.toString());
|
||||
updateVoteDeadlineAndDate(maxVoteHours);
|
||||
}}
|
||||
>
|
||||
{t('UseMax')}
|
||||
</ButtonLink>
|
||||
@ -422,6 +470,7 @@ export const ProposalFormVoteAndEnactmentDeadline = ({
|
||||
|
||||
{validationRequired && (
|
||||
<ValidationForm
|
||||
onValidationMinMax={onValidationMinMax}
|
||||
validationRegister={validationRegister}
|
||||
deadlines={deadlines}
|
||||
deadlineDates={deadlineDates}
|
||||
@ -430,8 +479,9 @@ export const ProposalFormVoteAndEnactmentDeadline = ({
|
||||
/>
|
||||
)}
|
||||
|
||||
{enactmentMinClose && enactmentMaxClose && maxEnactmentHours && (
|
||||
{minEnactmentHours && maxEnactmentHours && (
|
||||
<EnactmentForm
|
||||
onEnactMinMax={onEnactMinMax}
|
||||
enactmentRegister={enactmentRegister}
|
||||
deadlines={deadlines}
|
||||
deadlineDates={deadlineDates}
|
||||
@ -443,4 +493,4 @@ export const ProposalFormVoteAndEnactmentDeadline = ({
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
getClosingTimestamp,
|
||||
useProposalSubmit,
|
||||
deadlineToRoundedHours,
|
||||
} from '@vegaprotocol/governance';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import {
|
||||
@ -44,10 +45,17 @@ export const ProposeFreeform = () => {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { isSubmitting, errors },
|
||||
setValue,
|
||||
} = useForm<FreeformProposalFormFields>();
|
||||
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||
|
||||
const onSubmit = async (fields: FreeformProposalFormFields) => {
|
||||
const isVoteDeadlineAtMinimum =
|
||||
fields.proposalVoteDeadline ===
|
||||
deadlineToRoundedHours(
|
||||
params.governance_proposal_freeform_minClose
|
||||
).toString();
|
||||
|
||||
await submit({
|
||||
rationale: {
|
||||
title: fields.proposalTitle,
|
||||
@ -55,7 +63,10 @@ export const ProposeFreeform = () => {
|
||||
},
|
||||
terms: {
|
||||
newFreeform: {},
|
||||
closingTimestamp: getClosingTimestamp(fields.proposalVoteDeadline),
|
||||
closingTimestamp: getClosingTimestamp(
|
||||
fields.proposalVoteDeadline,
|
||||
isVoteDeadlineAtMinimum
|
||||
),
|
||||
},
|
||||
});
|
||||
};
|
||||
@ -114,6 +125,7 @@ export const ProposeFreeform = () => {
|
||||
/>
|
||||
|
||||
<ProposalFormVoteAndEnactmentDeadline
|
||||
onVoteMinMax={setValue}
|
||||
voteRegister={register('proposalVoteDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
|
@ -9,6 +9,7 @@ import {
|
||||
getClosingTimestamp,
|
||||
getEnactmentTimestamp,
|
||||
useProposalSubmit,
|
||||
deadlineToRoundedHours,
|
||||
} from '@vegaprotocol/governance';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import {
|
||||
@ -89,6 +90,7 @@ export const ProposeNetworkParameter = () => {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { isSubmitting, errors },
|
||||
setValue,
|
||||
} = useForm<NetworkParameterProposalFormFields>();
|
||||
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||
|
||||
@ -100,6 +102,13 @@ export const ProposeNetworkParameter = () => {
|
||||
const acutalNetworkParamKey = fields.proposalNetworkParameterKey
|
||||
.split('_')
|
||||
.join('.');
|
||||
|
||||
const isVoteDeadlineAtMinimum =
|
||||
fields.proposalVoteDeadline ===
|
||||
deadlineToRoundedHours(
|
||||
params.governance_proposal_updateNetParam_minClose
|
||||
).toString();
|
||||
|
||||
await submit({
|
||||
rationale: {
|
||||
title: fields.proposalTitle,
|
||||
@ -112,10 +121,14 @@ export const ProposeNetworkParameter = () => {
|
||||
value: fields.proposalNetworkParameterValue,
|
||||
},
|
||||
},
|
||||
closingTimestamp: getClosingTimestamp(fields.proposalVoteDeadline),
|
||||
closingTimestamp: getClosingTimestamp(
|
||||
fields.proposalVoteDeadline,
|
||||
isVoteDeadlineAtMinimum
|
||||
),
|
||||
enactmentTimestamp: getEnactmentTimestamp(
|
||||
fields.proposalVoteDeadline,
|
||||
fields.proposalEnactmentDeadline
|
||||
fields.proposalEnactmentDeadline,
|
||||
isVoteDeadlineAtMinimum
|
||||
),
|
||||
},
|
||||
});
|
||||
@ -243,6 +256,7 @@ export const ProposeNetworkParameter = () => {
|
||||
)}
|
||||
|
||||
<ProposalFormVoteAndEnactmentDeadline
|
||||
onVoteMinMax={setValue}
|
||||
voteRegister={register('proposalVoteDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
@ -253,6 +267,7 @@ export const ProposeNetworkParameter = () => {
|
||||
voteMaxClose={
|
||||
params.governance_proposal_updateNetParam_maxClose
|
||||
}
|
||||
onEnactMinMax={setValue}
|
||||
enactmentRegister={register('proposalEnactmentDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
|
@ -5,6 +5,7 @@ import {
|
||||
getEnactmentTimestamp,
|
||||
getValidationTimestamp,
|
||||
useProposalSubmit,
|
||||
deadlineToRoundedHours,
|
||||
} from '@vegaprotocol/governance';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import {
|
||||
@ -59,10 +60,17 @@ export const ProposeNewAsset = () => {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { isSubmitting, errors },
|
||||
setValue,
|
||||
} = useForm<NewAssetProposalFormFields>();
|
||||
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||
|
||||
const onSubmit = async (fields: NewAssetProposalFormFields) => {
|
||||
const isVoteDeadlineAtMinimum =
|
||||
fields.proposalVoteDeadline ===
|
||||
deadlineToRoundedHours(
|
||||
params.governance_proposal_asset_minClose
|
||||
).toString();
|
||||
|
||||
await submit({
|
||||
rationale: {
|
||||
title: fields.proposalTitle,
|
||||
@ -72,10 +80,14 @@ export const ProposeNewAsset = () => {
|
||||
newAsset: {
|
||||
...JSON.parse(fields.proposalTerms),
|
||||
},
|
||||
closingTimestamp: getClosingTimestamp(fields.proposalVoteDeadline),
|
||||
closingTimestamp: getClosingTimestamp(
|
||||
fields.proposalVoteDeadline,
|
||||
isVoteDeadlineAtMinimum
|
||||
),
|
||||
enactmentTimestamp: getEnactmentTimestamp(
|
||||
fields.proposalVoteDeadline,
|
||||
fields.proposalEnactmentDeadline
|
||||
fields.proposalEnactmentDeadline,
|
||||
isVoteDeadlineAtMinimum
|
||||
),
|
||||
validationTimestamp: getValidationTimestamp(
|
||||
fields.proposalValidationDeadline
|
||||
@ -155,12 +167,14 @@ export const ProposeNewAsset = () => {
|
||||
/>
|
||||
|
||||
<ProposalFormVoteAndEnactmentDeadline
|
||||
onVoteMinMax={setValue}
|
||||
voteRegister={register('proposalVoteDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
voteErrorMessage={errors?.proposalVoteDeadline?.message}
|
||||
voteMinClose={params.governance_proposal_asset_minClose}
|
||||
voteMaxClose={params.governance_proposal_asset_maxClose}
|
||||
onEnactMinMax={setValue}
|
||||
enactmentRegister={register('proposalEnactmentDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
@ -170,6 +184,7 @@ export const ProposeNewAsset = () => {
|
||||
enactmentMinClose={params.governance_proposal_asset_minEnact}
|
||||
enactmentMaxClose={params.governance_proposal_asset_maxEnact}
|
||||
validationRequired={true}
|
||||
onValidationMinMax={setValue}
|
||||
validationRegister={register('proposalValidationDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
|
@ -4,6 +4,7 @@ import {
|
||||
getClosingTimestamp,
|
||||
getEnactmentTimestamp,
|
||||
useProposalSubmit,
|
||||
deadlineToRoundedHours,
|
||||
} from '@vegaprotocol/governance';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import {
|
||||
@ -57,10 +58,17 @@ export const ProposeNewMarket = () => {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { isSubmitting, errors },
|
||||
setValue,
|
||||
} = useForm<NewMarketProposalFormFields>();
|
||||
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||
|
||||
const onSubmit = async (fields: NewMarketProposalFormFields) => {
|
||||
const isVoteDeadlineAtMinimum =
|
||||
fields.proposalVoteDeadline ===
|
||||
deadlineToRoundedHours(
|
||||
params.governance_proposal_market_minClose
|
||||
).toString();
|
||||
|
||||
await submit({
|
||||
rationale: {
|
||||
title: fields.proposalTitle,
|
||||
@ -70,10 +78,14 @@ export const ProposeNewMarket = () => {
|
||||
newMarket: {
|
||||
...JSON.parse(fields.proposalTerms),
|
||||
},
|
||||
closingTimestamp: getClosingTimestamp(fields.proposalVoteDeadline),
|
||||
closingTimestamp: getClosingTimestamp(
|
||||
fields.proposalVoteDeadline,
|
||||
isVoteDeadlineAtMinimum
|
||||
),
|
||||
enactmentTimestamp: getEnactmentTimestamp(
|
||||
fields.proposalVoteDeadline,
|
||||
fields.proposalEnactmentDeadline
|
||||
fields.proposalEnactmentDeadline,
|
||||
isVoteDeadlineAtMinimum
|
||||
),
|
||||
},
|
||||
});
|
||||
@ -150,12 +162,14 @@ export const ProposeNewMarket = () => {
|
||||
/>
|
||||
|
||||
<ProposalFormVoteAndEnactmentDeadline
|
||||
onVoteMinMax={setValue}
|
||||
voteRegister={register('proposalVoteDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
voteErrorMessage={errors?.proposalVoteDeadline?.message}
|
||||
voteMinClose={params.governance_proposal_market_minClose}
|
||||
voteMaxClose={params.governance_proposal_market_maxClose}
|
||||
onEnactMinMax={setValue}
|
||||
enactmentRegister={register('proposalEnactmentDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
|
@ -4,6 +4,7 @@ import {
|
||||
getClosingTimestamp,
|
||||
getEnactmentTimestamp,
|
||||
useProposalSubmit,
|
||||
deadlineToRoundedHours,
|
||||
} from '@vegaprotocol/governance';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import {
|
||||
@ -57,10 +58,17 @@ export const ProposeUpdateAsset = () => {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { isSubmitting, errors },
|
||||
setValue,
|
||||
} = useForm<UpdateAssetProposalFormFields>();
|
||||
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||
|
||||
const onSubmit = async (fields: UpdateAssetProposalFormFields) => {
|
||||
const isVoteDeadlineAtMinimum =
|
||||
fields.proposalVoteDeadline ===
|
||||
deadlineToRoundedHours(
|
||||
params.governance_proposal_updateAsset_minClose
|
||||
).toString();
|
||||
|
||||
await submit({
|
||||
rationale: {
|
||||
title: fields.proposalTitle,
|
||||
@ -70,10 +78,14 @@ export const ProposeUpdateAsset = () => {
|
||||
updateAsset: {
|
||||
...JSON.parse(fields.proposalTerms),
|
||||
},
|
||||
closingTimestamp: getClosingTimestamp(fields.proposalVoteDeadline),
|
||||
closingTimestamp: getClosingTimestamp(
|
||||
fields.proposalVoteDeadline,
|
||||
isVoteDeadlineAtMinimum
|
||||
),
|
||||
enactmentTimestamp: getEnactmentTimestamp(
|
||||
fields.proposalVoteDeadline,
|
||||
fields.proposalEnactmentDeadline
|
||||
fields.proposalEnactmentDeadline,
|
||||
isVoteDeadlineAtMinimum
|
||||
),
|
||||
},
|
||||
});
|
||||
@ -152,6 +164,7 @@ export const ProposeUpdateAsset = () => {
|
||||
/>
|
||||
|
||||
<ProposalFormVoteAndEnactmentDeadline
|
||||
onVoteMinMax={setValue}
|
||||
voteRegister={register('proposalVoteDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
|
@ -6,6 +6,7 @@ import {
|
||||
getClosingTimestamp,
|
||||
getEnactmentTimestamp,
|
||||
useProposalSubmit,
|
||||
deadlineToRoundedHours,
|
||||
} from '@vegaprotocol/governance';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import {
|
||||
@ -117,10 +118,17 @@ export const ProposeUpdateMarket = () => {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { isSubmitting, errors },
|
||||
setValue,
|
||||
} = useForm<UpdateMarketProposalFormFields>();
|
||||
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||
|
||||
const onSubmit = async (fields: UpdateMarketProposalFormFields) => {
|
||||
const isVoteDeadlineAtMinimum =
|
||||
fields.proposalVoteDeadline ===
|
||||
deadlineToRoundedHours(
|
||||
params.governance_proposal_updateMarket_minClose
|
||||
).toString();
|
||||
|
||||
await submit({
|
||||
rationale: {
|
||||
title: fields.proposalTitle,
|
||||
@ -133,10 +141,14 @@ export const ProposeUpdateMarket = () => {
|
||||
...JSON.parse(fields.proposalTerms),
|
||||
},
|
||||
},
|
||||
closingTimestamp: getClosingTimestamp(fields.proposalVoteDeadline),
|
||||
closingTimestamp: getClosingTimestamp(
|
||||
fields.proposalVoteDeadline,
|
||||
isVoteDeadlineAtMinimum
|
||||
),
|
||||
enactmentTimestamp: getEnactmentTimestamp(
|
||||
fields.proposalVoteDeadline,
|
||||
fields.proposalEnactmentDeadline
|
||||
fields.proposalEnactmentDeadline,
|
||||
isVoteDeadlineAtMinimum
|
||||
),
|
||||
},
|
||||
});
|
||||
@ -269,6 +281,7 @@ export const ProposeUpdateMarket = () => {
|
||||
/>
|
||||
|
||||
<ProposalFormVoteAndEnactmentDeadline
|
||||
onVoteMinMax={setValue}
|
||||
voteRegister={register('proposalVoteDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
@ -279,6 +292,7 @@ export const ProposeUpdateMarket = () => {
|
||||
voteMaxClose={
|
||||
params.governance_proposal_updateMarket_maxClose
|
||||
}
|
||||
onEnactMinMax={setValue}
|
||||
enactmentRegister={register('proposalEnactmentDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
|
41
libs/governance/src/utils/deadline-helpers.spec.ts
Normal file
41
libs/governance/src/utils/deadline-helpers.spec.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import { deadlineToSeconds, secondsToRoundedHours } from './deadline-helpers';
|
||||
|
||||
describe('deadlineToSeconds', () => {
|
||||
it('should throw an error if the deadline does not match the format "XhXmXs"', () => {
|
||||
expect(() => deadlineToSeconds('abcde')).toThrowError(
|
||||
'Invalid deadline format, expected format "XhXmXs", got "abcde"'
|
||||
);
|
||||
});
|
||||
|
||||
it('should convert "0h0m1s" to 1', () => {
|
||||
expect(deadlineToSeconds('0h0m1s')).toEqual(1);
|
||||
});
|
||||
|
||||
it('should convert "0h1m0s" to 60', () => {
|
||||
expect(deadlineToSeconds('0h1m0s')).toEqual(60);
|
||||
});
|
||||
|
||||
it('should convert "1h0m0s" to 3600', () => {
|
||||
expect(deadlineToSeconds('1h0m0s')).toEqual(3600);
|
||||
});
|
||||
|
||||
it('should convert "1h1m1s" to 3661', () => {
|
||||
expect(deadlineToSeconds('1h1m1s')).toEqual(3661);
|
||||
});
|
||||
});
|
||||
|
||||
describe('secondsToRoundedHours', () => {
|
||||
it('should return 1 hour for anything up to 3600 seconds', () => {
|
||||
expect(secondsToRoundedHours(0)).toEqual(1);
|
||||
expect(secondsToRoundedHours(3600)).toEqual(1);
|
||||
});
|
||||
|
||||
it('should round to the nearest hour for anything more than 3600 seconds', () => {
|
||||
// 5399 seconds is 1 hour 29 minutes 59 seconds
|
||||
expect(secondsToRoundedHours(5399)).toEqual(1);
|
||||
expect(secondsToRoundedHours(5400)).toEqual(2);
|
||||
// 8999 seconds is 2 hours 29 minutes 59 seconds
|
||||
expect(secondsToRoundedHours(8999)).toEqual(2);
|
||||
expect(secondsToRoundedHours(9000)).toEqual(3);
|
||||
});
|
||||
});
|
22
libs/governance/src/utils/deadline-helpers.ts
Normal file
22
libs/governance/src/utils/deadline-helpers.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { parse as ISO8601Parse, toSeconds } from 'iso8601-duration';
|
||||
|
||||
// Converts API deadlines ("XhXmXs") to seconds
|
||||
export const deadlineToSeconds = (deadline: string) => {
|
||||
// check that the deadline string matches the format "XhXmXs"
|
||||
const regex = /^(\d+h)?(\d+m)?(\d+s)?$/;
|
||||
if (!regex.test(deadline)) {
|
||||
throw new Error(
|
||||
`Invalid deadline format, expected format "XhXmXs", got "${deadline}"`
|
||||
);
|
||||
}
|
||||
return toSeconds(ISO8601Parse(`PT${deadline.toUpperCase()}`));
|
||||
};
|
||||
|
||||
// Converts seconds to rounded hours, min 1 hour
|
||||
export const secondsToRoundedHours = (seconds: number) => {
|
||||
const hours = Math.round(seconds / 3600);
|
||||
return hours < 1 ? 1 : hours;
|
||||
};
|
||||
|
||||
export const deadlineToRoundedHours = (deadline: string) =>
|
||||
secondsToRoundedHours(deadlineToSeconds(deadline));
|
@ -11,23 +11,31 @@ afterEach(() => {
|
||||
});
|
||||
|
||||
describe('getClosingTimestamp', () => {
|
||||
it('should return the correct timestamp if the proposalVoteDeadline is 1 (when 2 mins are added)', () => {
|
||||
it('should return the correct timestamp if the proposalVoteDeadline is set to minimum (when 2 mins are added)', () => {
|
||||
const proposalVoteDeadline = '1';
|
||||
const isMinimumDeadlineSelected = true;
|
||||
const expected = Math.floor(
|
||||
getTime(
|
||||
addHours(addMinutes(new Date(), 2), Number(proposalVoteDeadline))
|
||||
) / 1000
|
||||
);
|
||||
const actual = getClosingTimestamp(proposalVoteDeadline);
|
||||
const actual = getClosingTimestamp(
|
||||
proposalVoteDeadline,
|
||||
isMinimumDeadlineSelected
|
||||
);
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should return the correct timestamp if the proposalVoteDeadline is 2 (when no extra mins are added)', () => {
|
||||
it('should return the correct timestamp if the proposalVoteDeadline is not set to minimum (when no extra mins are added)', () => {
|
||||
const proposalVoteDeadline = '2';
|
||||
const isMinimumDeadlineSelected = false;
|
||||
const expected = Math.floor(
|
||||
getTime(addHours(new Date(), Number(proposalVoteDeadline))) / 1000
|
||||
);
|
||||
const actual = getClosingTimestamp(proposalVoteDeadline);
|
||||
const actual = getClosingTimestamp(
|
||||
proposalVoteDeadline,
|
||||
isMinimumDeadlineSelected
|
||||
);
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
@ -1,13 +1,16 @@
|
||||
import { addHours, addMinutes, getTime } from 'date-fns';
|
||||
|
||||
// If proposaVoteDeadline is at its minimum of 1 hour, then we add
|
||||
// If proposaVoteDeadline is at its minimum, then we add
|
||||
// 2 extra minutes to the closing timestamp to ensure that there's time
|
||||
// to confirm in the wallet.
|
||||
|
||||
export const getClosingTimestamp = (proposalVoteDeadline: string) =>
|
||||
export const getClosingTimestamp = (
|
||||
proposalVoteDeadline: string,
|
||||
minimumDeadlineSelected: boolean
|
||||
) =>
|
||||
Math.floor(
|
||||
getTime(
|
||||
proposalVoteDeadline === '1'
|
||||
minimumDeadlineSelected
|
||||
? addHours(addMinutes(new Date(), 2), Number(proposalVoteDeadline))
|
||||
: addHours(new Date(), Number(proposalVoteDeadline))
|
||||
) / 1000
|
||||
|
@ -13,6 +13,7 @@ afterEach(() => {
|
||||
describe('getEnactmentTimestamp', () => {
|
||||
it('should return the correct timestamp', () => {
|
||||
const proposalVoteDeadline = '2';
|
||||
const isMinimumVoteDeadlineSelected = false;
|
||||
const enactmentDeadline = '1';
|
||||
const expected = Math.floor(
|
||||
getTime(
|
||||
@ -24,7 +25,8 @@ describe('getEnactmentTimestamp', () => {
|
||||
);
|
||||
const actual = getEnactmentTimestamp(
|
||||
proposalVoteDeadline,
|
||||
enactmentDeadline
|
||||
enactmentDeadline,
|
||||
isMinimumVoteDeadlineSelected
|
||||
);
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
@ -3,12 +3,20 @@ import { getClosingTimestamp } from './get-closing-timestamp';
|
||||
|
||||
export const getEnactmentTimestamp = (
|
||||
proposalVoteDeadline: string,
|
||||
enactmentDeadline: string
|
||||
enactmentDeadline: string,
|
||||
minimumVoteDeadlineSelected: boolean
|
||||
) =>
|
||||
Math.floor(
|
||||
getTime(
|
||||
addHours(
|
||||
new Date(fromUnixTime(getClosingTimestamp(proposalVoteDeadline))),
|
||||
new Date(
|
||||
fromUnixTime(
|
||||
getClosingTimestamp(
|
||||
proposalVoteDeadline,
|
||||
minimumVoteDeadlineSelected
|
||||
)
|
||||
)
|
||||
),
|
||||
Number(enactmentDeadline)
|
||||
)
|
||||
) / 1000
|
||||
|
@ -2,3 +2,4 @@ export * from './proposal-dialog-helpers';
|
||||
export * from './get-closing-timestamp';
|
||||
export * from './get-enactment-timestamp';
|
||||
export * from './get-validation-timestamp';
|
||||
export * from './deadline-helpers';
|
||||
|
Loading…
Reference in New Issue
Block a user