mirror of
https://github.com/pezkuwichain/pezkuwi-apps.git
synced 2026-10-23 20:16:17 +00:00
- dev ^0.86.4 at the root and in packages/apps; directives renamed by pezkuwi-dev-migrate-eslint10. - eslint --fix with the 0.86.4 config; one fix broke syntax (typescript-eslint prefer-optional-chain turned `(isStarted && onStart) && onStart()` into `(isStarted && onStart?.();` in TxButton) and is corrected by hand. - The remaining findings fixed rather than disabled: optional chains (each suggestion reviewed), `cause` on rethrown errors, `if` instead of `a && promise` in effects, parallel instead of sequential awaits in Promise.all, Error rejections, no `delete` on arrays (the storage query cache is a Map; an upload's file list no longer keeps a hole), no `[object Object]` stringification (TFunction's second argument is text or options), dead code removed (referenda's never-shown expand button, react-query's unused CoreDescriptor).
178 lines
6.1 KiB
TypeScript
178 lines
6.1 KiB
TypeScript
// Copyright 2017-2026 @pezkuwi/app-referenda authors & contributors
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
import type { ApiPromise } from '@pezkuwi/api';
|
|
import type { Option } from '@pezkuwi/types';
|
|
import type { PezpalletConvictionVotingVoteCasting, PezpalletConvictionVotingVoteVoting, PezpalletReferendaReferendumInfoConvictionVotingTally } from '@pezkuwi/types/lookup';
|
|
import type { BN } from '@pezkuwi/util';
|
|
import type { Lock, PalletReferenda, PalletVote } from './types.js';
|
|
|
|
import { useMemo } from 'react';
|
|
|
|
import { CONVICTIONS } from '@pezkuwi/react-components/ConvictionDropdown';
|
|
import { createNamedHook, useApi, useCall } from '@pezkuwi/react-hooks';
|
|
import { BN_MAX_INTEGER } from '@pezkuwi/util';
|
|
|
|
const OPT_CLASS = {
|
|
transform: (locks: [BN, BN][]): BN[] =>
|
|
locks.map(([classId]) => classId)
|
|
};
|
|
|
|
const OPT_VOTES = {
|
|
transform: ([[params], votes]: [[[string, BN][]], PezpalletConvictionVotingVoteVoting[]]): [classId: BN, refIds: BN[], casting: PezpalletConvictionVotingVoteCasting][] =>
|
|
votes
|
|
.map((v, index): null | [BN, BN[], PezpalletConvictionVotingVoteCasting] => {
|
|
if (!v.isCasting) {
|
|
return null;
|
|
}
|
|
|
|
const casting = v.asCasting;
|
|
|
|
return [
|
|
params[index][1],
|
|
casting.votes.map(([refId]) => refId),
|
|
casting
|
|
];
|
|
})
|
|
.filter((v): v is [BN, BN[], PezpalletConvictionVotingVoteCasting] => !!v),
|
|
withParamsTransform: true
|
|
};
|
|
|
|
const OPT_REFS = {
|
|
transform: ([[params], optTally]: [[BN[]], Option<PezpalletReferendaReferendumInfoConvictionVotingTally>[]]): [BN, PezpalletReferendaReferendumInfoConvictionVotingTally][] =>
|
|
optTally
|
|
.map((v, index): null | [BN, PezpalletReferendaReferendumInfoConvictionVotingTally] =>
|
|
v.isSome
|
|
? [params[index], v.unwrap()]
|
|
: null
|
|
)
|
|
.filter((v): v is [BN, PezpalletReferendaReferendumInfoConvictionVotingTally] => !!v),
|
|
withParamsTransform: true
|
|
};
|
|
|
|
function getVoteParams (accountId: string, lockClasses?: BN[]): [[accountId: string, classId: BN][]] | undefined {
|
|
if (lockClasses) {
|
|
return [lockClasses.map((classId) => [accountId, classId])];
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
function getRefParams (votes?: [classId: BN, refIds: BN[], casting: PezpalletConvictionVotingVoteCasting][]): [BN[]] | undefined {
|
|
if (votes?.length) {
|
|
const refIds = votes.reduce<BN[]>((all, [, refIds]) => all.concat(refIds), []);
|
|
|
|
if (refIds.length) {
|
|
return [refIds];
|
|
}
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
function getLocks (api: ApiPromise, palletVote: PalletVote, votes: [classId: BN, refIds: BN[], casting: PezpalletConvictionVotingVoteCasting][], referenda: [BN, PezpalletReferendaReferendumInfoConvictionVotingTally][]): Lock[] {
|
|
const lockPeriod = api.consts[palletVote].voteLockingPeriod as BN;
|
|
const locks: Lock[] = [];
|
|
|
|
for (let i = 0, voteCount = votes.length; i < voteCount; i++) {
|
|
const [classId,, casting] = votes[i];
|
|
|
|
for (let i = 0, castCount = casting.votes.length; i < castCount; i++) {
|
|
const [refId, accountVote] = casting.votes[i];
|
|
const refInfo = referenda.find(([id]) => id.eq(refId));
|
|
|
|
if (refInfo) {
|
|
const [, tally] = refInfo;
|
|
let total: BN | undefined;
|
|
let endBlock: BN | undefined;
|
|
let convictionIndex = 0;
|
|
let locked = 'None';
|
|
const durationIndex = 1;
|
|
|
|
if (accountVote.isStandard) {
|
|
const { balance, vote } = accountVote.asStandard;
|
|
|
|
total = balance;
|
|
|
|
if ((tally.isApproved && vote.isAye) || (tally.isRejected && vote.isNay)) {
|
|
convictionIndex = vote.conviction.index;
|
|
locked = vote.conviction.type;
|
|
}
|
|
} else if (accountVote.isSplit) {
|
|
const { aye, nay } = accountVote.asSplit;
|
|
|
|
total = aye.add(nay);
|
|
} else if (accountVote.isSplitAbstain) {
|
|
const { abstain, aye, nay } = accountVote.asSplitAbstain;
|
|
|
|
total = aye.add(nay).add(abstain);
|
|
} else {
|
|
console.error(`Unable to handle ${accountVote.type}`);
|
|
}
|
|
|
|
if (tally.isOngoing) {
|
|
endBlock = BN_MAX_INTEGER;
|
|
} else if (tally.isKilled) {
|
|
endBlock = tally.asKilled;
|
|
} else if (tally.isCancelled || tally.isTimedOut) {
|
|
endBlock = tally.isCancelled
|
|
? tally.asCancelled[0]
|
|
: tally.asTimedOut[0];
|
|
} else if (tally.isApproved || tally.isRejected) {
|
|
endBlock = lockPeriod
|
|
.muln(convictionIndex ? CONVICTIONS[convictionIndex - 1][durationIndex] : 0)
|
|
.add(
|
|
tally.isApproved
|
|
? tally.asApproved[0]
|
|
: tally.asRejected[0]
|
|
);
|
|
} else {
|
|
console.error(`Unable to handle ${tally.type}`);
|
|
}
|
|
|
|
if (total && endBlock) {
|
|
locks.push({ classId, endBlock, locked, refId, total });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return locks;
|
|
}
|
|
|
|
function useAccountLocksImpl (palletReferenda: PalletReferenda, palletVote: PalletVote, accountId: string): Lock[] | undefined {
|
|
const { api } = useApi();
|
|
|
|
// retrieve the locks for the account (all classes) via the accountId
|
|
const lockParams = useMemo(
|
|
() => [accountId],
|
|
[accountId]
|
|
);
|
|
|
|
const lockClasses = useCall<BN[] | undefined>(lockParams && api.query[palletVote]?.classLocksFor, lockParams, OPT_CLASS);
|
|
|
|
// retrieve the specific votes casted over the classes & accountId
|
|
const voteParams = useMemo(
|
|
() => getVoteParams(accountId, lockClasses),
|
|
[accountId, lockClasses]
|
|
);
|
|
|
|
const votes = useCall<[BN, BN[], PezpalletConvictionVotingVoteCasting][] | undefined>(voteParams && api.query[palletVote]?.votingFor.multi, voteParams, OPT_VOTES);
|
|
|
|
// retrieve the referendums that were voted on
|
|
const refParams = useMemo(
|
|
() => getRefParams(votes),
|
|
[votes]
|
|
);
|
|
|
|
const referenda = useCall(refParams && api.query[palletReferenda]?.referendumInfoFor.multi, refParams, OPT_REFS);
|
|
|
|
// combine the referenda outcomes and the votes into locks
|
|
return useMemo(
|
|
() => votes && referenda && getLocks(api, palletVote, votes, referenda),
|
|
[api, palletVote, referenda, votes]
|
|
);
|
|
}
|
|
|
|
export default createNamedHook('useAccountLocks', useAccountLocksImpl);
|