Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create use thresholds hook and use correct thresholds for reject origin #4588

Closed
wants to merge 12 commits into from
52 changes: 25 additions & 27 deletions packages/apps-config/src/api/params/proposalThresholds.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,36 @@
// Copyright 2017-2021 @polkadot/app-config authors & contributors
// SPDX-License-Identifier: Apache-2.0

import type { ApiPromise } from '@polkadot/api';

import { KULUPU_GENESIS, KUSAMA_GENESIS, POLKADOT_GENESIS } from '../constants';

const PROPOSE_THRESHOLDS: Record<string, number> = {
[KULUPU_GENESIS]: 1,
[KUSAMA_GENESIS]: 0.5,
[POLKADOT_GENESIS]: 0.6,
default: 0.5
};
export interface Threshold {
value: number;
option: 'AtLeast' | 'MoreThan';

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does it make sense to create an enum for these options? for simplifying further maintaining and exclude typos

}

const SLASH_THRESHOLDS: Record<string, number> = {
[KUSAMA_GENESIS]: 0.5,
[POLKADOT_GENESIS]: 0.75,
default: 0.5
export const PROPOSE_THRESHOLDS: Record<string, Threshold> = {
[KULUPU_GENESIS]: { option: 'MoreThan', value: 0.8 },
[KUSAMA_GENESIS]: { option: 'AtLeast', value: 0.5 },
[POLKADOT_GENESIS]: { option: 'AtLeast', value: 0.6 },
default: { option: 'AtLeast', value: 0.5 }
};

const TREASURY_THRESHOLDS: Record<string, number> = {
[KULUPU_GENESIS]: 0.5,
[KUSAMA_GENESIS]: 0.6,
[POLKADOT_GENESIS]: 0.6,
default: 0.6
export const REJECT_THRESHOLDS: Record<string, Threshold> = {
[KULUPU_GENESIS]: { option: 'MoreThan', value: 0.5 },
[KUSAMA_GENESIS]: { option: 'MoreThan', value: 0.5 },
[POLKADOT_GENESIS]: { option: 'MoreThan', value: 0.5 },
default: { option: 'MoreThan', value: 0.5 }
};

export function getProposalThreshold (api: ApiPromise): number {
return PROPOSE_THRESHOLDS[api.genesisHash.toHex()] || PROPOSE_THRESHOLDS.default;
}

export function getSlashProposalThreshold (api: ApiPromise): number {
return SLASH_THRESHOLDS[api.genesisHash.toHex()] || SLASH_THRESHOLDS.default;
}
export const SLASH_THRESHOLDS: Record<string, Threshold> = {
[KUSAMA_GENESIS]: { option: 'AtLeast', value: 0.5 },
[POLKADOT_GENESIS]: { option: 'AtLeast', value: 0.75 },
default: { option: 'AtLeast', value: 0.5 }
};

export function getTreasuryProposalThreshold (api: ApiPromise): number {
return TREASURY_THRESHOLDS[api.genesisHash.toHex()] || TREASURY_THRESHOLDS.default;
}
export const TREASURY_THRESHOLDS: Record<string, Threshold> = {
[KULUPU_GENESIS]: { option: 'MoreThan', value: 0.5 },
[KUSAMA_GENESIS]: { option: 'AtLeast', value: 0.6 },
[POLKADOT_GENESIS]: { option: 'AtLeast', value: 0.6 },
default: { option: 'AtLeast', value: 0.6 }
};
27 changes: 8 additions & 19 deletions packages/page-bounties/src/BountyActions/BountyInitiateVoting.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@
import type { DeriveCollectiveProposal } from '@polkadot/api-derive/types';
import type { BountyIndex } from '@polkadot/types/interfaces';

import BN from 'bn.js';
import React, { useEffect, useMemo, useRef, useState } from 'react';
import React, { useMemo, useRef, useState } from 'react';

import { getTreasuryProposalThreshold } from '@polkadot/apps-config';
import { Button, InputAddress, Modal, TxButton } from '@polkadot/react-components';
import { useApi, useMembers, useToggle } from '@polkadot/react-hooks';
import { useThresholds } from '@polkadot/react-hooks/useThresholds';

import { useBounties } from '../hooks';
import { useTranslation } from '../translate';
Expand All @@ -28,13 +27,7 @@ function BountyInitiateVoting ({ index, proposals }: Props): React.ReactElement<
const { approveBounty, closeBounty } = useBounties();
const [isOpen, toggleOpen] = useToggle();
const [accountId, setAccountId] = useState<string | null>(null);
const [threshold, setThreshold] = useState<BN>();

useEffect((): void => {
members && setThreshold(
new BN(Math.ceil(members.length * getTreasuryProposalThreshold(api)))
);
}, [api, members]);
const { treasuryProposalThreshold } = useThresholds();

const approveBountyProposal = useRef(approveBounty(index));
const closeBountyProposal = useRef(closeBounty(index));
Expand All @@ -56,13 +49,9 @@ function BountyInitiateVoting ({ index, proposals }: Props): React.ReactElement<
size='large'
>
<Modal.Content>
<Modal.Columns>
<Modal.Column>
<p>{t<string>('This action will create a Council motion to either approve or reject the Bounty.')}</p>
</Modal.Column>
<Modal.Column>
</Modal.Column>
</Modal.Columns>
<Modal.Column>
<p>{t<string>('This action will create a Council motion to either approve or reject the Bounty.')}</p>
</Modal.Column>
<Modal.Columns>
<Modal.Column>
<InputAddress
Expand All @@ -86,7 +75,7 @@ function BountyInitiateVoting ({ index, proposals }: Props): React.ReactElement<
isDisabled={false}
label={t<string>('Approve')}
onStart={toggleOpen}
params={[threshold, approveBountyProposal.current, approveBountyProposal.current.length]}
params={[treasuryProposalThreshold, approveBountyProposal.current, approveBountyProposal.current.length]}
tx={api.tx.council.propose}
/>
<TxButton
Expand All @@ -95,7 +84,7 @@ function BountyInitiateVoting ({ index, proposals }: Props): React.ReactElement<
isDisabled={false}
label={t<string>('Reject')}
onStart={toggleOpen}
params={[threshold, closeBountyProposal.current, closeBountyProposal.current.length]}
params={[treasuryProposalThreshold, closeBountyProposal.current, closeBountyProposal.current.length]}
tx={api.tx.council.propose}
/>
</Modal.Actions>
Expand Down
17 changes: 5 additions & 12 deletions packages/page-bounties/src/BountyActions/CloseBounty.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@

import type { BountyIndex } from '@polkadot/types/interfaces';

import BN from 'bn.js';
import React, { useEffect, useRef, useState } from 'react';
import React, { useRef, useState } from 'react';

import { getTreasuryProposalThreshold } from '@polkadot/apps-config';
import { InputAddress, Modal, TxButton } from '@polkadot/react-components';
import { useApi, useMembers } from '@polkadot/react-hooks';
import { useApi, useMembers, useThresholds } from '@polkadot/react-hooks';

import { useBounties } from '../hooks';
import { useTranslation } from '../translate';
Expand All @@ -23,14 +21,9 @@ function CloseBounty ({ index, toggleOpen }: Props): React.ReactElement<Props> |
const { api } = useApi();
const { members } = useMembers();
const { closeBounty } = useBounties();
const [accountId, setAccountId] = useState<string | null>(null);
const [threshold, setThreshold] = useState<BN>();
const { treasuryRejectionThreshold } = useThresholds();

useEffect((): void => {
members && setThreshold(
new BN(Math.ceil(members.length * getTreasuryProposalThreshold(api)))
);
}, [api, members]);
const [accountId, setAccountId] = useState<string | null>(null);

const closeBountyProposal = useRef(closeBounty(index));

Expand Down Expand Up @@ -66,7 +59,7 @@ function CloseBounty ({ index, toggleOpen }: Props): React.ReactElement<Props> |
isDisabled={false}
label={t<string>('Close Bounty')}
onStart={toggleOpen}
params={[threshold, closeBountyProposal.current, closeBountyProposal.current.length]}
params={[treasuryRejectionThreshold, closeBountyProposal.current, closeBountyProposal.current.length]}
tx={api.tx.council.propose}
/>
</Modal.Actions>
Expand Down
14 changes: 4 additions & 10 deletions packages/page-bounties/src/BountyActions/ProposeCuratorAction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ import BN from 'bn.js';
import React, { useEffect, useMemo, useState } from 'react';

import { truncateTitle } from '@polkadot/app-bounties/helpers';
import { getTreasuryProposalThreshold } from '@polkadot/apps-config';
import { Button, InputAddress, InputBalance, MarkError, Modal, TxButton } from '@polkadot/react-components';
import { useApi, useMembers, useToggle } from '@polkadot/react-hooks';
import { useApi, useMembers, useThresholds, useToggle } from '@polkadot/react-hooks';
import { BN_ZERO } from '@polkadot/util';

import { useBounties } from '../hooks';
Expand All @@ -30,19 +29,14 @@ function ProposeCuratorAction ({ description, index, proposals, value }: Props):
const { api } = useApi();
const { isMember, members } = useMembers();
const { proposeCurator } = useBounties();
const { treasuryProposalThreshold } = useThresholds();

const [isOpen, toggleOpen] = useToggle();
const [accountId, setAccountId] = useState<string | null>(null);
const [curatorId, setCuratorId] = useState<string | null>(null);
const [threshold, setThreshold] = useState<BN>();
const [fee, setFee] = useState<BN>(BN_ZERO);
const [isFeeValid, setIsFeeValid] = useState(false);

useEffect((): void => {
members && setThreshold(
new BN(Math.ceil(members.length * getTreasuryProposalThreshold(api)))
);
}, [api, members]);

const proposeCuratorProposal = useMemo(() => curatorId && proposeCurator(index, curatorId, fee), [curatorId, fee, index, proposeCurator]);

const isVotingInitiated = useMemo(() => proposals?.filter(({ proposal }) => BOUNTY_METHODS.includes(proposal.method)).length !== 0, [proposals]);
Expand Down Expand Up @@ -123,7 +117,7 @@ function ProposeCuratorAction ({ description, index, proposals, value }: Props):
isDisabled={!isFeeValid}
label={t<string>('Assign curator')}
onStart={toggleOpen}
params={[threshold, proposeCuratorProposal, proposeCuratorProposal?.length]}
params={[treasuryProposalThreshold, proposeCuratorProposal, proposeCuratorProposal?.length]}
tx={api.tx.council.propose}
/>
</Modal.Actions>
Expand Down
20 changes: 6 additions & 14 deletions packages/page-bounties/src/BountyActions/SlashCurator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@

import type { AccountId, BountyIndex } from '@polkadot/types/interfaces';

import BN from 'bn.js';
import React, { useEffect, useMemo, useState } from 'react';
import React, { useMemo, useState } from 'react';

import { SubmittableExtrinsic } from '@polkadot/api/types';
import { getTreasuryProposalThreshold } from '@polkadot/apps-config';
import { InputAddress, Modal, TxButton } from '@polkadot/react-components';
import { useAccounts, useApi, useMembers } from '@polkadot/react-hooks';
import { useAccounts, useApi, useMembers, useThresholds } from '@polkadot/react-hooks';

import { truncateTitle } from '../helpers';
import { useBounties } from '../hooks';
Expand Down Expand Up @@ -41,15 +39,9 @@ function SlashCurator ({ action, curatorId, description, index, toggleOpen }: Pr
const { members } = useMembers();
const { unassignCurator } = useBounties();
const [accountId, setAccountId] = useState<string | null>(null);
const [threshold, setThreshold] = useState<BN>();
const { treasuryRejectionThreshold } = useThresholds();
const { allAccounts } = useAccounts();

useEffect((): void => {
members && setThreshold(
new BN(Math.ceil(members.length * getTreasuryProposalThreshold(api)))
);
}, [api, members]);

const unassignCuratorProposal = useMemo(() => unassignCurator(index), [index, unassignCurator]);

const actionProperties = useMemo<Record<ValidUnassignCuratorAction, ActionProperties>>(() => ({
Expand Down Expand Up @@ -77,7 +69,7 @@ function SlashCurator ({ action, curatorId, description, index, toggleOpen }: Pr
filter: members,
header: t('This action will create a Council motion to slash the Curator.'),
helpMessage: t('The Curator that will be slashed.'),
params: [threshold, unassignCuratorProposal, unassignCuratorProposal?.length],
params: [treasuryRejectionThreshold, unassignCuratorProposal, unassignCuratorProposal?.length],
proposingAccountTip: t('The council member that will create the motion, submission equates to an "aye" vote.'),
tip: t("If the motion is outvoted, Curator's deposit will be slashed and Curator will be unassigned. Bounty will return to the Funded state."),
title: t('Slash curator'),
Expand All @@ -87,13 +79,13 @@ function SlashCurator ({ action, curatorId, description, index, toggleOpen }: Pr
filter: members,
header: t('This action will create a Council motion to unassign the Curator.'),
helpMessage: t('The Curator that will be unassigned'),
params: [threshold, unassignCuratorProposal, unassignCuratorProposal?.length],
params: [treasuryRejectionThreshold, unassignCuratorProposal, unassignCuratorProposal?.length],
proposingAccountTip: t('The council member that will create the motion, submission equates to an "aye" vote.'),
tip: t('If the motion is outvoted, the current Curator will be unassigned and the Bounty will return to the Funded state.'),
title: t('Unassign curator'),
tx: api.tx.council.propose
}
}), [t, curatorId, index, unassignCurator, api.tx.council.propose, allAccounts, members, threshold, unassignCuratorProposal]);
}), [t, curatorId, index, unassignCurator, api.tx.council.propose, allAccounts, members, treasuryRejectionThreshold, unassignCuratorProposal]);

const { filter, header, helpMessage, params, proposingAccountTip, tip, title, tx } = actionProperties[action];

Expand Down
13 changes: 6 additions & 7 deletions packages/page-council/src/Motions/ProposeExternal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@ import type { SubmittableExtrinsic } from '@polkadot/api/types';

import React, { useCallback, useEffect, useState } from 'react';

import { getProposalThreshold } from '@polkadot/apps-config';
import { Button, Input, InputAddress, Modal, TxButton } from '@polkadot/react-components';
import { useApi, useToggle } from '@polkadot/react-hooks';
import { useApi, useThresholds, useToggle } from '@polkadot/react-hooks';
import { isHex } from '@polkadot/util';

import { useTranslation } from '../translate';
Expand All @@ -31,13 +30,13 @@ interface ProposalState {
function ProposeExternal ({ className = '', isMember, members }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const { proposalThreshold } = useThresholds();

const [isVisible, toggleVisible] = useToggle();
const [accountId, setAcountId] = useState<string | null>(null);
const [{ proposal, proposalLength }, setProposal] = useState<ProposalState>({ proposalLength: 0 });
const [{ hash, isHashValid }, setHash] = useState<HashState>({ hash: '', isHashValid: false });

const threshold = Math.ceil((members.length || 0) * getProposalThreshold(api));

const _onChangeHash = useCallback(
(hash?: string): void => setHash({ hash, isHashValid: isHex(hash, 256) }),
[]
Expand Down Expand Up @@ -108,13 +107,13 @@ function ProposeExternal ({ className = '', isMember, members }: Props): React.R
<TxButton
accountId={accountId}
icon='plus'
isDisabled={!threshold || !members.includes(accountId || '') || !proposal}
isDisabled={!proposalThreshold || !members.includes(accountId || '') || !proposal}
label={t<string>('Propose')}
onStart={toggleVisible}
params={
api.tx.council.propose.meta.args.length === 3
? [threshold, proposal, proposalLength]
: [threshold, proposal]
? [proposalThreshold, proposal, proposalLength]
: [proposalThreshold, proposal]
}
tx={api.tx.council.propose}
/>
Expand Down
9 changes: 4 additions & 5 deletions packages/page-council/src/Motions/ProposeMotion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@ import type { SubmittableExtrinsic } from '@polkadot/api/types';
import BN from 'bn.js';
import React, { useCallback, useEffect, useState } from 'react';

import { getProposalThreshold } from '@polkadot/apps-config';
import { Button, Extrinsic, InputAddress, InputNumber, Modal, TxButton } from '@polkadot/react-components';
import { useApi, useToggle } from '@polkadot/react-hooks';
import { useApi, useThresholds, useToggle } from '@polkadot/react-hooks';
import { BN_ZERO } from '@polkadot/util';

import { useTranslation } from '../translate';
Expand All @@ -31,6 +30,7 @@ interface ProposalState {
function Propose ({ isMember, members }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api, apiDefaultTxSudo } = useApi();
const { proposalThreshold } = useThresholds();
const [isOpen, toggleOpen] = useToggle();
const [accountId, setAcountId] = useState<string | null>(null);
const [{ proposal, proposalLength }, setProposal] = useState<ProposalState>({ proposalLength: 0 });
Expand All @@ -39,10 +39,9 @@ function Propose ({ isMember, members }: Props): React.ReactElement<Props> {
useEffect((): void => {
members && setThreshold({
isThresholdValid: members.length !== 0,
threshold: new BN(Math.ceil(members.length * getProposalThreshold(api)))
threshold: new BN(proposalThreshold)
});
}, [api, members]);

}, [api, members, proposalThreshold]);
const _setMethod = useCallback(
(proposal?: SubmittableExtrinsic<'promise'> | null) => setProposal({
proposal,
Expand Down
12 changes: 5 additions & 7 deletions packages/page-council/src/Motions/Slashing.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@ import type { SubmittableExtrinsic } from '@polkadot/api/types';

import React, { useEffect, useMemo, useState } from 'react';

import { getSlashProposalThreshold } from '@polkadot/apps-config';
import { Button, Dropdown, Input, InputAddress, Modal, TxButton } from '@polkadot/react-components';
import { useApi, useAvailableSlashes, useToggle } from '@polkadot/react-hooks';
import { useApi, useAvailableSlashes, useThresholds, useToggle } from '@polkadot/react-hooks';

import { useTranslation } from '../translate';

Expand All @@ -30,14 +29,13 @@ interface ProposalState {
function Slashing ({ className = '', isMember, members }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const { slashProposalThreshold } = useThresholds();
const slashes = useAvailableSlashes();
const [isVisible, toggleVisible] = useToggle();
const [accountId, setAcountId] = useState<string | null>(null);
const [{ proposal, proposalLength }, setProposal] = useState<ProposalState>({ proposal: null, proposalLength: 0 });
const [selectedEra, setSelectedEra] = useState(0);

const threshold = Math.ceil((members.length || 0) * getSlashProposalThreshold(api));

const eras = useMemo(
() => (slashes || []).map(([era, slashes]): Option => ({
text: t<string>('era {{era}}, {{count}} slashes', {
Expand Down Expand Up @@ -123,13 +121,13 @@ function Slashing ({ className = '', isMember, members }: Props): React.ReactEle
<TxButton
accountId={accountId}
icon='sync'
isDisabled={!threshold || !members.includes(accountId || '') || !proposal}
isDisabled={!slashProposalThreshold || !members.includes(accountId || '') || !proposal}
label={t<string>('Revert')}
onStart={toggleVisible}
params={
api.tx.council.propose.meta.args.length === 3
? [threshold, proposal, proposalLength]
: [threshold, proposal]
? [slashProposalThreshold, proposal, proposalLength]
: [slashProposalThreshold, proposal]
}
tx={api.tx.council.propose}
/>
Expand Down
Loading