// 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 | null { const { t } = useTranslation(); const { api } = useApi(); const [dispatchInfo, setDispatchInfo] = useState(null); const balances = useCall(api.derive.balances?.all, [accountId]); const mountedRef = useIsMountedRef(); useEffect((): void => { accountId && extrinsic && extrinsic.hasPaymentInfo && nextTick(async (): Promise => { 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 ( <> Fees of {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)} will be applied to the submission } /> {isFeeError && ( )} ); } export default React.memo(PaymentInfo);