mirror of
https://github.com/pezkuwichain/pezkuwi-sdk-ui.git
synced 2026-04-25 11:37:56 +00:00
d949863789
Comprehensive web interface for interacting with Pezkuwi blockchain. Features: - Blockchain explorer - Wallet management - Staking interface - Governance participation - Developer tools Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
128 lines
4.7 KiB
TypeScript
128 lines
4.7 KiB
TypeScript
// Copyright 2017-2026 @pezkuwi/react-hooks authors & contributors
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
import type { ApiPromise } from '@pezkuwi/api';
|
|
import type { CombinatorFunction } from '@pezkuwi/api/promise/Combinator';
|
|
import type { DeriveStakingAccount } from '@pezkuwi/api-derive/types';
|
|
import type { AccountId, ValidatorPrefs } from '@pezkuwi/types/interfaces';
|
|
import type { Codec, ITuple } from '@pezkuwi/types/types';
|
|
import type { StakerState } from './types.js';
|
|
|
|
import { useEffect, useMemo, useState } from 'react';
|
|
|
|
import { u8aConcat, u8aToHex } from '@pezkuwi/util';
|
|
|
|
import { isEmpty } from './utils/isEmpty.js';
|
|
import { createNamedHook } from './createNamedHook.js';
|
|
import { useAccounts } from './useAccounts.js';
|
|
import { useApi } from './useApi.js';
|
|
import { useIsMountedRef } from './useIsMountedRef.js';
|
|
import { useOwnStashes } from './useOwnStashes.js';
|
|
|
|
type ValidatorInfo = ITuple<[ValidatorPrefs, Codec]> | ValidatorPrefs;
|
|
type Queried = Record<string, [boolean, DeriveStakingAccount, ValidatorInfo]>;
|
|
|
|
function toIdString (id?: AccountId | null): string | null {
|
|
return id
|
|
? id.toString()
|
|
: null;
|
|
}
|
|
|
|
const QUERY_OPTS = {
|
|
withClaimedRewardsEras: true,
|
|
withDestination: true,
|
|
withLedger: true,
|
|
withNominations: true,
|
|
withPrefs: true
|
|
};
|
|
|
|
function getStakerState (stashId: string, allAccounts: string[], [isOwnStash, { claimedRewardsEras, controllerId: _controllerId, exposureMeta, exposurePaged, nextSessionIds: _nextSessionIds, nominators, rewardDestination, sessionIds: _sessionIds, stakingLedger, validatorPrefs }, validateInfo]: [boolean, DeriveStakingAccount, ValidatorInfo]): StakerState {
|
|
const isStashNominating = !!(nominators?.length);
|
|
const isStashValidating = !(Array.isArray(validateInfo) ? isEmpty(validateInfo[1] as ValidatorPrefs) : isEmpty(validateInfo));
|
|
const nextSessionIds = _nextSessionIds instanceof Map
|
|
? [..._nextSessionIds.values()]
|
|
: _nextSessionIds;
|
|
const nextConcat = u8aConcat(...nextSessionIds.map((id) => id.toU8a()));
|
|
const sessionIds = _sessionIds instanceof Map
|
|
? [..._sessionIds.values()]
|
|
: _sessionIds;
|
|
const currConcat = u8aConcat(...sessionIds.map((id) => id.toU8a()));
|
|
const controllerId = toIdString(_controllerId);
|
|
|
|
return {
|
|
claimedRewardsEras,
|
|
controllerId,
|
|
destination: rewardDestination,
|
|
exposureMeta,
|
|
exposurePaged,
|
|
hexSessionIdNext: u8aToHex(nextConcat, 48),
|
|
hexSessionIdQueue: u8aToHex(currConcat.length ? currConcat : nextConcat, 48),
|
|
isLoading: false,
|
|
isOwnController: allAccounts.includes(controllerId || ''),
|
|
isOwnStash,
|
|
isStashNominating,
|
|
isStashValidating,
|
|
// we assume that all ids are non-null
|
|
nominating: nominators?.map(toIdString) as string[],
|
|
sessionIds: (
|
|
nextSessionIds.length
|
|
? nextSessionIds
|
|
: sessionIds
|
|
).map(toIdString) as string[],
|
|
stakingLedger,
|
|
stashId,
|
|
validatorPrefs
|
|
};
|
|
}
|
|
|
|
function useOwnStashInfosImpl (apiOverride?: ApiPromise): StakerState[] | undefined {
|
|
const { api: connectedApi } = useApi();
|
|
const api = useMemo(() => apiOverride ?? connectedApi, [apiOverride, connectedApi]);
|
|
const { allAccounts } = useAccounts();
|
|
const mountedRef = useIsMountedRef();
|
|
const ownStashes = useOwnStashes(undefined, api);
|
|
const [queried, setQueried] = useState<Queried | undefined>();
|
|
|
|
useEffect((): () => void => {
|
|
let unsub: (() => void) | undefined;
|
|
|
|
if (ownStashes) {
|
|
if (ownStashes.length) {
|
|
const stashIds = ownStashes.map(([stashId]) => stashId);
|
|
const fns = [
|
|
[api.derive.staking.accounts, stashIds, QUERY_OPTS],
|
|
[api.query.staking.validators.multi, stashIds]
|
|
] as unknown as CombinatorFunction[];
|
|
|
|
api.combineLatest<[DeriveStakingAccount[], ValidatorInfo[]]>(fns, ([accounts, validators]): void => {
|
|
mountedRef.current && ownStashes.length === accounts.length && ownStashes.length === validators.length && setQueried(
|
|
ownStashes.reduce((queried: Queried, [stashId, isOwnStash], index): Queried => ({
|
|
...queried,
|
|
[stashId]: [isOwnStash, accounts[index], validators[index]]
|
|
}), {})
|
|
);
|
|
}).then((u): void => {
|
|
unsub = u;
|
|
}).catch(console.error);
|
|
} else {
|
|
mountedRef.current && setQueried({});
|
|
}
|
|
}
|
|
|
|
return (): void => {
|
|
unsub && unsub();
|
|
};
|
|
}, [api, mountedRef, ownStashes]);
|
|
|
|
return useMemo(
|
|
() => ownStashes && queried && ownStashes.length === Object.keys(queried).length
|
|
? ownStashes
|
|
.filter(([stashId]) => queried[stashId])
|
|
.map(([stashId]) => getStakerState(stashId, allAccounts, queried[stashId]))
|
|
: undefined,
|
|
[allAccounts, ownStashes, queried]
|
|
);
|
|
}
|
|
|
|
export const useOwnStashInfos = createNamedHook('useOwnStashInfos', useOwnStashInfosImpl);
|