Files
pezkuwi-apps/packages/react-signer/src/PaymentInfo.tsx
T
Satoshi Qazi Muhammed 45d479d2ec Move to @pezkuwi/dev 0.86.4 (eslint 10 toolchain)
- 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).
2026-10-10 02:06:12 +04:00

99 lines
3.7 KiB
TypeScript

// Copyright 2017-2026 @pezkuwi/react-signer authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { SubmittableExtrinsic } from '@pezkuwi/api/promise/types';
import type { DeriveBalancesAll } from '@pezkuwi/api-derive/types';
import type { RuntimeDispatchInfo } from '@pezkuwi/types/interfaces';
import type { ExtendedSignerOptions } from './types.js';
import React, { useEffect, useState } from 'react';
import { Trans } from 'react-i18next';
import { Expander, MarkWarning } from '@pezkuwi/react-components';
import { useApi, useCall, useIsMountedRef } from '@pezkuwi/react-hooks';
import { BN, formatBalance, nextTick } from '@pezkuwi/util';
import { useTranslation } from './translate.js';
interface Props {
accountId?: string | null;
className?: string;
extrinsic?: SubmittableExtrinsic | null;
isHeader?: boolean;
onChange?: (hasAvailable: boolean) => void;
tip?: BN;
signerOptions: ExtendedSignerOptions;
}
function PaymentInfo ({ accountId, className = '', extrinsic, isHeader, signerOptions }: 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 && extrinsic.hasPaymentInfo &&
nextTick(async (): Promise<void> => {
setDispatchInfo(null);
try {
const info = await extrinsic.paymentInfo(accountId, signerOptions);
if (signerOptions?.assetId) {
const convertedFee = new BN((await api.call.assetConversionApi.quotePriceTokensForExactTokens(
signerOptions?.assetId as any,
{
interior: 'Here',
parents: 1
} as any,
info.partialFee,
true
)).toString());
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
mountedRef.current && setDispatchInfo({ ...info, partialFee: convertedFee });
} else {
mountedRef.current && setDispatchInfo(info);
}
} catch (error) {
console.error(error);
}
});
}, [api, accountId, extrinsic, mountedRef, signerOptions]);
if (!dispatchInfo || !extrinsic) {
return null;
}
const isFeeError = api.consts.balances && !(api.tx.balances?.transferAllowDeath?.is(extrinsic) || api.tx.balances?.transfer?.is(extrinsic)) && balances?.accountId.eq(accountId) && (
(balances.transferable || balances.availableBalance).lte(dispatchInfo.partialFee) ||
balances.freeBalance.sub(dispatchInfo.partialFee).lte(api.consts.balances.existentialDeposit)
);
const feeSymbol = signerOptions?.feeAsset?.metadata.symbol.toHuman();
return (
<>
<Expander
className={className}
isHeader={isHeader}
summary={
<Trans i18nKey='feesForSubmission'>
Fees of <span className='highlight'>
{formatBalance(dispatchInfo.partialFee, { decimals: signerOptions?.feeAsset?.metadata.decimals.toNumber() ?? api.registry.chainDecimals.at(0), withSiFull: true }).split(' ').slice(0, -1).join(' ')}{' '}
{typeof feeSymbol === 'string' ? feeSymbol : api.registry.chainTokens.at(0)}
</span> will be applied to the submission
</Trans>
}
/>
{isFeeError && (
<MarkWarning content={t('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.')} />
)}
</>
);
}
export default React.memo(PaymentInfo);