From ee4621e753d4c87d0ed021e96de322de81ef1fdb Mon Sep 17 00:00:00 2001 From: Arjun Porwal <65214523+ap211unitech@users.noreply.github.com> Date: Thu, 9 Oct 2025 22:05:07 +0530 Subject: [PATCH] Display validators info after AHM (#11871) * feat: improved validator set keys ux * feat: refactor InjectKeys and useStakingAsyncApis for improved API readiness checks * feat: migrated actions to staking async * feat: refactor ListNominees and useInactives for improved nomination handling and data fetching * fix: build --- .../src/Actions/Account/BondExtra.tsx | 109 +++++ .../src/Actions/Account/InjectKeys.tsx | 137 +++++++ .../Actions/Account/InputValidateAmount.tsx | 111 ++++++ .../Account/InputValidationController.tsx | 86 ++++ .../Account/InputValidationSessionKey.tsx | 43 ++ .../InputValidationUnstakeThreshold.tsx | 46 +++ .../src/Actions/Account/KickNominees.tsx | 93 +++++ .../src/Actions/Account/ListNominees.tsx | 147 +++++++ .../src/Actions/Account/Nominate.tsx | 71 ++++ .../src/Actions/Account/Rebond.tsx | 73 ++++ .../Actions/Account/SetControllerAccount.tsx | 92 +++++ .../Actions/Account/SetRewardDestination.tsx | 90 +++++ .../src/Actions/Account/SetSessionKey.tsx | 52 +++ .../src/Actions/Account/Unbond.tsx | 92 +++++ .../src/Actions/Account/Validate.tsx | 55 +++ .../src/Actions/Account/WarnBond.tsx | 32 ++ .../src/Actions/Account/index.tsx | 377 ++++++++++++++++++ .../src/Actions/Accounts.tsx | 59 +++ .../src/Actions/NewNominator.tsx | 135 +++++++ .../src/Actions/NewStash.tsx | 60 +++ .../src/Actions/NewValidator.tsx | 143 +++++++ .../src/Actions/Pool/Account.tsx | 201 ++++++++++ .../src/Actions/Pool/BondExtra.tsx | 92 +++++ .../src/Actions/Pool/Unbond.tsx | 84 ++++ .../src/Actions/Pool/index.tsx | 49 +++ .../src/Actions/Pool/types.ts | 10 + .../src/Actions/Pool/useAccountInfo.ts | 36 ++ .../page-staking-async/src/Actions/Pools.tsx | 61 +++ .../src/Actions/destOptions.tsx | 19 + .../page-staking-async/src/Actions/index.tsx | 210 ++++++++++ .../src/Actions/partials/Bond.tsx | 218 ++++++++++ .../src/Actions/partials/Nominate.tsx | 175 ++++++++ .../src/Actions/partials/PoolInfo.tsx | 44 ++ .../src/Actions/partials/SenderInfo.tsx | 50 +++ .../src/Actions/partials/SessionKey.tsx | 68 ++++ .../src/Actions/partials/Validate.tsx | 104 +++++ .../src/Actions/partials/types.ts | 23 ++ .../page-staking-async/src/Actions/types.ts | 25 ++ .../src/Actions/useInactives.ts | 206 ++++++++++ .../src/Actions/useSlashingSpans.ts | 22 + .../src/Actions/useUnbondDuration.ts | 24 ++ .../page-staking-async/src/System/index.tsx | 2 +- tsconfig.base.json | 2 +- 43 files changed, 3826 insertions(+), 2 deletions(-) create mode 100644 packages/page-staking-async/src/Actions/Account/BondExtra.tsx create mode 100644 packages/page-staking-async/src/Actions/Account/InjectKeys.tsx create mode 100644 packages/page-staking-async/src/Actions/Account/InputValidateAmount.tsx create mode 100644 packages/page-staking-async/src/Actions/Account/InputValidationController.tsx create mode 100644 packages/page-staking-async/src/Actions/Account/InputValidationSessionKey.tsx create mode 100644 packages/page-staking-async/src/Actions/Account/InputValidationUnstakeThreshold.tsx create mode 100644 packages/page-staking-async/src/Actions/Account/KickNominees.tsx create mode 100644 packages/page-staking-async/src/Actions/Account/ListNominees.tsx create mode 100644 packages/page-staking-async/src/Actions/Account/Nominate.tsx create mode 100644 packages/page-staking-async/src/Actions/Account/Rebond.tsx create mode 100644 packages/page-staking-async/src/Actions/Account/SetControllerAccount.tsx create mode 100644 packages/page-staking-async/src/Actions/Account/SetRewardDestination.tsx create mode 100644 packages/page-staking-async/src/Actions/Account/SetSessionKey.tsx create mode 100644 packages/page-staking-async/src/Actions/Account/Unbond.tsx create mode 100644 packages/page-staking-async/src/Actions/Account/Validate.tsx create mode 100644 packages/page-staking-async/src/Actions/Account/WarnBond.tsx create mode 100644 packages/page-staking-async/src/Actions/Account/index.tsx create mode 100644 packages/page-staking-async/src/Actions/Accounts.tsx create mode 100644 packages/page-staking-async/src/Actions/NewNominator.tsx create mode 100644 packages/page-staking-async/src/Actions/NewStash.tsx create mode 100644 packages/page-staking-async/src/Actions/NewValidator.tsx create mode 100644 packages/page-staking-async/src/Actions/Pool/Account.tsx create mode 100644 packages/page-staking-async/src/Actions/Pool/BondExtra.tsx create mode 100644 packages/page-staking-async/src/Actions/Pool/Unbond.tsx create mode 100644 packages/page-staking-async/src/Actions/Pool/index.tsx create mode 100644 packages/page-staking-async/src/Actions/Pool/types.ts create mode 100644 packages/page-staking-async/src/Actions/Pool/useAccountInfo.ts create mode 100644 packages/page-staking-async/src/Actions/Pools.tsx create mode 100644 packages/page-staking-async/src/Actions/destOptions.tsx create mode 100644 packages/page-staking-async/src/Actions/index.tsx create mode 100644 packages/page-staking-async/src/Actions/partials/Bond.tsx create mode 100644 packages/page-staking-async/src/Actions/partials/Nominate.tsx create mode 100644 packages/page-staking-async/src/Actions/partials/PoolInfo.tsx create mode 100644 packages/page-staking-async/src/Actions/partials/SenderInfo.tsx create mode 100644 packages/page-staking-async/src/Actions/partials/SessionKey.tsx create mode 100644 packages/page-staking-async/src/Actions/partials/Validate.tsx create mode 100644 packages/page-staking-async/src/Actions/partials/types.ts create mode 100644 packages/page-staking-async/src/Actions/types.ts create mode 100644 packages/page-staking-async/src/Actions/useInactives.ts create mode 100644 packages/page-staking-async/src/Actions/useSlashingSpans.ts create mode 100644 packages/page-staking-async/src/Actions/useUnbondDuration.ts diff --git a/packages/page-staking-async/src/Actions/Account/BondExtra.tsx b/packages/page-staking-async/src/Actions/Account/BondExtra.tsx new file mode 100644 index 0000000000..945cc01aa8 --- /dev/null +++ b/packages/page-staking-async/src/Actions/Account/BondExtra.tsx @@ -0,0 +1,109 @@ +// Copyright 2017-2025 @polkadot/app-staking-async authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import type { ApiPromise } from '@polkadot/api'; +import type { DeriveBalancesAll, DeriveStakingAccount } from '@polkadot/api-derive/types'; +import type { AmountValidateState } from '../types.js'; + +import React, { useMemo, useState } from 'react'; + +import { InputAddress, InputBalance, Modal, TxButton } from '@polkadot/react-components'; +import { useApi, useCall } from '@polkadot/react-hooks'; +import { BalanceFree } from '@polkadot/react-query'; +import { BN, BN_ZERO } from '@polkadot/util'; + +import { useTranslation } from '../../translate.js'; +import ValidateAmount from './InputValidateAmount.js'; + +interface Props { + controllerId: string | null; + onClose: () => void; + stakingInfo?: DeriveStakingAccount; + stashId: string; +} + +function calcBalance (api: ApiPromise, stakingInfo?: DeriveStakingAccount, stashBalance?: DeriveBalancesAll): BN | null { + if (stakingInfo?.stakingLedger && stashBalance) { + const sumUnlocking = (stakingInfo.unlocking || []).reduce((acc, { value }) => acc.iadd(value), new BN(0)); + const redeemable = stakingInfo.redeemable || BN_ZERO; + const available = stashBalance.freeBalance.sub(stakingInfo.stakingLedger.active?.unwrap() || BN_ZERO).sub(sumUnlocking).sub(redeemable); + + return available.gt(api.consts.balances.existentialDeposit) + ? available.sub(api.consts.balances.existentialDeposit) + : BN_ZERO; + } + + return null; +} + +function BondExtra ({ controllerId, onClose, stakingInfo, stashId }: Props): React.ReactElement { + const { t } = useTranslation(); + const { api } = useApi(); + const [amountError, setAmountError] = useState(null); + const [maxAdditional, setMaxAdditional] = useState(); + const stashBalance = useCall(api.derive.balances?.all, [stashId]); + const currentAmount = useMemo( + () => stakingInfo?.stakingLedger?.active?.unwrap(), + [stakingInfo] + ); + + const startBalance = useMemo( + () => calcBalance(api, stakingInfo, stashBalance), + [api, stakingInfo, stashBalance] + ); + + return ( + + + + + + {startBalance && ( + + {t('balance')}} + params={stashId} + /> + } + onChange={setMaxAdditional} + /> + + + )} + + + + + + ); +} + +export default React.memo(BondExtra); diff --git a/packages/page-staking-async/src/Actions/Account/InjectKeys.tsx b/packages/page-staking-async/src/Actions/Account/InjectKeys.tsx new file mode 100644 index 0000000000..1d9cd04301 --- /dev/null +++ b/packages/page-staking-async/src/Actions/Account/InjectKeys.tsx @@ -0,0 +1,137 @@ +// Copyright 2017-2025 @polkadot/app-staking-async authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import type { KeypairType } from '@polkadot/util-crypto/types'; + +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; + +import { Button, Dropdown, Input, MarkWarning, Modal } from '@polkadot/react-components'; +import { useQueue } from '@polkadot/react-hooks'; +import { keyring } from '@polkadot/ui-keyring'; +import { assert, u8aToHex } from '@polkadot/util'; +import { keyExtractSuri, mnemonicValidate } from '@polkadot/util-crypto'; + +import { useTranslation } from '../../translate.js'; + +interface Props { + onClose: () => void; +} + +const CRYPTO_MAP: Record = { + aura: ['ed25519', 'sr25519'], + babe: ['sr25519'], + gran: ['ed25519'], + imon: ['ed25519', 'sr25519'], + para: ['sr25519'] +}; + +const EMPTY_KEY = '0x'; + +function InjectKeys ({ onClose }: Props): React.ReactElement | null { + const { t } = useTranslation(); + const { queueRpc } = useQueue(); + + // this needs to align with what is set as the first value in `type` + const [crypto, setCrypto] = useState('sr25519'); + const [publicKey, setPublicKey] = useState(EMPTY_KEY); + const [suri, setSuri] = useState(''); + const [keyType, setKeyType] = useState('babe'); + + const keyTypeOptRef = useRef([ + { text: t('Aura'), value: 'aura' }, + { text: t('Babe'), value: 'babe' }, + { text: t('Grandpa'), value: 'gran' }, + { text: t('I\'m Online'), value: 'imon' }, + { text: t('Parachains'), value: 'para' } + ]); + + useEffect((): void => { + setCrypto(CRYPTO_MAP[keyType][0]); + }, [keyType]); + + useEffect((): void => { + try { + const { phrase } = keyExtractSuri(suri); + + assert(mnemonicValidate(phrase), 'Invalid mnemonic phrase'); + + setPublicKey(u8aToHex(keyring.createFromUri(suri, {}, crypto).publicKey)); + } catch { + setPublicKey(EMPTY_KEY); + } + }, [crypto, suri]); + + const _onSubmit = useCallback( + (): void => queueRpc({ + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + rpc: { method: 'insertKey', section: 'author' } as any, + values: [keyType, suri, publicKey] + }), + [keyType, publicKey, queueRpc, suri] + ); + + const _cryptoOptions = useMemo( + () => CRYPTO_MAP[keyType].map((value): { text: string; value: KeypairType } => ({ + text: value === 'ed25519' + ? t('ed25519, Edwards') + : t('sr15519, Schnorrkel'), + value + })), + [keyType, t] + ); + + return ( + + + + + + + + + + + + + + + + + + +