// Copyright 2017-2020 @polkadot/react-components authors & contributors // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. import { DerivedBalancesAll, DerivedStakingAccount } from '@polkadot/api-derive/types'; import { ValidatorPrefsTo145 } from '@polkadot/types/interfaces'; import { BareProps } from './types'; import BN from 'bn.js'; import React from 'react'; import styled from 'styled-components'; import { formatBalance, formatNumber, isObject } from '@polkadot/util'; import { Icon, Tooltip, TxButton } from '@polkadot/react-components'; import { withCalls, withMulti } from '@polkadot/react-api/hoc'; import { useAccounts } from '@polkadot/react-hooks'; import { FormatBalance } from '@polkadot/react-query'; import CryptoType from './CryptoType'; import Label from './Label'; import { useTranslation } from './translate'; // true to display, or (for bonded) provided values [own, ...all extras] export interface BalanceActiveType { available?: boolean; bonded?: boolean | BN[]; extraInfo?: [React.ReactNode, React.ReactNode][]; locked?: boolean; redeemable?: boolean; reserved?: boolean; total?: boolean; unlocking?: boolean; vested?: boolean; } export interface CryptoActiveType { crypto?: boolean; nonce?: boolean; } export interface ValidatorPrefsType { unstakeThreshold?: boolean; validatorPayment?: boolean; } const PERBILL = new BN(1000000000); interface Props extends BareProps { address: string; balancesAll?: DerivedBalancesAll; children?: React.ReactNode; extraInfo?: [string, string][]; stakingInfo?: DerivedStakingAccount; withBalance?: boolean | BalanceActiveType; withBalanceToggle?: false; withExtended?: boolean | CryptoActiveType; withHexSessionId?: (string | null)[]; withRewardDestination?: boolean; withValidatorPrefs?: boolean | ValidatorPrefsType; } const DEFAULT_BALANCES: BalanceActiveType = { available: true, bonded: true, locked: true, redeemable: true, reserved: true, total: true, unlocking: true, vested: true }; const DEFAULT_EXTENDED = { crypto: true, nonce: true }; const DEFAULT_PREFS = { unstakeThreshold: true, validatorPayment: true }; // skip balances retrieval of none of this matches function skipBalancesIf ({ withBalance = true, withExtended = false }: Props): boolean { if (withBalance === true || withExtended === true) { return false; } else if (isObject(withBalance)) { // these all pull from the all balances if (withBalance.available || withBalance.locked || withBalance.reserved || withBalance.total || withBalance.vested) { return false; } } else if (isObject(withExtended)) { if (withExtended.nonce) { return false; } } return true; } function skipStakingIf ({ stakingInfo, withBalance = true, withRewardDestination = false, withValidatorPrefs = false }: Props): boolean { if (stakingInfo) { return true; } else if (withBalance === true || withValidatorPrefs || withRewardDestination) { return false; } else if (isObject(withBalance)) { if (withBalance.unlocking || withBalance.redeemable) { return false; } else if (withBalance.bonded) { return Array.isArray(withBalance.bonded); } } return true; } // calculates the bonded, first being the own, the second being nominated function calcBonded (stakingInfo?: DerivedStakingAccount, bonded?: boolean | BN[]): [BN, BN[]] { let other: BN[] = []; let own = new BN(0); if (Array.isArray(bonded)) { other = bonded .filter((_, index): boolean => index !== 0) .filter((value): boolean => value.gtn(0)); own = bonded[0]; } else if (stakingInfo && stakingInfo.stakingLedger && stakingInfo.accountId.eq(stakingInfo.stashId)) { own = stakingInfo.stakingLedger.active.unwrap(); } return [own, other]; } function renderExtended ({ balancesAll, address, withExtended }: Props, t: (key: string) => string): React.ReactNode { const extendedDisplay = withExtended === true ? DEFAULT_EXTENDED : withExtended || undefined; if (!extendedDisplay) { return null; } return (
{balancesAll && extendedDisplay.nonce && ( <>
); } function renderUnlocking ({ address, stakingInfo }: Props, t: (key: string, data: any) => string): React.ReactNode { if (!stakingInfo || !stakingInfo.unlocking || !stakingInfo.unlocking.length) { return null; } const total = stakingInfo.unlocking.reduce((total, { value }): BN => total.add(value), new BN(0)); if (total.eqn(0)) { return null; } return (
(
{t('{{value}}, {{remaining}} blocks left', { replace: { remaining: formatNumber(remainingBlocks), value: formatBalance(value, { forceUnit: '-' }) } })}
))} trigger={`${address}-unlocking-trigger`} />
); } function renderValidatorPrefs ({ stakingInfo, withValidatorPrefs = false }: Props, t: (key: string) => string): React.ReactNode { const validatorPrefsDisplay = withValidatorPrefs === true ? DEFAULT_PREFS : withValidatorPrefs; if (!validatorPrefsDisplay || !stakingInfo || !stakingInfo.validatorPrefs) { return null; } return ( <>
{validatorPrefsDisplay.unstakeThreshold && (stakingInfo.validatorPrefs as any as ValidatorPrefsTo145).unstakeThreshold && ( <>