fix(1837): account for proposal vote and enactment deadlines being uncoupled (#2005)
* Fix/1837: Remove 2 seconds from proposal vote deadline on submission to ensure the deadline is always slightly below the maximum the API accepts * fix(1837): Adjust for vote deadline and enactment deadline being decoupled in the API * fix(1837): Removed unnecessary dependencies * fix(1837): A couple of extra tests for get-enactment-timestamp * fix(1837): propose-update-asset.tsx tweaked to ensure max enactment button works properly
This commit is contained in:
parent
266f87be8f
commit
d3cb3896f4
@ -675,13 +675,14 @@
|
||||
"ProposalVoteTitle": "Vote deadline",
|
||||
"ProposalVoteAndEnactmentTitle": "Vote deadline and enactment",
|
||||
"ProposalVoteDeadline": "Time till voting closes",
|
||||
"ProposalEnactmentDeadline": "Time till enactment (after vote close)",
|
||||
"ProposalEnactmentDeadline": "Time till enactment (must be equal to or after vote close)",
|
||||
"ProposalValidationDeadline": "Time till ERC-20 asset validation. Maximum value is affected by the vote deadline.",
|
||||
"ThisWillSetVotingDeadlineTo": "This will set the voting deadline to",
|
||||
"ThisWillSetEnactmentDeadlineTo": "This will set the enactment date to",
|
||||
"ThisWillSetValidationDeadlineTo": "This will set the validation deadline to",
|
||||
"Hours": "hours",
|
||||
"ThisWillAdd2MinutesToAllowTimeToConfirmInWallet": "Note: we add 2 minutes of extra time when you choose the minimum value. This gives you time to confirm the proposal in your wallet.",
|
||||
"ProposalWillFailIfEnactmentIsEarlierThanVotingDeadline": "Proposal will fail if enactment is earlier than the voting deadline",
|
||||
"SelectAMarketToChange": "Select a market to change",
|
||||
"MarketName": "Market name",
|
||||
"MarketCode": "Market code",
|
||||
|
@ -20,7 +20,7 @@ afterEach(() => {
|
||||
const minVoteDeadline = '1h0m0s';
|
||||
const maxVoteDeadline = '5h0m0s';
|
||||
const minEnactDeadline = '1h0m0s';
|
||||
const maxEnactDeadline = '4h0m0s';
|
||||
const maxEnactDeadline = '5h0m0s';
|
||||
|
||||
/**
|
||||
* Formats date according to locale.
|
||||
@ -118,7 +118,7 @@ describe('Proposal form vote, validation and enactment deadline', () => {
|
||||
const maxButton = screen.getByTestId('max-enactment');
|
||||
const minButton = screen.getByTestId('min-enactment');
|
||||
fireEvent.click(maxButton);
|
||||
expect(enactmentDeadlineInput).toHaveValue(4);
|
||||
expect(enactmentDeadlineInput).toHaveValue(5);
|
||||
fireEvent.click(minButton);
|
||||
expect(enactmentDeadlineInput).toHaveValue(1);
|
||||
});
|
||||
@ -154,7 +154,24 @@ describe('Proposal form vote, validation and enactment deadline', () => {
|
||||
'2022-01-01T00:02:00.000Z'
|
||||
);
|
||||
expect(screen.getByTestId('enactment-date')).toHaveTextContent(
|
||||
'2022-01-01T02:00:00.000Z'
|
||||
'2022-01-01T01:02:00.000Z'
|
||||
);
|
||||
// When max values are used, the deadlines should have 2 seconds subtracted
|
||||
// from them to account any delays
|
||||
const voteDeadlineMaxButton = screen.getByTestId('max-vote');
|
||||
const enactmentDeadlineMaxButton = screen.getByTestId('max-enactment');
|
||||
const validationDeadlineMaxButton = screen.getByTestId('max-validation');
|
||||
fireEvent.click(voteDeadlineMaxButton);
|
||||
fireEvent.click(enactmentDeadlineMaxButton);
|
||||
fireEvent.click(validationDeadlineMaxButton);
|
||||
expect(screen.getByTestId('voting-date')).toHaveTextContent(
|
||||
'2022-01-01T04:59:58.000Z'
|
||||
);
|
||||
expect(screen.getByTestId('validation-date')).toHaveTextContent(
|
||||
'2022-01-01T04:59:58.000Z'
|
||||
);
|
||||
expect(screen.getByTestId('enactment-date')).toHaveTextContent(
|
||||
'2022-01-01T04:59:58.000Z'
|
||||
);
|
||||
});
|
||||
|
||||
@ -171,19 +188,7 @@ describe('Proposal form vote, validation and enactment deadline', () => {
|
||||
'2022-01-01T00:02:30.000Z'
|
||||
);
|
||||
expect(screen.getByTestId('enactment-date')).toHaveTextContent(
|
||||
'2022-01-01T02:00:30.000Z'
|
||||
);
|
||||
});
|
||||
|
||||
it('update the vote deadline date and the enactment deadline date when the vote deadline is changed', () => {
|
||||
renderComponent();
|
||||
const voteDeadlineInput = screen.getByTestId('proposal-vote-deadline');
|
||||
fireEvent.change(voteDeadlineInput, { target: { value: 2 } });
|
||||
expect(screen.getByTestId('voting-date')).toHaveTextContent(
|
||||
'2022-01-01T02:00:00.000Z'
|
||||
);
|
||||
expect(screen.getByTestId('enactment-date')).toHaveTextContent(
|
||||
'2022-01-01T03:00:00.000Z'
|
||||
'2022-01-01T01:02:30.000Z'
|
||||
);
|
||||
});
|
||||
|
||||
@ -198,7 +203,7 @@ describe('Proposal form vote, validation and enactment deadline', () => {
|
||||
fireEvent.click(voteDeadlineMaxButton);
|
||||
fireEvent.click(validationDeadlineMaxButton);
|
||||
expect(screen.getByTestId('validation-date')).toHaveTextContent(
|
||||
'2022-01-01T05:00:00.000Z'
|
||||
'2022-01-01T04:59:58.000Z'
|
||||
);
|
||||
expect(validationDeadlineInput).toHaveValue(5);
|
||||
fireEvent.click(voteDeadlineMinButton);
|
||||
@ -207,4 +212,19 @@ describe('Proposal form vote, validation and enactment deadline', () => {
|
||||
);
|
||||
expect(validationDeadlineInput).toHaveValue(1);
|
||||
});
|
||||
|
||||
it('displays error text if the vote deadline is set later than the enactment deadline', () => {
|
||||
renderComponent();
|
||||
const voteDeadlineInput = screen.getByTestId('proposal-vote-deadline');
|
||||
const enactmentDeadlineInput = screen.getByTestId(
|
||||
'proposal-enactment-deadline'
|
||||
);
|
||||
fireEvent.change(voteDeadlineInput, { target: { value: 5 } });
|
||||
fireEvent.change(enactmentDeadlineInput, { target: { value: 2 } });
|
||||
expect(
|
||||
screen.getByTestId('enactment-before-voting-deadline')
|
||||
).toHaveTextContent(
|
||||
'Proposal will fail if enactment is earlier than the voting deadline'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -7,10 +7,12 @@ import {
|
||||
InputError,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { getDateTimeFormat } from '@vegaprotocol/react-helpers';
|
||||
import { addHours, addMinutes } from 'date-fns';
|
||||
import { addHours } from 'date-fns';
|
||||
import {
|
||||
addTwoMinutes,
|
||||
deadlineToSeconds,
|
||||
secondsToRoundedHours,
|
||||
subtractTwoSeconds,
|
||||
} from '@vegaprotocol/governance';
|
||||
import { ProposalFormSubheader } from './proposal-form-subheader';
|
||||
import type { UseFormRegisterReturn } from 'react-hook-form';
|
||||
@ -218,6 +220,22 @@ const EnactmentForm = ({
|
||||
<span data-testid="enactment-date" className="pl-2">
|
||||
{getDateTimeFormat().format(deadlineDates.enactment)}
|
||||
</span>
|
||||
{deadlines.enactment === minEnactmentHours && (
|
||||
<span
|
||||
data-testid="enactment-2-mins-extra"
|
||||
className="block mt-4 font-light"
|
||||
>
|
||||
{t('ThisWillAdd2MinutesToAllowTimeToConfirmInWallet')}
|
||||
</span>
|
||||
)}
|
||||
{deadlines.enactment && deadlines.enactment < deadlines.vote && (
|
||||
<span
|
||||
data-testid="enactment-before-voting-deadline"
|
||||
className="block mt-4 text-vega-red-dark"
|
||||
>
|
||||
{t('ProposalWillFailIfEnactmentIsEarlierThanVotingDeadline')}
|
||||
</span>
|
||||
)}
|
||||
</p>
|
||||
)}
|
||||
</FormGroup>
|
||||
@ -302,16 +320,13 @@ export function ProposalFormVoteAndEnactmentDeadline({
|
||||
});
|
||||
|
||||
const [deadlineDates, setDeadlineDates] = useState<DeadlineDatesProps>({
|
||||
vote:
|
||||
deadlines.vote === minVoteHours
|
||||
? addHours(addMinutes(new Date(), 2), deadlines.vote)
|
||||
: addHours(new Date(), deadlines.vote),
|
||||
vote: addHours(addTwoMinutes(), deadlines.vote),
|
||||
enactment: deadlines.enactment
|
||||
? addHours(new Date(), deadlines.vote + deadlines.enactment)
|
||||
? addHours(addTwoMinutes(), deadlines.enactment)
|
||||
: undefined,
|
||||
validation:
|
||||
deadlines.validation === 0
|
||||
? addHours(addMinutes(new Date(), 2), deadlines.validation)
|
||||
? addHours(addTwoMinutes(), deadlines.validation)
|
||||
: addHours(new Date(), deadlines.validation),
|
||||
});
|
||||
|
||||
@ -319,21 +334,40 @@ export function ProposalFormVoteAndEnactmentDeadline({
|
||||
const interval = setInterval(() => {
|
||||
setDeadlineDates((prev) => ({
|
||||
...prev,
|
||||
vote:
|
||||
deadlines.vote === minVoteHours
|
||||
? addHours(addMinutes(new Date(), 2), deadlines.vote)
|
||||
: addHours(new Date(), deadlines.vote),
|
||||
vote: addHours(
|
||||
(deadlines.vote === minVoteHours && addTwoMinutes()) ||
|
||||
(deadlines.vote === maxVoteHours && subtractTwoSeconds()) ||
|
||||
new Date(),
|
||||
deadlines.vote
|
||||
),
|
||||
enactment: deadlines.enactment
|
||||
? addHours(new Date(), deadlines.vote + deadlines.enactment)
|
||||
? addHours(
|
||||
(deadlines.enactment === minEnactmentHours && addTwoMinutes()) ||
|
||||
(deadlines.enactment === maxEnactmentHours &&
|
||||
subtractTwoSeconds()) ||
|
||||
new Date(),
|
||||
deadlines.enactment
|
||||
)
|
||||
: undefined,
|
||||
validation:
|
||||
deadlines.validation === 0
|
||||
? addHours(addMinutes(new Date(), 2), deadlines.validation)
|
||||
: addHours(new Date(), deadlines.validation),
|
||||
? addHours(addTwoMinutes(), deadlines.validation)
|
||||
: addHours(
|
||||
(deadlines.validation === maxVoteHours &&
|
||||
subtractTwoSeconds()) ||
|
||||
new Date(),
|
||||
deadlines.validation
|
||||
),
|
||||
}));
|
||||
}, 1000);
|
||||
return () => clearInterval(interval);
|
||||
}, [deadlines, minVoteHours]);
|
||||
}, [
|
||||
deadlines,
|
||||
maxEnactmentHours,
|
||||
maxVoteHours,
|
||||
minEnactmentHours,
|
||||
minVoteHours,
|
||||
]);
|
||||
|
||||
const updateVoteDeadlineAndDate = (hours: number) => {
|
||||
// Validation, when needed, can only happen within the voting period. Therefore, if the
|
||||
@ -345,22 +379,24 @@ export function ProposalFormVoteAndEnactmentDeadline({
|
||||
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
|
||||
// If the vote deadlines are set to minimum, add 2 mins to the date as we do
|
||||
// this on submission to allow time to confirm in the wallet. Amending the
|
||||
// vote deadline also changes the enactment date and potentially the validation
|
||||
// date.
|
||||
// The validation deadline date cannot be after the vote deadline date. Therefore,
|
||||
// if the vote deadline is changed, the validation deadline must potentially
|
||||
// be changed to be within the new vote deadline.
|
||||
// Whilst it's not ideal, currently enactment deadlines are uncoupled from
|
||||
// vote deadlines in the API. Therefore, the UI currently is too, so updating
|
||||
// the vote deadline does not update the enactment deadline.
|
||||
setDeadlineDates((prev) => ({
|
||||
...prev,
|
||||
vote:
|
||||
hours === minVoteHours
|
||||
? addHours(addMinutes(new Date(), 2), hours)
|
||||
: addHours(new Date(), hours),
|
||||
enactment: deadlines.enactment
|
||||
? addHours(new Date(), hours + deadlines.enactment)
|
||||
: undefined,
|
||||
vote: addHours(
|
||||
(hours === minVoteHours && addTwoMinutes()) ||
|
||||
(hours === maxVoteHours && subtractTwoSeconds()) ||
|
||||
new Date(),
|
||||
hours
|
||||
),
|
||||
validation: addHours(new Date(), Math.min(hours, deadlines.validation)),
|
||||
}));
|
||||
};
|
||||
@ -373,7 +409,12 @@ export function ProposalFormVoteAndEnactmentDeadline({
|
||||
|
||||
setDeadlineDates((prev) => ({
|
||||
...prev,
|
||||
enactment: addHours(deadlineDates.vote, hours),
|
||||
enactment: addHours(
|
||||
(hours === minEnactmentHours && addTwoMinutes()) ||
|
||||
(hours === maxEnactmentHours && subtractTwoSeconds()) ||
|
||||
new Date(),
|
||||
hours
|
||||
),
|
||||
}));
|
||||
};
|
||||
|
||||
@ -385,10 +426,12 @@ export function ProposalFormVoteAndEnactmentDeadline({
|
||||
|
||||
setDeadlineDates((prev) => ({
|
||||
...prev,
|
||||
validation:
|
||||
hours === 0
|
||||
? addHours(addMinutes(new Date(), 2), hours)
|
||||
: addHours(new Date(), hours),
|
||||
validation: addHours(
|
||||
(hours === 0 && addTwoMinutes()) ||
|
||||
(hours === maxVoteHours && subtractTwoSeconds()) ||
|
||||
new Date(),
|
||||
hours
|
||||
),
|
||||
}));
|
||||
};
|
||||
|
||||
|
@ -56,6 +56,12 @@ export const ProposeFreeform = () => {
|
||||
params.governance_proposal_freeform_minClose
|
||||
).toString();
|
||||
|
||||
const isVoteDeadlineAtMaximum =
|
||||
fields.proposalVoteDeadline ===
|
||||
deadlineToRoundedHours(
|
||||
params.governance_proposal_freeform_maxClose
|
||||
).toString();
|
||||
|
||||
await submit({
|
||||
rationale: {
|
||||
title: fields.proposalTitle,
|
||||
@ -65,7 +71,8 @@ export const ProposeFreeform = () => {
|
||||
newFreeform: {},
|
||||
closingTimestamp: getClosingTimestamp(
|
||||
fields.proposalVoteDeadline,
|
||||
isVoteDeadlineAtMinimum
|
||||
isVoteDeadlineAtMinimum,
|
||||
isVoteDeadlineAtMaximum
|
||||
),
|
||||
},
|
||||
});
|
||||
|
@ -9,7 +9,7 @@ import {
|
||||
getClosingTimestamp,
|
||||
getEnactmentTimestamp,
|
||||
useProposalSubmit,
|
||||
deadlineToRoundedHours,
|
||||
doesValueEquateToParam,
|
||||
} from '@vegaprotocol/governance';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import {
|
||||
@ -103,11 +103,22 @@ export const ProposeNetworkParameter = () => {
|
||||
.split('_')
|
||||
.join('.');
|
||||
|
||||
const isVoteDeadlineAtMinimum =
|
||||
fields.proposalVoteDeadline ===
|
||||
deadlineToRoundedHours(
|
||||
const isVoteDeadlineAtMinimum = doesValueEquateToParam(
|
||||
fields.proposalVoteDeadline,
|
||||
params.governance_proposal_updateNetParam_minClose
|
||||
).toString();
|
||||
);
|
||||
const isVoteDeadlineAtMaximum = doesValueEquateToParam(
|
||||
fields.proposalVoteDeadline,
|
||||
params.governance_proposal_updateNetParam_maxClose
|
||||
);
|
||||
const isEnactmentDeadlineAtMinimum = doesValueEquateToParam(
|
||||
fields.proposalEnactmentDeadline,
|
||||
params.governance_proposal_updateNetParam_minEnact
|
||||
);
|
||||
const isEnactmentDeadlineAtMaximum = doesValueEquateToParam(
|
||||
fields.proposalEnactmentDeadline,
|
||||
params.governance_proposal_updateNetParam_maxEnact
|
||||
);
|
||||
|
||||
await submit({
|
||||
rationale: {
|
||||
@ -123,12 +134,13 @@ export const ProposeNetworkParameter = () => {
|
||||
},
|
||||
closingTimestamp: getClosingTimestamp(
|
||||
fields.proposalVoteDeadline,
|
||||
isVoteDeadlineAtMinimum
|
||||
isVoteDeadlineAtMinimum,
|
||||
isVoteDeadlineAtMaximum
|
||||
),
|
||||
enactmentTimestamp: getEnactmentTimestamp(
|
||||
fields.proposalVoteDeadline,
|
||||
fields.proposalEnactmentDeadline,
|
||||
isVoteDeadlineAtMinimum
|
||||
isEnactmentDeadlineAtMinimum,
|
||||
isEnactmentDeadlineAtMaximum
|
||||
),
|
||||
},
|
||||
});
|
||||
|
@ -5,7 +5,7 @@ import {
|
||||
getEnactmentTimestamp,
|
||||
getValidationTimestamp,
|
||||
useProposalSubmit,
|
||||
deadlineToRoundedHours,
|
||||
doesValueEquateToParam,
|
||||
} from '@vegaprotocol/governance';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import {
|
||||
@ -65,11 +65,26 @@ export const ProposeNewAsset = () => {
|
||||
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||
|
||||
const onSubmit = async (fields: NewAssetProposalFormFields) => {
|
||||
const isVoteDeadlineAtMinimum =
|
||||
fields.proposalVoteDeadline ===
|
||||
deadlineToRoundedHours(
|
||||
const isVoteDeadlineAtMinimum = doesValueEquateToParam(
|
||||
fields.proposalVoteDeadline,
|
||||
params.governance_proposal_asset_minClose
|
||||
).toString();
|
||||
);
|
||||
const isVoteDeadlineAtMaximum = doesValueEquateToParam(
|
||||
fields.proposalVoteDeadline,
|
||||
params.governance_proposal_asset_maxClose
|
||||
);
|
||||
const isEnactmentDeadlineAtMinimum = doesValueEquateToParam(
|
||||
fields.proposalEnactmentDeadline,
|
||||
params.governance_proposal_asset_minEnact
|
||||
);
|
||||
const isEnactmentDeadlineAtMaximum = doesValueEquateToParam(
|
||||
fields.proposalEnactmentDeadline,
|
||||
params.governance_proposal_asset_maxEnact
|
||||
);
|
||||
const isValidationDeadlineAtMaximum = doesValueEquateToParam(
|
||||
fields.proposalValidationDeadline,
|
||||
params.governance_proposal_asset_maxClose
|
||||
);
|
||||
|
||||
await submit({
|
||||
rationale: {
|
||||
@ -82,15 +97,17 @@ export const ProposeNewAsset = () => {
|
||||
},
|
||||
closingTimestamp: getClosingTimestamp(
|
||||
fields.proposalVoteDeadline,
|
||||
isVoteDeadlineAtMinimum
|
||||
isVoteDeadlineAtMinimum,
|
||||
isVoteDeadlineAtMaximum
|
||||
),
|
||||
enactmentTimestamp: getEnactmentTimestamp(
|
||||
fields.proposalVoteDeadline,
|
||||
fields.proposalEnactmentDeadline,
|
||||
isVoteDeadlineAtMinimum
|
||||
isEnactmentDeadlineAtMinimum,
|
||||
isEnactmentDeadlineAtMaximum
|
||||
),
|
||||
validationTimestamp: getValidationTimestamp(
|
||||
fields.proposalValidationDeadline
|
||||
fields.proposalValidationDeadline,
|
||||
isValidationDeadlineAtMaximum
|
||||
),
|
||||
},
|
||||
});
|
||||
|
@ -4,7 +4,7 @@ import {
|
||||
getClosingTimestamp,
|
||||
getEnactmentTimestamp,
|
||||
useProposalSubmit,
|
||||
deadlineToRoundedHours,
|
||||
doesValueEquateToParam,
|
||||
} from '@vegaprotocol/governance';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import {
|
||||
@ -63,11 +63,22 @@ export const ProposeNewMarket = () => {
|
||||
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||
|
||||
const onSubmit = async (fields: NewMarketProposalFormFields) => {
|
||||
const isVoteDeadlineAtMinimum =
|
||||
fields.proposalVoteDeadline ===
|
||||
deadlineToRoundedHours(
|
||||
const isVoteDeadlineAtMinimum = doesValueEquateToParam(
|
||||
fields.proposalVoteDeadline,
|
||||
params.governance_proposal_market_minClose
|
||||
).toString();
|
||||
);
|
||||
const isVoteDeadlineAtMaximum = doesValueEquateToParam(
|
||||
fields.proposalVoteDeadline,
|
||||
params.governance_proposal_market_maxClose
|
||||
);
|
||||
const isEnactmentDeadlineAtMinimum = doesValueEquateToParam(
|
||||
fields.proposalEnactmentDeadline,
|
||||
params.governance_proposal_market_minEnact
|
||||
);
|
||||
const isEnactmentDeadlineAtMaximum = doesValueEquateToParam(
|
||||
fields.proposalEnactmentDeadline,
|
||||
params.governance_proposal_market_maxEnact
|
||||
);
|
||||
|
||||
await submit({
|
||||
rationale: {
|
||||
@ -80,12 +91,13 @@ export const ProposeNewMarket = () => {
|
||||
},
|
||||
closingTimestamp: getClosingTimestamp(
|
||||
fields.proposalVoteDeadline,
|
||||
isVoteDeadlineAtMinimum
|
||||
isVoteDeadlineAtMinimum,
|
||||
isVoteDeadlineAtMaximum
|
||||
),
|
||||
enactmentTimestamp: getEnactmentTimestamp(
|
||||
fields.proposalVoteDeadline,
|
||||
fields.proposalEnactmentDeadline,
|
||||
isVoteDeadlineAtMinimum
|
||||
isEnactmentDeadlineAtMinimum,
|
||||
isEnactmentDeadlineAtMaximum
|
||||
),
|
||||
},
|
||||
});
|
||||
|
@ -4,7 +4,7 @@ import {
|
||||
getClosingTimestamp,
|
||||
getEnactmentTimestamp,
|
||||
useProposalSubmit,
|
||||
deadlineToRoundedHours,
|
||||
doesValueEquateToParam,
|
||||
} from '@vegaprotocol/governance';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import {
|
||||
@ -63,11 +63,22 @@ export const ProposeUpdateAsset = () => {
|
||||
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||
|
||||
const onSubmit = async (fields: UpdateAssetProposalFormFields) => {
|
||||
const isVoteDeadlineAtMinimum =
|
||||
fields.proposalVoteDeadline ===
|
||||
deadlineToRoundedHours(
|
||||
const isVoteDeadlineAtMinimum = doesValueEquateToParam(
|
||||
fields.proposalVoteDeadline,
|
||||
params.governance_proposal_updateAsset_minClose
|
||||
).toString();
|
||||
);
|
||||
const isVoteDeadlineAtMaximum = doesValueEquateToParam(
|
||||
fields.proposalVoteDeadline,
|
||||
params.governance_proposal_updateAsset_maxClose
|
||||
);
|
||||
const isEnactmentDeadlineAtMinimum = doesValueEquateToParam(
|
||||
fields.proposalEnactmentDeadline,
|
||||
params.governance_proposal_updateAsset_minEnact
|
||||
);
|
||||
const isEnactmentDeadlineAtMaximum = doesValueEquateToParam(
|
||||
fields.proposalEnactmentDeadline,
|
||||
params.governance_proposal_updateAsset_maxEnact
|
||||
);
|
||||
|
||||
await submit({
|
||||
rationale: {
|
||||
@ -80,12 +91,13 @@ export const ProposeUpdateAsset = () => {
|
||||
},
|
||||
closingTimestamp: getClosingTimestamp(
|
||||
fields.proposalVoteDeadline,
|
||||
isVoteDeadlineAtMinimum
|
||||
isVoteDeadlineAtMinimum,
|
||||
isVoteDeadlineAtMaximum
|
||||
),
|
||||
enactmentTimestamp: getEnactmentTimestamp(
|
||||
fields.proposalVoteDeadline,
|
||||
fields.proposalEnactmentDeadline,
|
||||
isVoteDeadlineAtMinimum
|
||||
isEnactmentDeadlineAtMinimum,
|
||||
isEnactmentDeadlineAtMaximum
|
||||
),
|
||||
},
|
||||
});
|
||||
@ -171,6 +183,7 @@ export const ProposeUpdateAsset = () => {
|
||||
voteErrorMessage={errors?.proposalVoteDeadline?.message}
|
||||
voteMinClose={params.governance_proposal_updateAsset_minClose}
|
||||
voteMaxClose={params.governance_proposal_updateAsset_maxClose}
|
||||
onEnactMinMax={setValue}
|
||||
enactmentRegister={register('proposalEnactmentDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
|
@ -6,7 +6,7 @@ import {
|
||||
getClosingTimestamp,
|
||||
getEnactmentTimestamp,
|
||||
useProposalSubmit,
|
||||
deadlineToRoundedHours,
|
||||
doesValueEquateToParam,
|
||||
} from '@vegaprotocol/governance';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import {
|
||||
@ -123,11 +123,22 @@ export const ProposeUpdateMarket = () => {
|
||||
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||
|
||||
const onSubmit = async (fields: UpdateMarketProposalFormFields) => {
|
||||
const isVoteDeadlineAtMinimum =
|
||||
fields.proposalVoteDeadline ===
|
||||
deadlineToRoundedHours(
|
||||
const isVoteDeadlineAtMinimum = doesValueEquateToParam(
|
||||
fields.proposalVoteDeadline,
|
||||
params.governance_proposal_updateMarket_minClose
|
||||
).toString();
|
||||
);
|
||||
const isVoteDeadlineAtMaximum = doesValueEquateToParam(
|
||||
fields.proposalVoteDeadline,
|
||||
params.governance_proposal_updateMarket_maxClose
|
||||
);
|
||||
const isEnactmentDeadlineAtMinimum = doesValueEquateToParam(
|
||||
fields.proposalEnactmentDeadline,
|
||||
params.governance_proposal_updateMarket_minEnact
|
||||
);
|
||||
const isEnactmentDeadlineAtMaximum = doesValueEquateToParam(
|
||||
fields.proposalEnactmentDeadline,
|
||||
params.governance_proposal_updateMarket_maxEnact
|
||||
);
|
||||
|
||||
await submit({
|
||||
rationale: {
|
||||
@ -143,12 +154,13 @@ export const ProposeUpdateMarket = () => {
|
||||
},
|
||||
closingTimestamp: getClosingTimestamp(
|
||||
fields.proposalVoteDeadline,
|
||||
isVoteDeadlineAtMinimum
|
||||
isVoteDeadlineAtMinimum,
|
||||
isVoteDeadlineAtMaximum
|
||||
),
|
||||
enactmentTimestamp: getEnactmentTimestamp(
|
||||
fields.proposalVoteDeadline,
|
||||
fields.proposalEnactmentDeadline,
|
||||
isVoteDeadlineAtMinimum
|
||||
isEnactmentDeadlineAtMinimum,
|
||||
isEnactmentDeadlineAtMaximum
|
||||
),
|
||||
},
|
||||
});
|
||||
|
@ -1,4 +1,9 @@
|
||||
import { deadlineToSeconds, secondsToRoundedHours } from './deadline-helpers';
|
||||
import {
|
||||
deadlineToSeconds,
|
||||
secondsToRoundedHours,
|
||||
addTwoMinutes,
|
||||
subtractTwoSeconds,
|
||||
} from './deadline-helpers';
|
||||
|
||||
describe('deadlineToSeconds', () => {
|
||||
it('should throw an error if the deadline does not match the format "XhXmXs"', () => {
|
||||
@ -39,3 +44,37 @@ describe('secondsToRoundedHours', () => {
|
||||
expect(secondsToRoundedHours(9000)).toEqual(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('addTwoMinutes', () => {
|
||||
it('should add two minutes to the current time', () => {
|
||||
const now = new Date();
|
||||
const twoMinutesLater = new Date(now.getTime() + 2 * 60 * 1000);
|
||||
expect(addTwoMinutes(now)).toEqual(twoMinutesLater);
|
||||
});
|
||||
|
||||
it('will use the current time if no date is provided', () => {
|
||||
const now = new Date();
|
||||
const twoMinutesLater = new Date(now.getTime() + 2 * 60 * 1000);
|
||||
expect(addTwoMinutes()).toEqual(twoMinutesLater);
|
||||
});
|
||||
|
||||
it('should add two minutes to a given time', () => {
|
||||
const date = new Date(2020, 0, 1);
|
||||
const twoMinutesLater = new Date(date.getTime() + 2 * 60 * 1000);
|
||||
expect(addTwoMinutes(date)).toEqual(twoMinutesLater);
|
||||
});
|
||||
});
|
||||
|
||||
describe('subtractTwoSeconds', () => {
|
||||
it('should subtract two seconds to the current time', () => {
|
||||
const now = new Date();
|
||||
const twoSecondsEarlier = new Date(now.getTime() - 2 * 1000);
|
||||
expect(subtractTwoSeconds(now)).toEqual(twoSecondsEarlier);
|
||||
});
|
||||
|
||||
it('should subtract two seconds from a given time', () => {
|
||||
const date = new Date(2020, 0, 1);
|
||||
const twoSecondsEarlier = new Date(date.getTime() - 2 * 1000);
|
||||
expect(subtractTwoSeconds(date)).toEqual(twoSecondsEarlier);
|
||||
});
|
||||
});
|
||||
|
@ -1,10 +1,15 @@
|
||||
import { parse as ISO8601Parse, toSeconds } from 'iso8601-duration';
|
||||
import { addMinutes, subSeconds } from 'date-fns';
|
||||
|
||||
const deadlineRegexChecker = (deadline: string) => {
|
||||
// check that the deadline string matches the format "XhXmXs"
|
||||
const regex = /^(\d+h)?(\d+m)?(\d+s)?$/;
|
||||
return regex.test(deadline);
|
||||
};
|
||||
|
||||
// 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)) {
|
||||
if (!deadlineRegexChecker(deadline)) {
|
||||
throw new Error(
|
||||
`Invalid deadline format, expected format "XhXmXs", got "${deadline}"`
|
||||
);
|
||||
@ -20,3 +25,11 @@ export const secondsToRoundedHours = (seconds: number) => {
|
||||
|
||||
export const deadlineToRoundedHours = (deadline: string) =>
|
||||
secondsToRoundedHours(deadlineToSeconds(deadline));
|
||||
|
||||
export const doesValueEquateToParam = (value: string, param: string) =>
|
||||
value === deadlineToRoundedHours(param).toString();
|
||||
|
||||
export const addTwoMinutes = (date?: Date) => addMinutes(date || new Date(), 2);
|
||||
|
||||
export const subtractTwoSeconds = (date?: Date) =>
|
||||
subSeconds(date || new Date(), 2);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { getClosingTimestamp } from './get-closing-timestamp';
|
||||
import { addHours, addMinutes, getTime } from 'date-fns';
|
||||
import { addHours, addMinutes, getTime, subSeconds } from 'date-fns';
|
||||
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
@ -14,6 +14,7 @@ describe('getClosingTimestamp', () => {
|
||||
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 isMaximumDeadlineSelected = false;
|
||||
const expected = Math.floor(
|
||||
getTime(
|
||||
addHours(addMinutes(new Date(), 2), Number(proposalVoteDeadline))
|
||||
@ -21,20 +22,40 @@ describe('getClosingTimestamp', () => {
|
||||
);
|
||||
const actual = getClosingTimestamp(
|
||||
proposalVoteDeadline,
|
||||
isMinimumDeadlineSelected
|
||||
isMinimumDeadlineSelected,
|
||||
isMaximumDeadlineSelected
|
||||
);
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should return the correct timestamp if the proposalVoteDeadline is not set to minimum (when no extra mins are added)', () => {
|
||||
it('should return the correct timestamp if the proposalVoteDeadline is not set to minimum or maximum (no extra time added or subtracted)', () => {
|
||||
const proposalVoteDeadline = '2';
|
||||
const isMinimumDeadlineSelected = false;
|
||||
const isMaximumDeadlineSelected = false;
|
||||
const expected = Math.floor(
|
||||
getTime(addHours(new Date(), Number(proposalVoteDeadline))) / 1000
|
||||
);
|
||||
const actual = getClosingTimestamp(
|
||||
proposalVoteDeadline,
|
||||
isMinimumDeadlineSelected
|
||||
isMinimumDeadlineSelected,
|
||||
isMaximumDeadlineSelected
|
||||
);
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should return the correct timestamp if the proposalVoteDeadline is set to maximum (when 2 secs are subtracted)', () => {
|
||||
const proposalVoteDeadline = '3';
|
||||
const isMinimumDeadlineSelected = false;
|
||||
const isMaximumDeadlineSelected = true;
|
||||
const expected = Math.floor(
|
||||
getTime(
|
||||
addHours(subSeconds(new Date(), 2), Number(proposalVoteDeadline))
|
||||
) / 1000
|
||||
);
|
||||
const actual = getClosingTimestamp(
|
||||
proposalVoteDeadline,
|
||||
isMinimumDeadlineSelected,
|
||||
isMaximumDeadlineSelected
|
||||
);
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
@ -1,17 +1,25 @@
|
||||
import { addHours, addMinutes, getTime } from 'date-fns';
|
||||
import { addHours, getTime } from 'date-fns';
|
||||
import { addTwoMinutes, subtractTwoSeconds } from './deadline-helpers';
|
||||
|
||||
// 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.
|
||||
// If the vote deadline 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.
|
||||
|
||||
// If it's at its maximum, remove a couple of seconds to ensure rounding errors
|
||||
// and communication delays don't cause the deadline to be slightly
|
||||
// later than the API can accept.
|
||||
|
||||
export const getClosingTimestamp = (
|
||||
proposalVoteDeadline: string,
|
||||
minimumDeadlineSelected: boolean
|
||||
minimumDeadlineSelected: boolean,
|
||||
maximumDeadlineSelected: boolean
|
||||
) =>
|
||||
Math.floor(
|
||||
getTime(
|
||||
minimumDeadlineSelected
|
||||
? addHours(addMinutes(new Date(), 2), Number(proposalVoteDeadline))
|
||||
: addHours(new Date(), Number(proposalVoteDeadline))
|
||||
addHours(
|
||||
(minimumDeadlineSelected && addTwoMinutes()) ||
|
||||
(maximumDeadlineSelected && subtractTwoSeconds()) ||
|
||||
new Date(),
|
||||
Number(proposalVoteDeadline)
|
||||
)
|
||||
) / 1000
|
||||
);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { getEnactmentTimestamp } from './get-enactment-timestamp';
|
||||
import { addHours, getTime } from 'date-fns';
|
||||
import { addHours, addMinutes, getTime, subSeconds } from 'date-fns';
|
||||
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
@ -12,21 +12,48 @@ afterEach(() => {
|
||||
|
||||
describe('getEnactmentTimestamp', () => {
|
||||
it('should return the correct timestamp', () => {
|
||||
const proposalVoteDeadline = '2';
|
||||
const isMinimumVoteDeadlineSelected = false;
|
||||
const isMaximumVoteDeadlineSelected = false;
|
||||
const enactmentDeadline = '1';
|
||||
const expected = Math.floor(
|
||||
getTime(
|
||||
addHours(
|
||||
new Date(),
|
||||
Number(proposalVoteDeadline) + Number(enactmentDeadline)
|
||||
)
|
||||
) / 1000
|
||||
getTime(addHours(new Date(), Number(enactmentDeadline))) / 1000
|
||||
);
|
||||
const actual = getEnactmentTimestamp(
|
||||
proposalVoteDeadline,
|
||||
enactmentDeadline,
|
||||
isMinimumVoteDeadlineSelected
|
||||
isMinimumVoteDeadlineSelected,
|
||||
isMaximumVoteDeadlineSelected
|
||||
);
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should return the correct timestamp when minimum vote deadline is selected', () => {
|
||||
const isMinimumVoteDeadlineSelected = true;
|
||||
const isMaximumVoteDeadlineSelected = false;
|
||||
const enactmentDeadline = '1';
|
||||
const expected = Math.floor(
|
||||
getTime(addMinutes(addHours(new Date(), Number(enactmentDeadline)), 2)) /
|
||||
1000
|
||||
);
|
||||
const actual = getEnactmentTimestamp(
|
||||
enactmentDeadline,
|
||||
isMinimumVoteDeadlineSelected,
|
||||
isMaximumVoteDeadlineSelected
|
||||
);
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should return the correct timestamp when maximum vote deadline is selected', () => {
|
||||
const isMinimumVoteDeadlineSelected = false;
|
||||
const isMaximumVoteDeadlineSelected = true;
|
||||
const enactmentDeadline = '1';
|
||||
const expected = Math.floor(
|
||||
getTime(subSeconds(addHours(new Date(), Number(enactmentDeadline)), 2)) /
|
||||
1000
|
||||
);
|
||||
const actual = getEnactmentTimestamp(
|
||||
enactmentDeadline,
|
||||
isMinimumVoteDeadlineSelected,
|
||||
isMaximumVoteDeadlineSelected
|
||||
);
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
@ -1,22 +1,24 @@
|
||||
import { addHours, fromUnixTime, getTime } from 'date-fns';
|
||||
import { getClosingTimestamp } from './get-closing-timestamp';
|
||||
import { addHours, getTime } from 'date-fns';
|
||||
import { addTwoMinutes, subtractTwoSeconds } from './deadline-helpers';
|
||||
|
||||
// If the enactment deadline 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.
|
||||
|
||||
// If it's at its maximum, remove a couple of seconds to ensure rounding errors
|
||||
// and communication delays don't cause the deadline to be slightly
|
||||
// later than the API can accept.
|
||||
|
||||
export const getEnactmentTimestamp = (
|
||||
proposalVoteDeadline: string,
|
||||
enactmentDeadline: string,
|
||||
minimumVoteDeadlineSelected: boolean
|
||||
minimumDeadlineSelected: boolean,
|
||||
maximumDeadlineSelected: boolean
|
||||
) =>
|
||||
Math.floor(
|
||||
getTime(
|
||||
addHours(
|
||||
new Date(
|
||||
fromUnixTime(
|
||||
getClosingTimestamp(
|
||||
proposalVoteDeadline,
|
||||
minimumVoteDeadlineSelected
|
||||
)
|
||||
)
|
||||
),
|
||||
(minimumDeadlineSelected && addTwoMinutes()) ||
|
||||
(maximumDeadlineSelected && subtractTwoSeconds()) ||
|
||||
new Date(),
|
||||
Number(enactmentDeadline)
|
||||
)
|
||||
) / 1000
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { addHours, addMinutes, getTime } from 'date-fns';
|
||||
import { addHours, addMinutes, getTime, subSeconds } from 'date-fns';
|
||||
import { getValidationTimestamp } from './get-validation-timestamp';
|
||||
|
||||
beforeEach(() => {
|
||||
@ -13,21 +13,44 @@ afterEach(() => {
|
||||
describe('getValidationTimestamp', () => {
|
||||
it('should return the correct timestamp if the proposalValidationDeadline is 0 (when 2 mins are added)', () => {
|
||||
const proposalValidationDeadline = '0';
|
||||
const isMaximumDeadlineSelected = false;
|
||||
const expected = Math.floor(
|
||||
getTime(
|
||||
addHours(addMinutes(new Date(), 2), Number(proposalValidationDeadline))
|
||||
) / 1000
|
||||
);
|
||||
const actual = getValidationTimestamp(proposalValidationDeadline);
|
||||
const actual = getValidationTimestamp(
|
||||
proposalValidationDeadline,
|
||||
isMaximumDeadlineSelected
|
||||
);
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should return the correct timestamp if the proposalValidationDeadline is 1 (when no extra mins are added)', () => {
|
||||
it('should return the correct timestamp if the proposalValidationDeadline is neither maximum nor minimum (when no extra mins are added)', () => {
|
||||
const proposalValidationDeadline = '1';
|
||||
const isMaximumDeadlineSelected = false;
|
||||
const expected = Math.floor(
|
||||
getTime(addHours(new Date(), Number(proposalValidationDeadline))) / 1000
|
||||
);
|
||||
const actual = getValidationTimestamp(proposalValidationDeadline);
|
||||
const actual = getValidationTimestamp(
|
||||
proposalValidationDeadline,
|
||||
isMaximumDeadlineSelected
|
||||
);
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should return the correct timestamp if the proposalValidationDeadline is maximum (when 2 secs are subtracted)', () => {
|
||||
const proposalValidationDeadline = '2';
|
||||
const isMaximumDeadlineSelected = true;
|
||||
const expected = Math.floor(
|
||||
getTime(
|
||||
addHours(subSeconds(new Date(), 2), Number(proposalValidationDeadline))
|
||||
) / 1000
|
||||
);
|
||||
const actual = getValidationTimestamp(
|
||||
proposalValidationDeadline,
|
||||
isMaximumDeadlineSelected
|
||||
);
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
@ -1,17 +1,25 @@
|
||||
import { addHours, addMinutes, getTime } from 'date-fns';
|
||||
import { addHours, getTime } from 'date-fns';
|
||||
import { addTwoMinutes, subtractTwoSeconds } from './deadline-helpers';
|
||||
|
||||
// If proposalValidationDeadline is at its minimum of 0 hours, then we add
|
||||
// 2 extra minutes to the validation timestamp to ensure that there's time
|
||||
// to confirm in the wallet.
|
||||
|
||||
export const getValidationTimestamp = (proposalValidationDeadline: string) =>
|
||||
// If it's at its maximum, remove a couple of seconds to ensure rounding errors
|
||||
// and communication delays don't cause the proposal deadline to be slightly
|
||||
// later than the API can accept.
|
||||
|
||||
export const getValidationTimestamp = (
|
||||
proposalValidationDeadline: string,
|
||||
maximumDeadlineSelected: boolean
|
||||
) =>
|
||||
Math.floor(
|
||||
getTime(
|
||||
proposalValidationDeadline === '0'
|
||||
? addHours(
|
||||
addMinutes(new Date(), 2),
|
||||
addHours(
|
||||
(proposalValidationDeadline === '0' && addTwoMinutes()) ||
|
||||
(maximumDeadlineSelected && subtractTwoSeconds()) ||
|
||||
new Date(),
|
||||
Number(proposalValidationDeadline)
|
||||
)
|
||||
: addHours(new Date(), Number(proposalValidationDeadline))
|
||||
) / 1000
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user