Skip to content

Commit

Permalink
Fix: add custom delegate validation (#2149)
Browse files Browse the repository at this point in the history
  • Loading branch information
Asmadek authored Aug 20, 2024
1 parent 34d1f1c commit 18fcf83
Show file tree
Hide file tree
Showing 8 changed files with 44 additions and 34 deletions.
12 changes: 4 additions & 8 deletions src/renderer/widgets/DelegateDetails/ui/YourDelegation.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { BN } from '@polkadot/util';
import { useUnit } from 'effector-react';
import { Trans } from 'react-i18next';

Expand Down Expand Up @@ -61,13 +60,10 @@ export const YourDelegation = () => {
components={{
votes: (
<AssetBalance
value={activeDelegations[activeAccounts[0]]?.balance
.mul(
new BN(
votingService.getConvictionMultiplier(activeDelegations[activeAccounts[0]]?.conviction),
),
)
.toString()}
value={votingService.calculateVotingPower(
activeDelegations[activeAccounts[0]]?.balance,
activeDelegations[activeAccounts[0]]?.conviction,
)}
asset={chain?.assets[0]}
showSymbol={false}
/>
Expand Down
10 changes: 5 additions & 5 deletions src/renderer/widgets/DelegateDetails/ui/YourDelegations.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { BN } from '@polkadot/util';
import { useUnit } from 'effector-react';
import { Trans } from 'react-i18next';

Expand Down Expand Up @@ -51,8 +50,6 @@ export const YourDelegations = () => {

if (!account || !activeDelegation) return null;

const convictionMultiplier = votingService.getConvictionMultiplier(activeDelegation.conviction);

return (
<div key={address} className="flex h-[52px] items-center">
<div className="flex-1 px-3">
Expand Down Expand Up @@ -80,7 +77,10 @@ export const YourDelegations = () => {
components={{
votes: (
<AssetBalance
value={activeDelegation.balance.mul(new BN(convictionMultiplier))}
value={votingService.calculateVotingPower(
activeDelegation.balance,
activeDelegation.conviction,
)}
asset={chain.assets[0]}
showSymbol={false}
/>
Expand All @@ -92,7 +92,7 @@ export const YourDelegations = () => {
<Trans
t={t}
i18nKey="governance.addDelegation.balanceValue"
values={{ conviction: convictionMultiplier }}
values={{ conviction: votingService.getConvictionMultiplier(activeDelegation.conviction) }}
components={{
balance: <AssetBalance value={activeDelegation.balance} asset={chain.assets[0]} />,
}}
Expand Down
5 changes: 5 additions & 0 deletions src/renderer/widgets/DelegateModal/lib/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { type Asset } from '@/shared/core';
import { nonNullable } from '@shared/lib/utils';

import { treasurySpendsDescription } from './constants';
import { type Track } from './types';

export const getTreasuryTrackDescription = (asset: Asset | null, description: string, t: TFunction) => {
if (nonNullable(asset) && treasurySpendsDescription[description]?.[asset.symbol]) {
Expand All @@ -12,3 +13,7 @@ export const getTreasuryTrackDescription = (asset: Asset | null, description: st

return t(`${description}General`);
};

export const getTrackIds = (tracks: Track[], votedTracks: string[]): number[] => {
return tracks.filter((t) => !votedTracks.includes(t.id)).map((track) => Number(track.id));
};
6 changes: 6 additions & 0 deletions src/renderer/widgets/DelegateModal/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,9 @@ export type FeeData = {
totalFee: string;
multisigDeposit: string;
};

export type Track = {
id: string;
value: string;
description: string;
};
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,9 @@ export const selectTracksModel = {
$availableTracks,
$votedTracks,
$tracksGroup,
$allTracks: $tracksGroup.map(({ adminTracks, governanceTracks, treasuryTracks, fellowshipTracks }) => {
return [...adminTracks, ...governanceTracks, ...treasuryTracks, ...fellowshipTracks];
}),

$accounts,
$availableAccounts,
Expand Down
28 changes: 16 additions & 12 deletions src/renderer/widgets/DelegateModal/ui/SelectTracksForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { BaseModal, Button, Checkbox, FootnoteText, Icon, MultiSelect, SmallTitl
import { OperationTitle } from '@/entities/chain';
import { AccountAddress, accountUtils } from '@/entities/wallet';
import { votingAssetModel } from '@/features/governance';
import { getTreasuryTrackDescription } from '../lib/helpers';
import { getTrackIds, getTreasuryTrackDescription } from '../lib/helpers';
import { delegateModel } from '../model/delegate-model';
import { selectTracksModel } from '../model/select-tracks-model';

Expand All @@ -21,9 +21,9 @@ export const SelectTrackForm = ({ isOpen, onClose }: Props) => {
const chain = useUnit(selectTracksModel.$chain);
const tracks = useUnit(selectTracksModel.$tracks);
const accounts = useUnit(selectTracksModel.$accounts);
const availableTracks = useUnit(selectTracksModel.$availableTracks);
const votedTracks = useUnit(selectTracksModel.$votedTracks);
const tracksGroup = useUnit(selectTracksModel.$tracksGroup);
const allTracks = useUnit(selectTracksModel.$allTracks);
const asset = useUnit(votingAssetModel.$votingAsset);

const { adminTracks, governanceTracks, treasuryTracks, fellowshipTracks } = tracksGroup;
Expand All @@ -47,37 +47,41 @@ export const SelectTrackForm = ({ isOpen, onClose }: Props) => {
<div className="flex flex-1 flex-col gap-6 px-5">
<div className="flex gap-3">
<Button
pallet={availableTracks.every((trackId) => tracks.includes(Number(trackId))) ? 'primary' : 'secondary'}
pallet={getTrackIds(allTracks, votedTracks).every((t) => tracks.includes(t)) ? 'primary' : 'secondary'}
variant="chip"
onClick={() => selectTracksModel.events.tracksSelected(availableTracks.map((trackId) => Number(trackId)))}
onClick={() => selectTracksModel.events.tracksSelected(getTrackIds(allTracks, votedTracks))}
>
{t('governance.addDelegation.group.selectAll')}
</Button>
<Button
pallet={adminTracks.every((track) => tracks.includes(Number(track.id))) ? 'primary' : 'secondary'}
pallet={getTrackIds(adminTracks, votedTracks).every((t) => tracks.includes(t)) ? 'primary' : 'secondary'}
variant="chip"
onClick={() => selectTracksModel.events.tracksSelected(adminTracks.map((track) => Number(track.id)))}
onClick={() => selectTracksModel.events.tracksSelected(getTrackIds(adminTracks, votedTracks))}
>
{t('governance.addDelegation.group.admin')}
</Button>
<Button
pallet={governanceTracks.every((track) => tracks.includes(Number(track.id))) ? 'primary' : 'secondary'}
pallet={
getTrackIds(governanceTracks, votedTracks).every((t) => tracks.includes(t)) ? 'primary' : 'secondary'
}
variant="chip"
onClick={() => selectTracksModel.events.tracksSelected(governanceTracks.map((track) => Number(track.id)))}
onClick={() => selectTracksModel.events.tracksSelected(getTrackIds(governanceTracks, votedTracks))}
>
{t('governance.addDelegation.group.governance')}
</Button>
<Button
pallet={treasuryTracks.every((track) => tracks.includes(Number(track.id))) ? 'primary' : 'secondary'}
pallet={getTrackIds(treasuryTracks, votedTracks).every((t) => tracks.includes(t)) ? 'primary' : 'secondary'}
variant="chip"
onClick={() => selectTracksModel.events.tracksSelected(treasuryTracks.map((track) => Number(track.id)))}
onClick={() => selectTracksModel.events.tracksSelected(getTrackIds(treasuryTracks, votedTracks))}
>
{t('governance.addDelegation.group.treasury')}
</Button>
<Button
pallet={fellowshipTracks.every((track) => tracks.includes(Number(track.id))) ? 'primary' : 'secondary'}
pallet={
getTrackIds(fellowshipTracks, votedTracks).every((t) => tracks.includes(t)) ? 'primary' : 'secondary'
}
variant="chip"
onClick={() => selectTracksModel.events.tracksSelected(fellowshipTracks.map((track) => Number(track.id)))}
onClick={() => selectTracksModel.events.tracksSelected(getTrackIds(fellowshipTracks, votedTracks))}
>
{t('governance.addDelegation.group.fellowship')}
</Button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export const AddCustomDelegationModel = () => {
</div>

<div className="flex justify-end px-5 pt-3">
<Button disabled={!error} onClick={() => delegationModel.events.createCustomDelegate()}>
<Button disabled={!!error} onClick={() => delegationModel.events.createCustomDelegate()}>
{t('signing.continueButton')}
</Button>
</div>
Expand Down
12 changes: 4 additions & 8 deletions src/renderer/widgets/DelegationModal/model/delegation-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { readonly } from 'patronum';

import { type DelegateAccount } from '@/shared/api/governance';
import { type Address } from '@/shared/core';
import { Step, includesMultiple, toAccountId, toAddress, validateAddress } from '@/shared/lib/utils';
import { Step, includesMultiple, toAccountId, validateAddress } from '@/shared/lib/utils';
import { votingService } from '@/entities/governance';
import { walletModel } from '@/entities/wallet';
import {
Expand Down Expand Up @@ -89,22 +89,18 @@ const $customError = combine(
wallet: walletModel.$activeWallet,
chain: delegationAggregate.$chain,
},
({ delegate, votes, wallet, chain }): DelegationErrors | undefined => {
({ delegate, votes, wallet, chain }): DelegationErrors | null => {
if (!wallet || !chain || !delegate || !validateAddress(delegate)) return DelegationErrors.INVALID_ADDRESS;

const isSameAccount = wallet.accounts.some((a) => a.accountId === toAccountId(delegate));

if (isSameAccount) return DelegationErrors.YOUR_ACCOUNT;

const isAlreadyDelegated = wallet.accounts.some((a) => {
const address = toAddress(a.accountId, { prefix: chain.addressPrefix });

return Object.keys(votes[address]).length > 0;
});
const isAlreadyDelegated = delegate && votes[delegate] && Object.keys(votes[delegate]).length > 0;

if (isAlreadyDelegated) return DelegationErrors.ALREADY_DELEGATED;

return;
return null;
},
);

Expand Down

0 comments on commit 18fcf83

Please sign in to comment.