@polkadot/util#isFunction TypeScript Examples
The following examples show how to use
@polkadot/util#isFunction.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: util.ts From crust-apps with Apache License 2.0 | 7 votes |
export function balanceToNumber (amount: BN | ToBN = BN_ZERO, divisor: BN): number {
const value = isBn(amount)
? amount
: isFunction(amount.toBn)
? amount.toBn()
: BN_ZERO;
return value.mul(BN_THOUSAND).div(divisor).toNumber() / 1000;
}
Example #2
Source File: Transaction.ts From gear-js with GNU General Public License v3.0 | 6 votes |
public async signAndSend(
account: AddressOrPair,
optionsOrCallback?: Partial<SignerOptions> | TransactionStatusCb,
optionalCallback?: TransactionStatusCb,
): Promise<Hash | (() => void)> {
const [options, callback] = isFunction(optionsOrCallback)
? [undefined, optionsOrCallback]
: [optionsOrCallback, optionalCallback];
try {
return await this.submitted.signAndSend(account, options, callback);
} catch (error) {
const errorCode = +error.message.split(':')[0];
if (errorCode === 1010) {
throw new TransactionError('Account balance too low');
} else {
throw new TransactionError(error.message);
}
}
}
Example #3
Source File: useStakerPayouts.ts From crust-apps with Apache License 2.0 | 6 votes |
export default function useStakerPayouts (): BN {
const { api } = useApi();
const migrateEraOpt = useCall<Option<EraIndex>>(api.query.staking?.migrateEra);
return useMemo(
() => (migrateEraOpt && migrateEraOpt.isSome && migrateEraOpt.unwrap()) || (
isFunction(api.tx.staking.payoutStakers)
? BN_ZERO
: BN_BILLION
),
[api, migrateEraOpt]
);
}
Example #4
Source File: democracy_proposal.ts From commonwealth with GNU General Public License v3.0 | 6 votes |
public updateVoters = async () => {
const depositOpt = await this._Chain.api.query.democracy.depositOf(this.data.index);
if (!depositOpt.isSome) return;
const depositorsTuple: ITuple<[ BalanceOf | Vec<AccountId>, BalanceOf | Vec<AccountId> ]> = depositOpt.unwrap();
let depositors: Vec<AccountId>;
if (isFunction((depositorsTuple[1] as BalanceOf).mul)) {
depositors = depositorsTuple[0] as Vec<AccountId>;
} else {
depositors = depositorsTuple[1] as Vec<AccountId>;
}
for (const depositor of depositors) {
const acct = this._Accounts.fromAddress(depositor.toString());
const votes = this.getVotes(acct);
if (!votes.length) {
this.addOrUpdateVote(new DepositVote(acct, this._Chain.coins(this.data.deposit)));
} else {
// if they second a proposal multiple times, sum up the vote weight
const vote = new DepositVote(acct, this._Chain.coins(votes[0].deposit.add(this.data.deposit)));
this.addOrUpdateVote(vote);
}
}
}
Example #5
Source File: triggerChange.ts From subscan-multisig-react with Apache License 2.0 | 6 votes |
export default function triggerChange(value?: unknown, ...callOnResult: (OnChangeCb | undefined)[]): void {
if (!callOnResult || !callOnResult.length) {
return;
}
// eslint-disable-next-line @typescript-eslint/no-shadow
callOnResult.forEach((callOnResult): void => {
if (isObservable(callOnResult)) {
callOnResult.next(value);
} else if (isFunction(callOnResult)) {
callOnResult(value);
}
});
}
Example #6
Source File: index.tsx From crust-apps with Apache License 2.0 | 6 votes |
function CreateButton ({ assetIds, className }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const { hasAccounts } = useAccounts();
const [isOpen, toggleOpen] = useToggle();
return (
<>
<Button
icon='plus'
isDisabled={!assetIds || !hasAccounts || !isFunction(api.tx.utility.batchAll)}
label={t<string>('Create')}
onClick={toggleOpen}
/>
{isOpen && assetIds && (
<Create
assetIds={assetIds}
className={className}
onClose={toggleOpen}
/>
)}
</>
);
}
Example #7
Source File: valueToText.tsx From subscan-multisig-react with Apache License 2.0 | 5 votes |
function toHuman(value: Codec | Codec[]): unknown {
// eslint-disable-next-line @typescript-eslint/unbound-method
return isFunction((value as Codec).toHuman) ? (value as Codec).toHuman() : (value as Codec[]).map(toHuman);
}
Example #8
Source File: index.tsx From crust-apps with Apache License 2.0 | 5 votes |
function BridgeApp ({ basePath, onStatusChange }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const { hasAccounts } = useAccounts();
const { isIpfs } = useIpfs();
const itemsRef = isFunction(api.tx.bridgeTransfer?.transferToElrond)
? useRef([
{
isRoot: true,
name: 'bridge',
text: t<string>('Crust to Ethereum')
},
{
name: 'bridgeBack',
text: t<string>('Ethereum to Crust')
},
{
name: 'bridgeToElrond',
text: t<string>('Crust to Elrond')
},
{
name: 'elrondToCrust',
text: t<string>('Elrond to Crust')
}
])
: useRef([
{
isRoot: true,
name: 'bridge',
text: t<string>('Crust to Ethereum')
},
{
name: 'bridgeBack',
text: t<string>('Ethereum to Crust')
},
{
name: 'bridgeToShadow',
text: t<string>('Maxwell to Shadow')
}
]);
return (
<Web3Provider>
<EthersProvider>
<main className='accounts--App'>
<header>
<Tabs
basePath={basePath}
hidden={(hasAccounts && !isIpfs) ? undefined : HIDDEN_ACC}
items={itemsRef.current}
/>
</header>
<Switch>
<Route path={`${basePath}/bridgeBack`}>
<EthereumAssets />
</Route>
{isFunction(api.tx.bridgeTransfer?.transferToElrond) && <Route path={`${basePath}/bridgeToElrond`}>
<ElrondAssets />
</Route>}
{isFunction(api.tx.bridgeTransfer?.transferToElrond) && <Route path={`${basePath}/elrondToCrust`}>
<ElrondBackAssets />
</Route>}
{isFunction(api.tx.bridgeTransfer?.transferCsmNative) && <Route path={`${basePath}/bridgeToShadow`}>
<ShadowAssets />
</Route>}
<Route basePath={basePath}
onStatusChange={onStatusChange}>
<MainnetAssets />
</Route>
</Switch>
</main>
</EthersProvider>
</Web3Provider>
);
}
Example #9
Source File: PaymentInfo.tsx From subscan-multisig-react with Apache License 2.0 | 5 votes |
function PaymentInfo({ accountId, className = '', extrinsic }: Props): React.ReactElement<Props> | null {
const { t } = useTranslation();
const { api } = useApi();
const [dispatchInfo, setDispatchInfo] = useState<RuntimeDispatchInfo | null>(null);
const balances = useCall<DeriveBalancesAll>(api.derive.balances?.all, [accountId]);
const mountedRef = useIsMountedRef();
useEffect((): void => {
accountId &&
extrinsic &&
isFunction(extrinsic.paymentInfo) &&
isFunction(api.rpc.payment?.queryInfo) &&
setTimeout((): void => {
try {
extrinsic
.paymentInfo(accountId)
.then((info) => mountedRef.current && setDispatchInfo(info))
.catch(console.error);
} catch (error) {
console.error(error);
}
}, 0);
}, [api, accountId, extrinsic, mountedRef]);
if (!dispatchInfo || !extrinsic) {
return null;
}
const isFeeError =
api.consts.balances &&
!api.tx.balances?.transfer.is(extrinsic) &&
balances?.accountId.eq(accountId) &&
(balances.availableBalance.lte(dispatchInfo.partialFee) ||
balances.freeBalance.sub(dispatchInfo.partialFee).lte(api.consts.balances.existentialDeposit as unknown as BN));
return (
<>
<Expander
className={className}
summary={
<Trans i18nKey="feesForSubmission">
Fees of <span className="highlight">{formatBalance(dispatchInfo.partialFee, { withSiFull: true })}</span>{' '}
will be applied to the submission
</Trans>
}
/>
{isFeeError && (
<MarkWarning
content={t<string>(
'The account does not have enough free funds (excluding locked/bonded/reserved) available to cover the transaction fees without dropping the balance below the account existential amount.'
)}
/>
)}
</>
);
}
Example #10
Source File: Validate.tsx From crust-apps with Apache License 2.0 | 5 votes |
function Validate ({ className = '', controllerId, onChange, stashId, withSenders }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const [commission, setCommission] = useState<BN | number>(1);
const [allowNoms, setAllowNoms] = useState(true);
const blockedOptions = useRef([
{ text: t('Yes, allow nominations'), value: true },
{ text: t('No, block all nominations'), value: false }
]);
useEffect((): void => {
try {
onChange({
validateTx: api.tx.staking.validate({
// @ts-ignore
guarantee_fee: new BN(commission).isZero()
// small non-zero set to avoid isEmpty
? '0'
: (new BN(commission).toNumber() > 1000000000) ? '1000000000' : new BN(commission).toNumber().toString()
})
});
} catch {
onChange({ validateTx: null });
}
}, [api, allowNoms, commission, onChange]);
const _setCommission = useCallback(
(value?: BN) => value && setCommission(
value.isZero()
? 1 // small non-zero set to avoid isEmpty
: value.mul(COMM_MUL)
),
[]
);
return (
<div className={className}>
{withSenders && (
<Modal.Columns hint={t<string>('The stash and controller pair. This transaction, managing preferences, will be sent from the controller.')}>
<InputAddress
defaultValue={stashId}
isDisabled
label={t<string>('stash account')}
/>
<InputAddress
defaultValue={controllerId}
isDisabled
label={t<string>('controller account')}
/>
</Modal.Columns>
)}
<Modal.Columns hint={t<string>('The commission is deducted from all rewards before the remainder is split with nominators.')}>
<InputNumber
help={t<string>('The guarantee fee (0-100) that should be applied for the validator')}
isZeroable
label={t<string>('guarantee fee')}
maxValue={MAX_COMM}
onChange={_setCommission}
/>
</Modal.Columns>
{isFunction(api.tx.staking.kick) && (
<Modal.Columns hint={t<string>('The validator can block any new nominations. By default it is set to allow all nominations.')}>
<Dropdown
defaultValue={true}
help={t<string>('Does this validator allow nominations or is it blocked for all')}
label={t<string>('allows new nominations')}
onChange={setAllowNoms}
options={blockedOptions.current}
/>
</Modal.Columns>
)}
</div>
);
}
Example #11
Source File: useAccountInfo.ts From subscan-multisig-react with Apache License 2.0 | 4 votes |
export function useAccountInfo(value: string | null, isContract = false): UseAccountInfo {
const { api } = useApi();
const { isAccount } = useAccounts();
const { isAddress } = useAddresses();
const accountInfo = useCall<DeriveAccountInfo>(api.derive.accounts.info, [value]);
const accountFlags = useCall<DeriveAccountFlags>(api.derive.accounts.flags, [value]);
const nominator = useCall<Nominations>(api.query.staking?.nominators, [value]);
const validator = useCall<ValidatorPrefs>(api.query.staking?.validators, [value]);
const [accountIndex, setAccountIndex] = useState<string | undefined>(undefined);
const [tags, setSortedTags] = useState<string[]>([]);
const [name, setName] = useState('');
const [genesisHash, setGenesisHash] = useState<string | null>(null);
const [identity, setIdentity] = useState<AddressIdentity | undefined>();
const [flags, setFlags] = useState<AddressFlags>(IS_NONE);
const [meta, setMeta] = useState<KeyringJson$Meta | undefined>();
const [isEditingName, toggleIsEditingName] = useToggle();
const [isEditingTags, toggleIsEditingTags] = useToggle();
useEffect((): void => {
// eslint-disable-next-line
validator &&
setFlags((flags) => ({
...flags,
isValidator: !validator.isEmpty,
}));
}, [validator]);
useEffect((): void => {
// eslint-disable-next-line
nominator &&
setFlags((flags) => ({
...flags,
isNominator: !nominator.isEmpty,
}));
}, [nominator]);
useEffect((): void => {
// eslint-disable-next-line
accountFlags &&
setFlags((flags) => ({
...flags,
...accountFlags,
}));
}, [accountFlags]);
useEffect((): void => {
const { accountIndex, identity, nickname } = accountInfo || {};
const newIndex = accountIndex?.toString();
setAccountIndex((oldIndex) => (oldIndex !== newIndex ? newIndex : oldIndex));
let name;
if (isFunction(api.query.identity?.identityOf)) {
if (identity?.display) {
name = identity.display;
}
} else if (nickname) {
name = nickname;
}
setName(name || '');
if (identity) {
const judgements = identity.judgements.filter(([, judgement]) => !judgement.isFeePaid);
const isKnownGood = judgements.some(([, judgement]) => judgement.isKnownGood);
const isReasonable = judgements.some(([, judgement]) => judgement.isReasonable);
const isErroneous = judgements.some(([, judgement]) => judgement.isErroneous);
const isLowQuality = judgements.some(([, judgement]) => judgement.isLowQuality);
setIdentity({
...identity,
isBad: isErroneous || isLowQuality,
isErroneous,
isExistent: !!identity.display,
isGood: isKnownGood || isReasonable,
isKnownGood,
isLowQuality,
isReasonable,
judgements,
waitCount: identity.judgements.length - judgements.length,
});
} else {
setIdentity(undefined);
}
}, [accountInfo, api]);
useEffect((): void => {
if (value) {
try {
const accountOrAddress = keyring.getAccount(value) || keyring.getAddress(value);
const isOwned = isAccount(value);
const isInContacts = isAddress(value);
setGenesisHash(accountOrAddress?.meta.genesisHash || null);
setFlags(
(flags): AddressFlags => ({
...flags,
isDevelopment: accountOrAddress?.meta.isTesting || false,
isEditable:
!!(
!identity?.display &&
(isInContacts ||
accountOrAddress?.meta.isMultisig ||
(accountOrAddress && !accountOrAddress.meta.isInjected))
) || false,
isExternal: !!accountOrAddress?.meta.isExternal || false,
isHardware: !!accountOrAddress?.meta.isHardware || false,
isInContacts,
isInjected: !!accountOrAddress?.meta.isInjected || false,
isMultisig: !!accountOrAddress?.meta.isMultisig || false,
isOwned,
isProxied: !!accountOrAddress?.meta.isProxied || false,
})
);
setMeta(accountOrAddress?.meta);
setName(accountOrAddress?.meta.name || '');
setSortedTags(accountOrAddress?.meta.tags ? (accountOrAddress.meta.tags as string[]).sort() : []);
} catch (error) {
// ignore
}
}
}, [identity, isAccount, isAddress, value]);
const onSaveName = useCallback((): void => {
if (isEditingName) {
toggleIsEditingName();
}
const meta = { name, whenEdited: Date.now() };
if (isContract) {
try {
if (value) {
const originalMeta = keyring.getAddress(value)?.meta;
keyring.saveContract(value, { ...originalMeta, ...meta });
}
} catch (error) {
console.error(error);
}
} else if (value) {
try {
const pair = keyring.getPair(value);
// eslint-disable-next-line
pair && keyring.saveAccountMeta(pair, meta);
} catch (error) {
const pair = keyring.getAddress(value);
if (pair) {
keyring.saveAddress(value, meta);
} else {
keyring.saveAddress(value, { genesisHash: api.genesisHash.toHex(), ...meta });
}
}
}
}, [api, isContract, isEditingName, name, toggleIsEditingName, value]);
const onSaveTags = useCallback((): void => {
const meta = { tags, whenEdited: Date.now() };
if (isContract) {
try {
if (value) {
const originalMeta = keyring.getAddress(value)?.meta;
// eslint-disable-next-line
value && keyring.saveContract(value, { ...originalMeta, ...meta });
}
} catch (error) {
console.error(error);
}
} else if (value) {
try {
const currentKeyring = keyring.getPair(value);
// eslint-disable-next-line
currentKeyring && keyring.saveAccountMeta(currentKeyring, meta);
} catch (error) {
keyring.saveAddress(value, meta);
}
}
}, [isContract, tags, value]);
const onForgetAddress = useCallback((): void => {
if (isEditingName) {
toggleIsEditingName();
}
if (isEditingTags) {
toggleIsEditingTags();
}
try {
// eslint-disable-next-line
value && keyring.forgetAddress(value);
} catch (e) {
console.error(e);
}
}, [isEditingName, isEditingTags, toggleIsEditingName, toggleIsEditingTags, value]);
const onSetGenesisHash = useCallback(
(genesisHash: string | null): void => {
if (value) {
const account = keyring.getPair(value);
// eslint-disable-next-line
account && keyring.saveAccountMeta(account, { ...account.meta, genesisHash });
setGenesisHash(genesisHash);
}
},
[value]
);
const setTags = useCallback((tags: string[]): void => setSortedTags(tags.sort()), []);
return {
accountIndex,
flags,
genesisHash,
identity,
isEditingName,
isEditingTags,
isNull: !value,
meta,
name,
onForgetAddress,
onSaveName,
onSaveTags,
onSetGenesisHash,
setName,
setTags,
tags,
toggleIsEditingName,
toggleIsEditingTags,
};
}
Example #12
Source File: CreateGroup.tsx From crust-apps with Apache License 2.0 | 4 votes |
function CreateGroup ({ className = '', onClose, onSuccess, senderId: propSenderId }: Props): React.ReactElement<Props> {
const ownStashes = useOwnStashInfos();
const { t } = useTranslation();
const { api } = useApi();
const [amount] = useState<BN | undefined>(BN_ZERO);
const [hasAvailable] = useState(true);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [, setMaxTransfer] = useState<BN | null>(null);
const [senderId, setSenderId] = useState<string | null>(propSenderId || null);
const balances = useCall<DeriveBalancesAll>(api.derive.balances.all, [senderId]);
const stashes = useMemo(
() => (ownStashes || []).map(({ stashId }) => stashId),
[ownStashes]
);
useEffect((): void => {
if (balances && balances.accountId.eq(senderId) && senderId && isFunction(api.rpc.payment?.queryInfo)) {
setTimeout((): void => {
try {
api.tx.swork
.createGroup()
.paymentInfo(senderId)
.then(({ partialFee }): void => {
const maxTransfer = balances.availableBalance.sub(partialFee);
setMaxTransfer(
maxTransfer.gt(api.consts.balances.existentialDeposit)
? maxTransfer
: null
);
})
.catch(console.error);
} catch (error) {
console.error((error as Error).message);
}
}, 0);
} else {
setMaxTransfer(null);
}
}, [api, balances, senderId]);
return (
<Modal
className='app--accounts-Modal'
header={t<string>('Group create')}
size='large'
>
<Modal.Content>
<div className={className}>
<Modal.Content>
<Modal.Columns hint={t<string>('The transferred balance will be subtracted (along with fees) from the sender account.')}>
<InputAddress
defaultValue={propSenderId}
filter={stashes}
help={t<string>('The account you will register')}
isDisabled={!!propSenderId}
label={t<string>('send from account')}
labelExtra={
<Available
label={t<string>('transferrable')}
params={senderId}
/>
}
onChange={setSenderId}
type='account'
/>
</Modal.Columns>
</Modal.Content>
</div>
</Modal.Content>
<Modal.Actions onCancel={onClose}>
<TxButton
accountId={senderId}
icon='paper-plane'
isDisabled={!hasAvailable || !amount}
label={t<string>('Create')}
onStart={onClose}
onSuccess={onSuccess}
tx={api.tx.swork.createGroup}
/>
</Modal.Actions>
</Modal>
);
}
Example #13
Source File: TxButton.tsx From subscan-multisig-react with Apache License 2.0 | 4 votes |
function TxButton({
accountId,
className = '',
extrinsic: propsExtrinsic,
icon,
isBasic,
isBusy,
isDisabled,
isIcon,
isToplevel,
isUnsigned,
label,
onClick,
onFailed,
onSendRef,
onStart,
onSuccess,
onUpdate,
params,
tooltip,
tx,
withSpinner,
withoutLink,
}: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const mountedRef = useIsMountedRef();
const { queueExtrinsic } = useContext(StatusContext);
const [isSending, setIsSending] = useState(false);
const [isStarted, setIsStarted] = useState(false);
useEffect((): void => {
// eslint-disable-next-line
isStarted && onStart && onStart();
}, [isStarted, onStart]);
const _onFailed = useCallback(
(result: SubmittableResult | null): void => {
// eslint-disable-next-line
mountedRef.current && setIsSending(false);
// eslint-disable-next-line
onFailed && onFailed(result);
},
[onFailed, setIsSending, mountedRef]
);
const _onSuccess = useCallback(
(result: SubmittableResult): void => {
// eslint-disable-next-line
mountedRef.current && setIsSending(false);
// eslint-disable-next-line
onSuccess && onSuccess(result);
},
[onSuccess, setIsSending, mountedRef]
);
const _onStart = useCallback((): void => {
// eslint-disable-next-line
mountedRef.current && setIsStarted(true);
}, [setIsStarted, mountedRef]);
const _onSend = useCallback((): void => {
let extrinsics: SubmittableExtrinsic<'promise'>[] | undefined;
if (propsExtrinsic) {
extrinsics = Array.isArray(propsExtrinsic) ? propsExtrinsic : [propsExtrinsic];
} else if (tx) {
extrinsics = [tx(...(isFunction(params) ? params() : params || []))];
}
assert(extrinsics?.length, 'Expected generated extrinsic passed to TxButton');
// eslint-disable-next-line
mountedRef.current && withSpinner && setIsSending(true);
extrinsics.forEach((extrinsic): void => {
queueExtrinsic({
accountId: accountId && accountId.toString(),
extrinsic,
isUnsigned,
txFailedCb: withSpinner ? _onFailed : onFailed,
txStartCb: _onStart,
txSuccessCb: withSpinner ? _onSuccess : onSuccess,
txUpdateCb: onUpdate,
});
});
// eslint-disable-next-line
onClick && onClick();
}, [
_onFailed,
_onStart,
_onSuccess,
accountId,
isUnsigned,
onClick,
onFailed,
onSuccess,
onUpdate,
params,
propsExtrinsic,
queueExtrinsic,
setIsSending,
tx,
withSpinner,
mountedRef,
]);
if (onSendRef) {
onSendRef.current = _onSend;
}
return (
<Button
className={className}
icon={icon || 'check'}
isBasic={isBasic}
isBusy={isBusy}
isDisabled={
isSending ||
isDisabled ||
(!isUnsigned && !accountId) ||
(tx ? false : Array.isArray(propsExtrinsic) ? propsExtrinsic.length === 0 : !propsExtrinsic)
}
isIcon={isIcon}
isToplevel={isToplevel}
label={label || (isIcon ? '' : t<string>('Submit'))}
onClick={_onSend}
tooltip={tooltip}
withoutLink={withoutLink}
/>
);
}
Example #14
Source File: index.tsx From crust-apps with Apache License 2.0 | 4 votes |
function Payouts ({ className = '', isInElection, ownValidators }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const [hasOwnValidators] = useState(() => ownValidators.length !== 0);
const [myStashesIndex, setMyStashesIndex] = useState(() => (isFunction(api.tx.staking.rewardStakers) && hasOwnValidators) ? 0 : 1);
const [eraSelectionIndex, setEraSelectionIndex] = useState(0);
const eraLength = useCall<BN>(api.derive.session.eraLength);
const historyDepth = useCall<BN>(api.query.staking.historyDepth);
const stakerPayoutsAfter = useStakerPayouts();
const isDisabled = isInElection || !isFunction(api.tx.utility?.batch);
const eraSelection = useMemo(
() => getOptions(api, eraLength, historyDepth, t),
[api, eraLength, historyDepth, t]
);
const { allRewards, isLoadingRewards } = useOwnEraRewards(eraSelection[eraSelectionIndex].value, myStashesIndex ? undefined : ownValidators);
const { stashTotal, stashes, validators } = useMemo(
() => getAvailable(allRewards, stakerPayoutsAfter),
[allRewards, stakerPayoutsAfter]
);
const headerStashes = useMemo(() => [
[myStashesIndex ? t('payout/stash') : t('overall/validator'), 'start', 2],
[t('eras'), 'start'],
// [t('available')],
[('remaining')],
[undefined, undefined, 4]
], [myStashesIndex, t]);
const headerValidatorsRef = useRef([
[t('payout/validator'), 'start', 2],
[t('eras'), 'start'],
// [t('available')],
[('remaining')],
[undefined, undefined, 3]
]);
const valOptions = useMemo(() => [
{ isDisabled: !hasOwnValidators, text: t('Validator/Candidate rewards'), value: 'val' },
{ text: t('Guarantor rewards'), value: 'all' }
], [hasOwnValidators, t]);
const footer = useMemo(() => (
<tr>
<td colSpan={3} />
<td className='number'>
{/* {stashTotal && <FormatBalance value={stashTotal} />} */}
</td>
<td colSpan={4} />
</tr>
), [stashTotal]);
const hasPayouts = isFunction(api.tx.staking.rewardStakers);
return (
<div className={className}>
{api.tx.staking.rewardStakers && (
<Button.Group>
<ToggleGroup
onChange={setMyStashesIndex}
options={valOptions}
value={myStashesIndex}
/>
<ToggleGroup
onChange={setEraSelectionIndex}
options={eraSelection}
value={eraSelectionIndex}
/>
<PayButton
isAll
isDisabled={isDisabled}
payout={validators}
/>
</Button.Group>
)}
<ElectionBanner isInElection={isInElection} />
{hasPayouts && !isLoadingRewards && !stashes?.length && (
<article className='warning centered'>
<p>{t('Payouts of rewards for a validator can be initiated by any account. This means that as soon as a validator or nominator requests a payout for an era, all the nominators for that validator will be rewarded. Each user does not need to claim individually and the suggestion is that validators should claim rewards for everybody as soon as an era ends.')}</p>
<p>{t('If you have not claimed rewards straight after the end of the era, the validator is in the active set and you are seeing no rewards, this would mean that the reward payout transaction was made by another account on your behalf. Always check your favorite explorer to see any historic payouts made to your accounts.')}</p>
</article>
)}
<Table
empty={!isLoadingRewards && stashes && t<string>('No pending payouts for your stashes')}
emptySpinner={t<string>('Retrieving info for the selected eras, this will take some time')}
footer={footer}
header={headerStashes}
isFixed
>
{!isLoadingRewards && stashes?.map((payout): React.ReactNode => (
<Stash
isDisabled={isDisabled}
key={payout.stashId}
payout={payout}
stakerPayoutsAfter={stakerPayoutsAfter}
/>
))}
</Table>
{hasPayouts && (myStashesIndex === 1) && !isLoadingRewards && validators && (validators.length !== 0) && (
<Table
header={headerValidatorsRef.current}
isFixed
>
{!isLoadingRewards && validators.map((payout): React.ReactNode => (
<Validator
isDisabled={isDisabled}
key={payout.validatorId}
payout={payout}
/>
))}
</Table>
)}
</div>
);
}
Example #15
Source File: Input.tsx From subscan-multisig-react with Apache License 2.0 | 4 votes |
function Input({
autoFocus = false,
children,
className,
defaultValue,
help,
icon,
inputClassName,
isAction = false,
isDisabled = false,
isDisabledError = false,
isEditable = false,
isError = false,
isFull = false,
isHidden = false,
isInPlaceEditor = false,
isReadOnly = false,
isWarning = false,
label,
labelExtra,
max,
maxLength,
min,
name,
onBlur,
onChange,
onEnter,
onEscape,
onKeyDown,
onKeyUp,
onPaste,
placeholder,
tabIndex,
type = 'text',
value,
withEllipsis,
withLabel,
}: Props): React.ReactElement<Props> {
const [stateName] = useState(() => `in_${counter++}_at_${Date.now()}`);
const _onBlur = useCallback(() => onBlur && onBlur(), [onBlur]);
const _onChange = useCallback(
({ target }: React.SyntheticEvent<HTMLInputElement>): void =>
onChange && onChange((target as HTMLInputElement).value),
[onChange]
);
const _onKeyDown = useCallback(
(event: React.KeyboardEvent<HTMLInputElement>): void => onKeyDown && onKeyDown(event),
[onKeyDown]
);
const _onKeyUp = useCallback(
(event: React.KeyboardEvent<HTMLInputElement>): void => {
onKeyUp && onKeyUp(event);
if (onEnter && event.keyCode === 13) {
(event.target as HTMLInputElement).blur();
isFunction(onEnter) && onEnter();
}
if (onEscape && event.keyCode === 27) {
(event.target as HTMLInputElement).blur();
onEscape();
}
},
[onEnter, onEscape, onKeyUp]
);
const _onPaste = useCallback(
(event: React.ClipboardEvent<HTMLInputElement>): void => onPaste && onPaste(event),
[onPaste]
);
return (
<Labelled
className={className}
help={help}
isFull={isFull}
label={label}
labelExtra={labelExtra}
withEllipsis={withEllipsis}
withLabel={withLabel}
>
<SUIInput
action={isAction}
autoFocus={autoFocus}
className={[
isEditable ? 'ui--Input edit icon' : 'ui--Input',
isInPlaceEditor ? 'inPlaceEditor' : '',
inputClassName || '',
isWarning && !isError ? 'isWarning' : '',
].join(' ')}
defaultValue={isUndefined(value) ? defaultValue || '' : undefined}
disabled={isDisabled}
error={(!isDisabled && isError) || isDisabledError}
hidden={isHidden}
iconPosition={isUndefined(icon) ? undefined : 'left'}
id={name}
max={max}
maxLength={maxLength}
min={min}
name={name || stateName}
onBlur={_onBlur}
onChange={_onChange}
onKeyDown={_onKeyDown}
onKeyUp={_onKeyUp}
placeholder={placeholder}
readOnly={isReadOnly}
tabIndex={tabIndex}
type={type}
value={value}
>
<input
autoCapitalize="off"
autoComplete={type === 'password' ? 'new-password' : 'off'}
autoCorrect="off"
data-testid={label}
onPaste={_onPaste}
spellCheck={false}
/>
{isEditable && <i className="edit icon" />}
{icon}
{children}
</SUIInput>
</Labelled>
);
}
Example #16
Source File: RebondFounds.tsx From crust-apps with Apache License 2.0 | 4 votes |
function RebondFounds ({ className = '', foundsType, onClose, onSuccess, senderId: propSenderId }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const [amount, setAmount] = useState<BN | undefined>(BN_ZERO);
const [hasAvailable] = useState(true);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [, setMaxTransfer] = useState<BN | null>(null);
const [senderId, setSenderId] = useState<string | null>(propSenderId || null);
const balances = useCall<DeriveBalancesAll>(api.derive.balances.all, [senderId]);
useEffect((): void => {
if (balances && balances.accountId.eq(senderId) && senderId && isFunction(api.rpc.payment?.queryInfo)) {
setTimeout((): void => {
try {
api.tx.benefits
.rebondBenefitFunds(balances.availableBalance, foundsType)
.paymentInfo(senderId)
.then(({ partialFee }): void => {
const maxTransfer = balances.availableBalance.sub(partialFee);
setMaxTransfer(
maxTransfer.gt(api.consts.balances.existentialDeposit)
? maxTransfer
: null
);
})
.catch(console.error);
} catch (error) {
console.error((error as Error).message);
}
}, 0);
} else {
setMaxTransfer(null);
}
}, [api, balances, senderId]);
return (
<Modal
className='app--accounts-Modal'
header={t<string>('Rebond')}
size='large'
>
<Modal.Content>
<div className={className}>
<Modal.Content>
<Modal.Columns hint={t<string>('The transferred balance will be subtracted (along with fees) from the sender account.')}>
<InputAddress
defaultValue={propSenderId}
help={t<string>('The account you will register')}
isDisabled={!!propSenderId}
label={t<string>('send from account')}
labelExtra={
<Available
label={t<string>('transferrable')}
params={senderId}
/>
}
onChange={setSenderId}
type='account'
/>
</Modal.Columns>
</Modal.Content>
<Modal.Content>
<Modal.Columns>
{<InputBalance
autoFocus
help={t<string>('Type the amount you want to transfer. Note that you can select the unit on the right e.g sending 1 milli is equivalent to sending 0.001.')}
isError={!hasAvailable}
isZeroable
label={t<string>('amount')}
onChange={setAmount}
/>
}
</Modal.Columns>
</Modal.Content>
</div>
</Modal.Content>
<Modal.Actions onCancel={onClose}>
<TxButton
accountId={propSenderId || senderId}
icon='paper-plane'
isDisabled={!hasAvailable || !amount}
label={t<string>('Rebond')}
onStart={onClose}
onSuccess={onSuccess}
params={[amount, foundsType]}
tx={api.tx.benefits.rebondBenefitFunds}
/>
</Modal.Actions>
</Modal>
);
}
Example #17
Source File: WalletState.tsx From subscan-multisig-react with Apache License 2.0 | 4 votes |
// eslint-disable-next-line complexity
export function WalletState() {
const { t } = useTranslation();
const history = useHistory();
const { network, api, networkConfig } = useApi();
const {
multisigAccount,
setMultisigAccount,
inProgress,
queryInProgress,
confirmedAccount,
refreshConfirmedAccount,
} = useMultisigContext();
const [isAccountsDisplay, setIsAccountsDisplay] = useState<boolean>(false);
const [isExtrinsicDisplay, setIsExtrinsicDisplay] = useState(false);
const [isTransferDisplay, setIsTransferDisplay] = useState(false);
const isExtensionAccount = useIsInjected();
const [renameModalVisible, setRenameModalVisible] = useState(false);
const [renameInput, setRenameInput] = useState('');
const [deleteModalVisible, setDeleteModalVisible] = useState(false);
const { supportSubql, mainColor } = useMemo(() => {
return {
supportSubql: !!networkConfig?.api?.subql,
mainColor: getThemeColor(network),
};
}, [network, networkConfig]);
const showTransferButton = useMemo(() => {
return (
isFunction(api?.tx?.balances?.transfer) &&
isFunction(api?.tx?.balances?.transferKeepAlive) &&
isFunction(api?.tx?.balances?.transferAll)
);
}, [api]);
const states = useMemo<{ label: string; count: number | undefined }[]>(() => {
const res = [];
res.push({
label: 'multisig.In Progress',
count: inProgress.length,
});
if (supportSubql) {
res.push({ label: 'multisig.Confirmed Extrinsic', count: confirmedAccount });
}
res.push(
{
label: 'multisig.Threshold',
count: multisigAccount?.meta.threshold as number,
},
{
label: 'multisig.Members',
// eslint-disable-next-line @typescript-eslint/no-explicit-any
count: (multisigAccount?.meta.who as any)?.length as number,
}
);
return res;
}, [inProgress.length, confirmedAccount, multisigAccount?.meta.threshold, multisigAccount?.meta.who, supportSubql]);
const renameWallet = useCallback(
({ name }: { name: string }) => {
try {
const pair = keyring.getPair(multisigAccount?.address as string);
keyring.saveAccountMeta(pair, {
name,
whenEdited: Date.now(),
});
message.success(t('success'));
setRenameModalVisible(false);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const { meta, ...others } = multisigAccount!;
if (setMultisigAccount) {
setMultisigAccount({
...others,
meta: {
...meta,
name,
},
});
}
} catch (error: unknown) {
if (error instanceof Error) {
message.error(error.message);
}
}
},
[multisigAccount, t, setMultisigAccount]
);
const deleteWallet = useCallback(() => {
try {
keyring.forgetAccount(multisigAccount?.address as string);
message.success(t('success'));
history.push('/' + history.location.hash);
} catch (error: unknown) {
if (error instanceof Error) {
message.error(error.message);
}
}
}, [multisigAccount?.address, t, history]);
useEffect(() => {
const id = setInterval(() => refreshConfirmedAccount(), LONG_DURATION);
return () => clearInterval(id);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const exportAccountConfig = () => {
if (!multisigAccount) {
return;
}
const config = {
name: multisigAccount.meta.name,
members: multisigAccount.meta.addressPair as KeyringJson[],
threshold: multisigAccount.meta.threshold,
scope: getMultiAccountScope(multisigAccount.publicKey),
};
const blob = new Blob([JSON.stringify(config)], { type: 'text/plain;charset=utf-8' });
saveAs(blob, `${multisigAccount.address}.json`);
};
const menu = (
<Menu>
{/* <Menu.Item key="0">
<a href="https://www.antgroup.com">View in Subscan</a>
</Menu.Item> */}
<Menu.Item key="1" onClick={exportAccountConfig}>
Export config
</Menu.Item>
<Menu.Item
key="3"
onClick={() => {
setRenameInput(multisigAccount?.meta.name || '');
setRenameModalVisible(true);
}}
>
Rename
</Menu.Item>
<Menu.Item key="4" onClick={() => setDeleteModalVisible(true)}>
Delete
</Menu.Item>
</Menu>
);
return (
<Space direction="vertical" className="w-full">
<div className="flex flex-col md:flex-row items-start md:items-center md:justify-between">
<div className="self-stretch md:self-center">
<div className="flex items-center gap-4 md:w-auto w-full">
<Text className="whitespace-nowrap font-semibold text-xl leading-none" style={{ color: mainColor }}>
{multisigAccount?.meta.name}
</Text>
<Dropdown overlay={menu} trigger={['click']} placement="bottomCenter">
<SettingOutlined
className="rounded-full opacity-60 cursor-pointer p-1"
style={{
color: mainColor,
backgroundColor: mainColor + '40',
}}
onClick={(e) => e.preventDefault()}
/>
</Dropdown>
</div>
<div className="flex flex-col md:flex-row md:items-center gap-4 md:w-auto w-full mt-2 mb-4 md:mb-0">
<SubscanLink address={multisigAccount?.address} copyable></SubscanLink>
</div>
</div>
{((multisigAccount?.meta.addressPair as KeyringJson[]) || []).some((pair) =>
isExtensionAccount(pair.address)
) && (
<div className="flex items-center mt-2 md:mt-0">
{showTransferButton && (
<Button
onClick={() => setIsTransferDisplay(true)}
type="default"
size="large"
className="w-full md:w-auto mt-4 md:mt-0 mr-4"
style={{ color: mainColor }}
>
{t('transfer')}
</Button>
)}
<Button
onClick={() => setIsExtrinsicDisplay(true)}
type="primary"
size="large"
className="w-full md:w-auto mt-4 md:mt-0"
>
{t('submit_extrinsic')}
</Button>
</div>
)}
</div>
<div style={{ height: '1px', backgroundColor: mainColor, opacity: 0.1 }} className="mt-2" />
<Space size="middle" className="items-center hidden md:flex mt-2">
{states.map((state, index) => (
<Space key={index}>
<b>{t(state.label)}</b>
<span>{state.count}</span>
</Space>
))}
<div
style={{ border: 'solid 1px #DBDBDB', transform: isAccountsDisplay ? 'rotate(180deg)' : '' }}
className="w-12 h-6 flex items-center justify-center rounded-md cursor-pointer"
onClick={() => setIsAccountsDisplay(!isAccountsDisplay)}
>
<img src={iconDown} />
</div>
</Space>
<div className="grid md:hidden grid-cols-2">
{states.map((state, index) => (
<Statistic title={t(state.label)} value={state.count} key={index} className="text-center" />
))}
<Button type="ghost" className="col-span-2" onClick={() => setIsAccountsDisplay(!isAccountsDisplay)}>
{t(isAccountsDisplay ? 'wallet:Hide members' : 'wallet:Show members')}
</Button>
</div>
{isAccountsDisplay && multisigAccount && <Members record={multisigAccount} />}
<Modal
title={t('submit_extrinsic')}
visible={isExtrinsicDisplay}
onCancel={() => setIsExtrinsicDisplay(false)}
style={{ minWidth: 800 }}
footer={null}
destroyOnClose
>
<ExtrinsicLaunch
onTxSuccess={() => {
setIsExtrinsicDisplay(false);
queryInProgress();
}}
/>
</Modal>
{isTransferDisplay && (
<Transfer
key="modal-transfer"
onClose={() => setIsTransferDisplay(false)}
senderId={multisigAccount?.address}
onTxSuccess={() => {
setIsTransferDisplay(false);
queryInProgress();
}}
/>
)}
<Modal
title={null}
footer={null}
visible={renameModalVisible}
destroyOnClose
onCancel={() => setRenameModalVisible(false)}
bodyStyle={{
paddingLeft: '80px',
paddingRight: '80px',
paddingBottom: '60px',
}}
>
<div className="text-center text-black-800 text-xl font-semibold leading-none">Rename</div>
<div className="text-sm text-black-800 font-semibold mt-6 mb-1">Name</div>
<Input
value={renameInput}
onChange={(e) => {
setRenameInput(e.target.value);
}}
/>
<Row gutter={16} className="mt-5">
<Col span={12}>
<Button
block
type="primary"
onClick={() => {
if (!renameInput.trim()) {
return;
}
renameWallet({ name: renameInput });
}}
>
OK
</Button>
</Col>
<Col span={12}>
<Button
block
style={{
color: mainColor,
}}
onClick={() => {
setRenameModalVisible(false);
}}
>
Cancel
</Button>
</Col>
</Row>
</Modal>
<ConfirmDialog
visible={deleteModalVisible}
onCancel={() => setDeleteModalVisible(false)}
title="Delete Wallet"
content={`Are you sure to delete “${multisigAccount?.meta.name}” ?`}
onConfirm={deleteWallet}
/>
</Space>
);
}