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:
Sam Keen 2022-10-21 13:47:46 +01:00 committed by GitHub
parent 2fa640a467
commit 269d3820dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 282 additions and 65 deletions

View File

@ -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,

View File

@ -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}

View File

@ -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 = ({
)}
</>
);
};
}

View File

@ -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'),
})}

View File

@ -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'),
})}

View File

@ -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'),
})}

View File

@ -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'),
})}

View File

@ -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'),
})}

View File

@ -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'),
})}

View 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);
});
});

View 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));

View File

@ -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);
});
});

View File

@ -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

View File

@ -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);
});

View File

@ -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

View File

@ -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';