AssetHub: Fee payment with any sufficient asset (#11229)
* feat: created useAssetIds and useAssetInfo hook * feat: created pay with asset component * feat: add paywith asset component with Transfer modal * fix: signer options as prop * fix: payment info * feat: created PayWithAsset provider and integrated with transfer form * chore: rename variable * fix: PayWithAsset provider wrapping * feat: integrate PayWithAsset context and enhance asset info fetching * fix: some build errors * refactor: update imports and simplify getFeeAssetLocation function * refactor: simplify component structure and update PayWithAsset import paths * fix: all build errors * fix: avoid redundant type * feat: added translation * feat: enhance fee payment info & fee asset handling * fix: translations ordering * fix: remove redundant types and hooks * chore: added statemine chain * refactor: generalize fee acceptance check for non-native assets * fix: remove constants and generalize palletIndex
This commit is contained in:
parent
6a94a5203d
commit
bdfcedae72
@ -95,6 +95,7 @@
|
||||
"Bond more": "رهن المزيد",
|
||||
"Bond more funds": "زيادة رهون الأموال",
|
||||
"Bonding Preferences": "تفضيلات الرهن",
|
||||
"By selecting this option, the transaction fee will be automatically deducted from the specified asset, ensuring a seamless and efficient payment process.": "من خلال تحديد هذا الخيار، سيتم خصم رسوم المعاملة تلقائيًا من الأصل المحدد، مما يضمن عملية دفع سلسة وفعالة.",
|
||||
"Call": "مكالمة",
|
||||
"Call a contract": "اتصل بعقد",
|
||||
"Call a method on this contract": "اتصل بطريقة على هذا العقد",
|
||||
@ -856,6 +857,7 @@
|
||||
"approval type": "نوع الاعتماد",
|
||||
"approved": "موافق عليه",
|
||||
"asset id": "تعريف الأصل",
|
||||
"asset to pay the fee": "الأصول لدفع الرسوم",
|
||||
"auto-selected targets for nomination": "أهداف محددة تلقائيًا للترشيح",
|
||||
"available": "متوفر",
|
||||
"available signatories": "الموقعون المتاحين",
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
"0.1x voting balance, no lockup period": "0.1x voting balance, no lockup period",
|
||||
"<unknown>": "<unknown>",
|
||||
"Add identity judgment": "Add identity judgment",
|
||||
"By selecting this option, the transaction fee will be automatically deducted from the specified asset, ensuring a seamless and efficient payment process.": "By selecting this option, the transaction fee will be automatically deducted from the specified asset, ensuring a seamless and efficient payment process.",
|
||||
"Cancel": "Cancel",
|
||||
"Confirm account removal": "Confirm account removal",
|
||||
"Confirm address removal": "Confirm address removal",
|
||||
@ -62,6 +63,7 @@
|
||||
"You are about to remove this contract from your list of available contracts. Once completed, should you need to access it again, you will have to manually add the contract's address in the Instantiate tab.": "You are about to remove this contract from your list of available contracts. Once completed, should you need to access it again, you will have to manually add the contract's address in the Instantiate tab.",
|
||||
"address copied": "address copied",
|
||||
"amount": "amount",
|
||||
"asset to pay the fee": "asset to pay the fee",
|
||||
"available to be unlocked": "available to be unlocked",
|
||||
"balance": "balance",
|
||||
"beneficiary": "beneficiary",
|
||||
|
||||
@ -138,6 +138,7 @@
|
||||
"Bonding Preferences": "",
|
||||
"Bounty approval under voting": "",
|
||||
"Bounty rejection under voting": "",
|
||||
"By selecting this option, the transaction fee will be automatically deducted from the specified asset, ensuring a seamless and efficient payment process.": "",
|
||||
"Call a contract": "",
|
||||
"Call results": "",
|
||||
"Calls": "",
|
||||
@ -1189,6 +1190,7 @@
|
||||
"asset id or name to query": "",
|
||||
"asset name": "",
|
||||
"asset symbol": "",
|
||||
"asset to pay the fee": "asset to pay the fee",
|
||||
"assets": "",
|
||||
"assignment": "",
|
||||
"at specific block": "",
|
||||
|
||||
@ -73,6 +73,7 @@
|
||||
"Bond more": "Vincular más",
|
||||
"Bond more funds": "Vincular más fondos",
|
||||
"Bonding Preferences": "Preferencias de la vinculación",
|
||||
"By selecting this option, the transaction fee will be automatically deducted from the specified asset, ensuring a seamless and efficient payment process.": "Al seleccionar esta opción, la tarifa de transacción se deducirá automáticamente del activo especificado, lo que garantiza un proceso de pago eficiente y sin inconvenientes.",
|
||||
"Call": "Llamada",
|
||||
"Call a contract": "Llamada a contrato",
|
||||
"Call a method on this contract": "Llamada a un método en este contrato",
|
||||
@ -714,6 +715,7 @@
|
||||
"approval type": "tipo de aprobación",
|
||||
"approved": "aprobado",
|
||||
"asset id": "id del activo",
|
||||
"asset to pay the fee": "asseto para pagar la tarifa",
|
||||
"assignment": "asignación",
|
||||
"auto-selected targets for nomination": "candidatos auto seleccionados para la nominación",
|
||||
"available": "disponible",
|
||||
|
||||
@ -107,6 +107,7 @@
|
||||
"Bond more": "Allouer davantage",
|
||||
"Bond more funds": "Allouer plus de fonds",
|
||||
"Bonding Preferences": "Préférences d'allocation",
|
||||
"By selecting this option, the transaction fee will be automatically deducted from the specified asset, ensuring a seamless and efficient payment process.": "En sélectionnant cette option, les frais de transaction seront automatiquement déduits de l'actif spécifié, garantissant un processus de paiement transparent et efficace.",
|
||||
"Call": "Appel",
|
||||
"Call a contract": "Appel d'un contrat",
|
||||
"Call a method on this contract": "Appel d'une méthode sur ce contrat",
|
||||
@ -976,6 +977,7 @@
|
||||
"approval type": "type d'approbation",
|
||||
"approved": "approuvé",
|
||||
"asset id": "identifiant de l'élément",
|
||||
"asset to pay the fee": "actif pour payer les frais",
|
||||
"auto-selected targets for nomination": "cibles sélectionnées automatiquement pour la nomination",
|
||||
"available": "disponible",
|
||||
"available signatories": "signataires disponibles",
|
||||
|
||||
@ -112,6 +112,7 @@
|
||||
"Bond more": "Tambah obligasi",
|
||||
"Bond more funds": "Tambah dana obligasi",
|
||||
"Bonding Preferences": "Preferensi obligasi",
|
||||
"By selecting this option, the transaction fee will be automatically deducted from the specified asset, ensuring a seamless and efficient payment process.": "Dengan memilih opsi ini, biaya transaksi akan secara otomatis dipotong dari aset yang ditentukan, memastikan proses pembayaran yang lancar dan efisien",
|
||||
"Call": "Panggil",
|
||||
"Call a contract": "Panggil kontrak",
|
||||
"Call a method on this contract": "Panggile metode dalam kontrak ini",
|
||||
@ -1014,6 +1015,7 @@
|
||||
"approval type": "tipe persetujuan",
|
||||
"approved": "Disetujui",
|
||||
"asset id": "id aset",
|
||||
"asset to pay the fee": "aset untuk membayar biayanya",
|
||||
"auto-selected targets for nomination": "target yang dipilih secara otomatis untuk nominasi",
|
||||
"available": "Tersedia",
|
||||
"available signatories": "penandatangan yang tersedia",
|
||||
|
||||
@ -134,6 +134,7 @@
|
||||
"Bounties": "Bounties",
|
||||
"Bounty approval under voting": "Approvazione del bounty in votazione",
|
||||
"Bounty rejection under voting": "Sospensione / Rifiuto del bounty in votazione",
|
||||
"By selecting this option, the transaction fee will be automatically deducted from the specified asset, ensuring a seamless and efficient payment process.": "Selezionando questa opzione, la commissione di transazione verrà automaticamente detratta dall'asset specificato, garantendo un processo di pagamento fluido ed efficiente",
|
||||
"Call": "Chiama",
|
||||
"Call a contract": "Richiama un contratto",
|
||||
"Call a method on this contract": "Richiama un metodo su questo contratto",
|
||||
@ -1266,6 +1267,7 @@
|
||||
"asset id": "asset id",
|
||||
"asset name": "nome asset",
|
||||
"asset symbol": "simbolo asset",
|
||||
"asset to pay the fee": "bene per pagare la tassa",
|
||||
"assets": "assets",
|
||||
"auctions": "aste",
|
||||
"auto-selected targets for nomination": "target auto-selezionati per la nomina",
|
||||
|
||||
@ -45,6 +45,7 @@
|
||||
"Aye, I approve": "はい、承認します",
|
||||
"Backup account": "バックアップアカウント",
|
||||
"Block details": "ブロックの詳細",
|
||||
"By selecting this option, the transaction fee will be automatically deducted from the specified asset, ensuring a seamless and efficient payment process.": "このオプションを選択すると、取引手数料が指定された資産から自動的に差し引かれ、シームレスで効率的な支払いプロセスが保証されます。",
|
||||
"Call": "コール",
|
||||
"Call a contract": "コントラクトをコールする",
|
||||
"Call a method on this contract": "このコントラクトのメソッドをコールする",
|
||||
@ -362,6 +363,7 @@
|
||||
"amount": "量",
|
||||
"approval type": "承認のタイプ",
|
||||
"asset id": "アセットid",
|
||||
"asset to pay the fee": "料金を支払うための資産",
|
||||
"available signatories": "利用可能な署名",
|
||||
"available social recovery helpers": "利用可能なソーシャルリカバリーヘルパー",
|
||||
"aye: bool": "はい: bool",
|
||||
|
||||
@ -104,6 +104,7 @@
|
||||
"Bond more": "본드를 더하다",
|
||||
"Bond more funds": "추가적인 펀드를 본드하다",
|
||||
"Bonding Preferences": "선호하는 본딍",
|
||||
"By selecting this option, the transaction fee will be automatically deducted from the specified asset, ensuring a seamless and efficient payment process.": "이 옵션을 선택하면 거래 수수료가 지정된 자산에서 자동으로 공제되어 원활하고 효율적인 결제 프로세스가 보장됩니다.",
|
||||
"Call a contract": "계약서를 콜하다",
|
||||
"Call results": "결과를 콜하다",
|
||||
"Cancel": "취소",
|
||||
@ -948,6 +949,7 @@
|
||||
"approval type": "승인 형식",
|
||||
"approved": "승인된",
|
||||
"asset id": "자산 id",
|
||||
"asset to pay the fee": "수수료를 지불할 자산",
|
||||
"available": "가용",
|
||||
"available signatories": "현재 서명이 가능한 서명인",
|
||||
"available social recovery helpers": "현재 도움을 줄 수 있는 \"계정 복구 도우미\"",
|
||||
|
||||
@ -70,6 +70,7 @@
|
||||
"Bond more": "Vincular mais",
|
||||
"Bond more funds": "Vincular mais fundos",
|
||||
"Bonding Preferences": "Preferências de vínculação",
|
||||
"By selecting this option, the transaction fee will be automatically deducted from the specified asset, ensuring a seamless and efficient payment process.": "Ao selecionar esta opção, as taxas de transação serão automaticamente deduzidas dos ativos especificados, garantindo um processo de pagamento contínuo e eficiente.",
|
||||
"Call": "Chamar",
|
||||
"Call a contract": "Chamar um contrato",
|
||||
"Call a method on this contract": "Chamar um método neste contrato",
|
||||
@ -712,6 +713,7 @@
|
||||
"approval type": "tipo de aprovação",
|
||||
"approved": "aprovado",
|
||||
"asset id": "ID do recurso",
|
||||
"asset to pay the fee": "ativo para pagar a taxa",
|
||||
"auto-selected targets for nomination": "destinos selecionados automaticamente para nomeação",
|
||||
"available": "acessível",
|
||||
"available signatories": "signatários disponíveis",
|
||||
|
||||
@ -101,6 +101,7 @@
|
||||
"Bonding Preferences": "Настройки депонирования",
|
||||
"Bounty approval under voting": "Утверждение награды находится в обсуждении",
|
||||
"Bounty rejection under voting": "Отмена награды в обсеждении",
|
||||
"By selecting this option, the transaction fee will be automatically deducted from the specified asset, ensuring a seamless and efficient payment process.": "При выборе этой опции комиссия за транзакцию будет автоматически вычтена из указанного актива, обеспечивая беспрепятственный и эффективный процесс оплаты.",
|
||||
"Call": "Вызвать",
|
||||
"Call a contract": "Вызвать контракт",
|
||||
"Call a method on this contract": "Вызвать метод этого контракта",
|
||||
@ -901,6 +902,7 @@
|
||||
"asset id": "№ актива",
|
||||
"asset name": "имя средства",
|
||||
"asset symbol": "символ средства",
|
||||
"asset to pay the fee": "актив для оплаты пошлины",
|
||||
"assets": "средства",
|
||||
"auto-selected targets for nomination": "автоматически выбранные валидаторы",
|
||||
"available": "доступный",
|
||||
|
||||
@ -99,6 +99,7 @@
|
||||
"Bond more": "绑定更多",
|
||||
"Bond more funds": "绑定更多资金",
|
||||
"Bonding Preferences": "绑定偏好设置",
|
||||
"By selecting this option, the transaction fee will be automatically deducted from the specified asset, ensuring a seamless and efficient payment process.": "通过选择此选项,交易费用将从指定资产中自动扣除,确保支付流程无缝高效。",
|
||||
"Call": "调用",
|
||||
"Call a contract": "调用一个合约",
|
||||
"Call a method on this contract": "在合约上调用一个方法",
|
||||
@ -927,6 +928,7 @@
|
||||
"approved": "已赞成",
|
||||
"asset id": "资产 id",
|
||||
"asset id or name to query": "要查询的资产id或名称",
|
||||
"asset to pay the fee": "支付費用的資產",
|
||||
"auto-selected targets for nomination": "自动选择的提名对象",
|
||||
"available": "可用的",
|
||||
"available signatories": "可用签署人",
|
||||
|
||||
@ -9,7 +9,7 @@ import { HashRouter } from 'react-router-dom';
|
||||
import { ThemeProvider } from 'styled-components';
|
||||
|
||||
import { ApiCtxRoot } from '@polkadot/react-api';
|
||||
import { ApiStatsCtxRoot, BlockAuthorsCtxRoot, BlockEventsCtxRoot, KeyringCtxRoot, QueueCtxRoot, WindowSizeCtxRoot } from '@polkadot/react-hooks';
|
||||
import { ApiStatsCtxRoot, BlockAuthorsCtxRoot, BlockEventsCtxRoot, KeyringCtxRoot, PayWithAssetCtxRoot, QueueCtxRoot, WindowSizeCtxRoot } from '@polkadot/react-hooks';
|
||||
import { settings } from '@polkadot/ui-settings';
|
||||
|
||||
import Apps from './Apps.js';
|
||||
@ -53,7 +53,9 @@ function Root ({ isElectron, store }: Props): React.ReactElement<Props> {
|
||||
<BlockEventsCtxRoot>
|
||||
<HashRouter>
|
||||
<WindowSizeCtxRoot>
|
||||
<Apps />
|
||||
<PayWithAssetCtxRoot>
|
||||
<Apps />
|
||||
</PayWithAssetCtxRoot>
|
||||
</WindowSizeCtxRoot>
|
||||
</HashRouter>
|
||||
</BlockEventsCtxRoot>
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { DropdownItemProps } from 'semantic-ui-react';
|
||||
import type { AssetInfo, AssetInfoComplete } from '../types.js';
|
||||
import type { AssetInfo, AssetInfoComplete } from '@polkadot/react-hooks/types';
|
||||
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
// Copyright 2017-2025 @polkadot/app-assets authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { AssetInfo } from '../types.js';
|
||||
import type { AssetInfo } from '@polkadot/react-hooks/types';
|
||||
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
// Copyright 2017-2025 @polkadot/app-assets authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { AssetInfo } from '../types.js';
|
||||
import type { AssetInfo } from '@polkadot/react-hooks/types';
|
||||
|
||||
import React, { useRef } from 'react';
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2017-2025 @polkadot/app-assets authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { AssetInfo } from '@polkadot/react-hooks/types';
|
||||
import type { BN } from '@polkadot/util';
|
||||
import type { AssetInfo } from '../types.js';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
|
||||
|
||||
@ -12,14 +12,12 @@ import { Route, Routes } from 'react-router';
|
||||
|
||||
import { getGenesis } from '@polkadot/apps-config';
|
||||
import { Tabs } from '@polkadot/react-components';
|
||||
import { useAccounts, useApi } from '@polkadot/react-hooks';
|
||||
import { useAccounts, useApi, useAssetIds, useAssetInfos } from '@polkadot/react-hooks';
|
||||
import { BN_ONE } from '@polkadot/util';
|
||||
|
||||
import Balances from './Balances/index.js';
|
||||
import Overview from './Overview/index.js';
|
||||
import { useTranslation } from './translate.js';
|
||||
import useAssetIds from './useAssetIds.js';
|
||||
import useAssetInfos from './useAssetInfos.js';
|
||||
|
||||
interface Props {
|
||||
basePath: string;
|
||||
|
||||
@ -1,21 +0,0 @@
|
||||
// Copyright 2017-2025 @polkadot/app-assets authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { PalletAssetsAssetDetails, PalletAssetsAssetMetadata } from '@polkadot/types/lookup';
|
||||
import type { BN } from '@polkadot/util';
|
||||
|
||||
export interface AssetInfo {
|
||||
details: PalletAssetsAssetDetails | null;
|
||||
id: BN;
|
||||
isAdminMe: boolean;
|
||||
isIssuerMe: boolean;
|
||||
isFreezerMe: boolean;
|
||||
isOwnerMe: boolean;
|
||||
key: string;
|
||||
metadata: PalletAssetsAssetMetadata | null;
|
||||
}
|
||||
|
||||
export interface AssetInfoComplete extends AssetInfo {
|
||||
details: PalletAssetsAssetDetails;
|
||||
metadata: PalletAssetsAssetMetadata;
|
||||
}
|
||||
@ -1,8 +1,7 @@
|
||||
// Copyright 2017-2025 @polkadot/react-components authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Judgement } from '@polkadot/react-hooks/types';
|
||||
import type { DisplayedJudgement } from '../types.js';
|
||||
import type { DisplayedJudgement, Judgement } from '../types.js';
|
||||
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
|
||||
61
packages/react-components/src/PayWithAsset.tsx
Normal file
61
packages/react-components/src/PayWithAsset.tsx
Normal file
@ -0,0 +1,61 @@
|
||||
// Copyright 2017-2025 @polkadot/react-components authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { DropdownItemProps } from 'semantic-ui-react';
|
||||
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import { Dropdown } from '@polkadot/react-components';
|
||||
import { useApi, usePayWithAsset } from '@polkadot/react-hooks';
|
||||
import { BN } from '@polkadot/util';
|
||||
|
||||
import { useTranslation } from './translate.js';
|
||||
|
||||
const PayWithAsset = () => {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const [selectedAssetValue, setSelectedAssetValue] = useState('0');
|
||||
|
||||
const { assetOptions, isDisabled, onChange } = usePayWithAsset();
|
||||
|
||||
const nativeAsset = useMemo(
|
||||
() => api.registry.chainTokens[0],
|
||||
[api]
|
||||
);
|
||||
|
||||
const onSearch = useCallback(
|
||||
(options: DropdownItemProps[], value: string): DropdownItemProps[] =>
|
||||
options.filter((options) => {
|
||||
const { text: optText, value: optValue } = options as { text: string, value: number };
|
||||
|
||||
return parseInt(value) === optValue || optText.includes(value);
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
||||
const onSelect = useCallback((value: string) => {
|
||||
onChange(value === nativeAsset ? new BN(-1) : new BN(value), () => setSelectedAssetValue(value));
|
||||
}, [nativeAsset, onChange]);
|
||||
|
||||
useEffect((): void => {
|
||||
const info = assetOptions.find(({ value }) => value === selectedAssetValue);
|
||||
|
||||
// if no info found (usually happens on first load), select the first one automatically
|
||||
if (!info) {
|
||||
setSelectedAssetValue(assetOptions.at(0)?.value ?? nativeAsset);
|
||||
}
|
||||
}, [assetOptions, selectedAssetValue, nativeAsset]);
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
isDisabled={isDisabled}
|
||||
label={t('asset to pay the fee')}
|
||||
onChange={onSelect}
|
||||
onSearch={onSearch}
|
||||
options={assetOptions}
|
||||
value={selectedAssetValue}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(PayWithAsset);
|
||||
@ -3,7 +3,8 @@
|
||||
|
||||
import type { SubmittableResult } from '@polkadot/api';
|
||||
import type { SubmittableExtrinsic } from '@polkadot/api/promise/types';
|
||||
import type { SignerResult } from '@polkadot/api/types';
|
||||
import type { SignerOptions, SignerResult } from '@polkadot/api/types';
|
||||
import type { AssetInfoComplete } from '@polkadot/react-hooks/types';
|
||||
import type { AccountId, Address } from '@polkadot/types/interfaces';
|
||||
import type { DefinitionRpcExt, Registry, SignerPayloadJSON } from '@polkadot/types/types';
|
||||
|
||||
@ -51,6 +52,7 @@ export interface QueueTx extends AccountInfo {
|
||||
txUpdateCb?: TxCallback;
|
||||
values?: unknown[];
|
||||
status: QueueTxStatus;
|
||||
signerOptions?: Partial<SignerOptions & { feeAsset: AssetInfoComplete | null }>;
|
||||
}
|
||||
|
||||
export interface QueueStatus extends ActionStatus {
|
||||
@ -87,6 +89,7 @@ export interface PartialQueueTxExtrinsic extends PartialAccountInfo {
|
||||
txStartCb?: () => void;
|
||||
txUpdateCb?: TxCallback;
|
||||
isUnsigned?: boolean;
|
||||
signerOptions?: Partial<SignerOptions & { feeAsset: AssetInfoComplete | null }>;
|
||||
}
|
||||
|
||||
export interface PartialQueueTxRpc extends PartialAccountInfo {
|
||||
|
||||
@ -7,7 +7,8 @@ import type { TxButtonProps as Props } from './types.js';
|
||||
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
import { useIsMountedRef, useQueue } from '@polkadot/react-hooks';
|
||||
import { useApi, useIsMountedRef, usePayWithAsset, useQueue } from '@polkadot/react-hooks';
|
||||
import { getFeeAssetLocation } from '@polkadot/react-hooks/utils/getFeeAssetLocation';
|
||||
import { assert, isFunction } from '@polkadot/util';
|
||||
|
||||
import Button from './Button/index.js';
|
||||
@ -15,8 +16,10 @@ import { useTranslation } from './translate.js';
|
||||
|
||||
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 { api } = useApi();
|
||||
const mountedRef = useIsMountedRef();
|
||||
const { queueExtrinsic } = useQueue();
|
||||
const { selectedFeeAsset } = usePayWithAsset();
|
||||
const [isSending, setIsSending] = useState(false);
|
||||
const [isStarted, setIsStarted] = useState(false);
|
||||
|
||||
@ -76,6 +79,7 @@ function TxButton ({ accountId, className = '', extrinsic: propsExtrinsic, icon,
|
||||
accountId: accountId?.toString(),
|
||||
extrinsic,
|
||||
isUnsigned,
|
||||
signerOptions: { assetId: getFeeAssetLocation(api, selectedFeeAsset), feeAsset: selectedFeeAsset },
|
||||
txFailedCb: withSpinner ? _onFailed : onFailed,
|
||||
txStartCb: _onStart,
|
||||
txSuccessCb: withSpinner ? _onSuccess : onSuccess,
|
||||
@ -85,7 +89,7 @@ function TxButton ({ accountId, className = '', extrinsic: propsExtrinsic, icon,
|
||||
|
||||
onClick && onClick();
|
||||
},
|
||||
[_onFailed, _onStart, _onSuccess, accountId, isUnsigned, onClick, onFailed, onSuccess, onUpdate, params, propsExtrinsic, queueExtrinsic, setIsSending, tx, withSpinner, mountedRef]
|
||||
[_onFailed, _onStart, _onSuccess, accountId, api, isUnsigned, mountedRef, onClick, onFailed, onSuccess, onUpdate, params, propsExtrinsic, queueExtrinsic, selectedFeeAsset, tx, withSpinner]
|
||||
);
|
||||
|
||||
if (onSendRef) {
|
||||
|
||||
@ -74,6 +74,7 @@ export { default as Output } from './Output.js';
|
||||
export { default as ParaLink } from './ParaLink.js';
|
||||
export { default as Password } from './Password.js';
|
||||
export { default as PasswordStrength } from './PasswordStrength.js';
|
||||
export { default as PayWithAsset } from './PayWithAsset.js';
|
||||
export { default as Popup } from './Popup/index.js';
|
||||
export { default as Progress } from './Progress.js';
|
||||
export { default as ProgressBar } from './ProgressBar.js';
|
||||
|
||||
@ -19,6 +19,7 @@ import InputBalance from '../InputBalance.js';
|
||||
import MarkError from '../MarkError.js';
|
||||
import MarkWarning from '../MarkWarning.js';
|
||||
import Modal from '../Modal/index.js';
|
||||
import PayWithAsset from '../PayWithAsset.js';
|
||||
import { styled } from '../styled.js';
|
||||
import Toggle from '../Toggle.js';
|
||||
import { useTranslation } from '../translate.js';
|
||||
@ -147,6 +148,9 @@ function Transfer ({ className = '', onClose, recipientId: propRecipientId, send
|
||||
<MarkError content={t('The recipient is associated with a known phishing site on {{url}}', { replace: { url: recipientPhish } })} />
|
||||
)}
|
||||
</Modal.Columns>
|
||||
<Modal.Columns hint={t('By selecting this option, the transaction fee will be automatically deducted from the specified asset, ensuring a seamless and efficient payment process.')}>
|
||||
<PayWithAsset />
|
||||
</Modal.Columns>
|
||||
<Modal.Columns hint={t('If the recipient account is new, the balance needs to be more than the existential deposit. Likewise if the sending account balance drops below the same value, the account will be removed from the state.')}>
|
||||
{canToggleAll && isAll
|
||||
? (
|
||||
|
||||
@ -1,10 +1,13 @@
|
||||
// Copyright 2017-2025 @polkadot/react-components authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import '@polkadot/api-augment';
|
||||
|
||||
import type { IconName } from '@fortawesome/fontawesome-svg-core';
|
||||
import type React from 'react';
|
||||
import type { SubmittableExtrinsic } from '@polkadot/api/types';
|
||||
import type { Abi } from '@polkadot/api-contract';
|
||||
import type { Registrar } from '@polkadot/react-hooks/types';
|
||||
import type { AccountId, AccountIndex, Address, Index } from '@polkadot/types/interfaces';
|
||||
import type { ActionStatus, TxCallback, TxFailedCallback } from './Status/types.js';
|
||||
|
||||
@ -85,6 +88,13 @@ export type AccountIdIsh = AccountId | AccountIndex | Address | string | Uint8Ar
|
||||
|
||||
export type DisplayedJudgement = 'Erroneous' | 'Low quality' | 'Known good' | 'Reasonable';
|
||||
|
||||
export interface Judgement {
|
||||
judgementName: DisplayedJudgement;
|
||||
registrars: (Registrar | undefined)[];
|
||||
}
|
||||
|
||||
export type UseJudgements = Judgement[]
|
||||
|
||||
export interface TabItem {
|
||||
alias?: string;
|
||||
count?: number;
|
||||
|
||||
91
packages/react-hooks/src/ctx/PayWithAsset.tsx
Normal file
91
packages/react-hooks/src/ctx/PayWithAsset.tsx
Normal file
@ -0,0 +1,91 @@
|
||||
// Copyright 2017-2025 @polkadot/react-hooks authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { BN } from '@polkadot/util';
|
||||
import type { AssetInfoComplete } from '../types.js';
|
||||
import type { PayWithAsset } from './types.js';
|
||||
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
import { useApi, useAssetIds, useAssetInfos } from '@polkadot/react-hooks';
|
||||
import { formatNumber } from '@polkadot/util';
|
||||
|
||||
interface Props {
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
const EMPTY_STATE: PayWithAsset = {
|
||||
assetOptions: [],
|
||||
isDisabled: true,
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
onChange: () => {},
|
||||
selectedFeeAsset: null
|
||||
};
|
||||
|
||||
export const PayWithAssetCtx = React.createContext<PayWithAsset>(EMPTY_STATE);
|
||||
|
||||
export function PayWithAssetCtxRoot ({ children }: Props): React.ReactElement<Props> {
|
||||
const { isApiReady } = useApi();
|
||||
|
||||
return isApiReady ? <PayWithAssetProvider>{children}</PayWithAssetProvider> : <>{children}</>;
|
||||
}
|
||||
|
||||
function PayWithAssetProvider ({ children }: Props): React.ReactElement<Props> {
|
||||
const { api } = useApi();
|
||||
const ids = useAssetIds();
|
||||
const assetInfos = useAssetInfos(ids);
|
||||
const [selectedFeeAsset, setSelectedFeeAsset] = useState<AssetInfoComplete | null>(null);
|
||||
|
||||
const nativeAsset = useMemo(
|
||||
() => api.registry.chainTokens[0],
|
||||
[api]
|
||||
);
|
||||
|
||||
const completeAssetInfos = useMemo(
|
||||
() => (assetInfos
|
||||
?.filter((i): i is AssetInfoComplete =>
|
||||
!!(i.details && i.metadata) && !i.details.supply.isZero() && !!i.details?.toJSON().isSufficient)
|
||||
.sort((a, b) => a.id.cmp(b.id))) || [],
|
||||
[assetInfos]
|
||||
);
|
||||
|
||||
const assetOptions = useMemo(
|
||||
() => [
|
||||
{ text: `${nativeAsset} (Native)`, value: nativeAsset },
|
||||
...completeAssetInfos.map(({ id, metadata }) => ({
|
||||
text: `${metadata.name.toUtf8()} (${formatNumber(id)})`,
|
||||
value: id.toString()
|
||||
}))],
|
||||
[completeAssetInfos, nativeAsset]
|
||||
);
|
||||
|
||||
const onChange = useCallback((assetId: BN, cb?: () => void) => {
|
||||
const selectedFeeAsset = completeAssetInfos.find((a) => a.id.toString() === assetId.toString());
|
||||
|
||||
setSelectedFeeAsset(selectedFeeAsset ?? null);
|
||||
cb?.();
|
||||
}, [completeAssetInfos]);
|
||||
|
||||
const isEnabled = useMemo(() =>
|
||||
api.registry.metadata.extrinsic.signedExtensions.some(
|
||||
(a) => a.identifier.toString() === 'ChargeAssetTxPayment'
|
||||
) &&
|
||||
api.tx.assetConversion && completeAssetInfos.length > 0,
|
||||
[api.registry.metadata.extrinsic.signedExtensions, api.tx.assetConversion, completeAssetInfos.length]
|
||||
);
|
||||
|
||||
const values: PayWithAsset = useMemo(() => {
|
||||
return {
|
||||
assetOptions,
|
||||
isDisabled: !isEnabled,
|
||||
onChange,
|
||||
selectedFeeAsset
|
||||
};
|
||||
}, [assetOptions, isEnabled, onChange, selectedFeeAsset]);
|
||||
|
||||
return (
|
||||
<PayWithAssetCtx.Provider value={values}>
|
||||
{children}
|
||||
</PayWithAssetCtx.Provider>
|
||||
);
|
||||
}
|
||||
@ -5,5 +5,6 @@ export { ApiStatsCtxRoot } from './ApiStats.js';
|
||||
export { BlockAuthorsCtxRoot } from './BlockAuthors.js';
|
||||
export { BlockEventsCtxRoot } from './BlockEvents.js';
|
||||
export { KeyringCtxRoot } from './Keyring.js';
|
||||
export { PayWithAssetCtxRoot } from './PayWithAsset.js';
|
||||
export { QueueCtxRoot } from './Queue.js';
|
||||
export { WindowSizeCtxRoot } from './WindowSize.js';
|
||||
|
||||
@ -9,6 +9,8 @@ import type { LinkOption } from '@polkadot/apps-config/endpoints/types';
|
||||
import type { InjectedExtension } from '@polkadot/extension-inject/types';
|
||||
import type { ProviderStats } from '@polkadot/rpc-provider/types';
|
||||
import type { BlockNumber, EventRecord } from '@polkadot/types/interfaces';
|
||||
import type { BN } from '@polkadot/util';
|
||||
import type { AssetInfoComplete } from '../types.js';
|
||||
|
||||
export interface ApiState {
|
||||
apiDefaultTx: SubmittableExtrinsicFunction;
|
||||
@ -66,6 +68,13 @@ export interface ApiStats {
|
||||
when: number;
|
||||
}
|
||||
|
||||
export interface PayWithAsset {
|
||||
isDisabled: boolean;
|
||||
assetOptions: {text: string, value: string}[];
|
||||
onChange: (assetId: BN, cb?: () => void) => void;
|
||||
selectedFeeAsset: AssetInfoComplete | null;
|
||||
}
|
||||
|
||||
export interface BlockAuthors {
|
||||
byAuthor: Record<string, string>;
|
||||
eraPoints: Record<string, string>;
|
||||
|
||||
@ -13,6 +13,8 @@ export { useAddresses } from './useAddresses.js';
|
||||
export { useApi } from './useApi.js';
|
||||
export { useApiStats } from './useApiStats.js';
|
||||
export { useApiUrl } from './useApiUrl.js';
|
||||
export { useAssetIds } from './useAssetIds.js';
|
||||
export { useAssetInfos } from './useAssetInfos.js';
|
||||
export { useAvailableSlashes } from './useAvailableSlashes.js';
|
||||
export { useBalancesAll } from './useBalancesAll.js';
|
||||
export { useBestHash } from './useBestHash.js';
|
||||
@ -69,6 +71,7 @@ export { useOwnStashInfos } from './useOwnStashInfos.js';
|
||||
export { useParaApi } from './useParaApi.js';
|
||||
export { useIsParasLinked, useParaEndpoints } from './useParaEndpoints.js';
|
||||
export { usePassword } from './usePassword.js';
|
||||
export { usePayWithAsset } from './usePayWithAsset.js';
|
||||
export { usePeopleEndpoint } from './usePeopleEndpoint.js';
|
||||
export { usePopupWindow } from './usePopupWindow.js';
|
||||
export { usePreimage } from './usePreimage.js';
|
||||
|
||||
@ -1,14 +1,15 @@
|
||||
// Copyright 2017-2025 @polkadot/react-hooks authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import '@polkadot/api-augment';
|
||||
|
||||
import type React from 'react';
|
||||
import type { ApiPromise } from '@polkadot/api';
|
||||
import type { SubmittableExtrinsic } from '@polkadot/api/types';
|
||||
import type { DeriveAccountFlags, DeriveAccountRegistration } from '@polkadot/api-derive/types';
|
||||
import type { DisplayedJudgement } from '@polkadot/react-components/types';
|
||||
import type { Option, u32, u128, Vec } from '@polkadot/types';
|
||||
import type { AccountId, BlockNumber, Call, Hash, SessionIndex, ValidatorPrefs } from '@polkadot/types/interfaces';
|
||||
import type { PalletPreimageRequestStatus, PalletStakingRewardDestination, PalletStakingStakingLedger, PolkadotRuntimeParachainsAssignerCoretimeCoreDescriptor, SpStakingExposurePage, SpStakingPagedExposureMetadata } from '@polkadot/types/lookup';
|
||||
import type { PalletAssetsAssetDetails, PalletAssetsAssetMetadata, PalletPreimageRequestStatus, PalletStakingRewardDestination, PalletStakingStakingLedger, PolkadotRuntimeParachainsAssignerCoretimeCoreDescriptor, SpStakingExposurePage, SpStakingPagedExposureMetadata } from '@polkadot/types/lookup';
|
||||
import type { ICompact, IExtrinsic, INumber } from '@polkadot/types/types';
|
||||
import type { KeyringJson$Meta } from '@polkadot/ui-keyring/types';
|
||||
import type { BN } from '@polkadot/util';
|
||||
@ -52,6 +53,22 @@ export interface Inflation {
|
||||
stakedReturn: number;
|
||||
}
|
||||
|
||||
export interface AssetInfo {
|
||||
details: PalletAssetsAssetDetails | null;
|
||||
id: BN;
|
||||
isAdminMe: boolean;
|
||||
isIssuerMe: boolean;
|
||||
isFreezerMe: boolean;
|
||||
isOwnerMe: boolean;
|
||||
key: string;
|
||||
metadata: PalletAssetsAssetMetadata | null;
|
||||
}
|
||||
|
||||
export interface AssetInfoComplete extends AssetInfo {
|
||||
details: PalletAssetsAssetDetails;
|
||||
metadata: PalletAssetsAssetMetadata;
|
||||
}
|
||||
|
||||
export interface Slash {
|
||||
accountId: AccountId;
|
||||
amount: u128;
|
||||
@ -165,13 +182,6 @@ export interface Registrar {
|
||||
index: number;
|
||||
}
|
||||
|
||||
export interface Judgement {
|
||||
judgementName: DisplayedJudgement;
|
||||
registrars: (Registrar | undefined)[];
|
||||
}
|
||||
|
||||
export type UseJudgements = Judgement[]
|
||||
|
||||
export type BatchType = 'all' | 'default' | 'force';
|
||||
|
||||
export interface BatchOptions {
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
// Copyright 2017-2025 @polkadot/app-assets authors & contributors
|
||||
// Copyright 2017-2025 @polkadot/react-hooks authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Changes } from '@polkadot/react-hooks/useEventChanges';
|
||||
import type { StorageKey, u32 } from '@polkadot/types';
|
||||
import type { EventRecord } from '@polkadot/types/interfaces';
|
||||
import type { Changes } from './useEventChanges.js';
|
||||
|
||||
import { createNamedHook, useApi, useEventChanges, useMapKeys } from '@polkadot/react-hooks';
|
||||
import { createNamedHook, useApi, useEventChanges, useMapKeys } from './index.js';
|
||||
|
||||
const EMPTY_PARAMS: unknown[] = [];
|
||||
|
||||
@ -31,13 +31,13 @@ function filter (records: EventRecord[]): Changes<u32> {
|
||||
|
||||
function useAssetIdsImpl (): u32[] | undefined {
|
||||
const { api } = useApi();
|
||||
const startValue = useMapKeys(api.query.assets.asset, EMPTY_PARAMS, OPT_KEY);
|
||||
const startValue = useMapKeys(api.query.assets?.asset, EMPTY_PARAMS, OPT_KEY) || [];
|
||||
|
||||
return useEventChanges([
|
||||
api.events.assets.Created,
|
||||
api.events.assets.Destroyed,
|
||||
api.events.assets.ForceCreated
|
||||
api.events.assets?.Created,
|
||||
api.events.assets?.Destroyed,
|
||||
api.events.assets?.ForceCreated
|
||||
], filter, startValue);
|
||||
}
|
||||
|
||||
export default createNamedHook('useAssetIds', useAssetIdsImpl);
|
||||
export const useAssetIds = createNamedHook('useAssetIds', useAssetIdsImpl);
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2025 @polkadot/app-assets authors & contributors
|
||||
// Copyright 2017-2025 @polkadot/react-hooks authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Option } from '@polkadot/types';
|
||||
@ -7,7 +7,7 @@ import type { PalletAssetsAssetDetails, PalletAssetsAssetMetadata } from '@polka
|
||||
import type { BN } from '@polkadot/util';
|
||||
import type { AssetInfo } from './types.js';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import { createNamedHook, useAccounts, useApi, useCall } from '@polkadot/react-hooks';
|
||||
|
||||
@ -51,8 +51,11 @@ function extractInfo (allAccounts: string[], id: BN, optDetails: Option<PalletAs
|
||||
function useAssetInfosImpl (ids?: BN[]): AssetInfo[] | undefined {
|
||||
const { api } = useApi();
|
||||
const { allAccounts } = useAccounts();
|
||||
const metadata = useCall<[[BN[]], PalletAssetsAssetMetadata[]]>(api.query.assets.metadata.multi, [ids], QUERY_OPTS);
|
||||
const details = useCall<[[BN[]], Option<PalletAssetsAssetDetails>[]]>(api.query.assets.asset.multi, [ids], QUERY_OPTS);
|
||||
|
||||
const isReady = useMemo(() => !!api.tx.assets?.setMetadata && !!api.tx.assets?.transferKeepAlive, [api.tx.assets?.setMetadata, api.tx.assets?.transferKeepAlive]);
|
||||
|
||||
const metadata = useCall<[[BN[]], PalletAssetsAssetMetadata[]]>(isReady && api.query.assets.metadata.multi, [ids], QUERY_OPTS);
|
||||
const details = useCall<[[BN[]], Option<PalletAssetsAssetDetails>[]]>(isReady && api.query.assets.asset.multi, [ids], QUERY_OPTS);
|
||||
const [state, setState] = useState<AssetInfo[] | undefined>();
|
||||
|
||||
useEffect((): void => {
|
||||
@ -67,4 +70,4 @@ function useAssetInfosImpl (ids?: BN[]): AssetInfo[] | undefined {
|
||||
return state;
|
||||
}
|
||||
|
||||
export default createNamedHook('useAssetInfos', useAssetInfosImpl);
|
||||
export const useAssetInfos = createNamedHook('useAssetInfos', useAssetInfosImpl);
|
||||
@ -1,7 +1,7 @@
|
||||
// Copyright 2017-2025 @polkadot/react-hooks authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { UseJudgements } from './types.js';
|
||||
import type { UseJudgements } from '@polkadot/react-components/types';
|
||||
|
||||
import { useMemo } from 'react';
|
||||
|
||||
|
||||
15
packages/react-hooks/src/usePayWithAsset.ts
Normal file
15
packages/react-hooks/src/usePayWithAsset.ts
Normal file
@ -0,0 +1,15 @@
|
||||
// Copyright 2017-2025 @polkadot/react-hooks authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { PayWithAsset } from './ctx/types.js';
|
||||
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { PayWithAssetCtx } from './ctx/PayWithAsset.js';
|
||||
import { createNamedHook } from './createNamedHook.js';
|
||||
|
||||
function usePayWithAssetImpl (): PayWithAsset {
|
||||
return useContext(PayWithAssetCtx);
|
||||
}
|
||||
|
||||
export const usePayWithAsset = createNamedHook('usePayWithAsset', usePayWithAssetImpl);
|
||||
25
packages/react-hooks/src/utils/getFeeAssetLocation.ts
Normal file
25
packages/react-hooks/src/utils/getFeeAssetLocation.ts
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright 2017-2025 @polkadot/react-hooks authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { ApiPromise } from '@polkadot/api';
|
||||
import type { AnyNumber } from '@polkadot/types-codec/types';
|
||||
import type { AssetInfoComplete } from '../types.js';
|
||||
|
||||
export const getFeeAssetLocation = (api: ApiPromise, feeAsset: AssetInfoComplete | null): AnyNumber | object | undefined => {
|
||||
if (!feeAsset?.id) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const metadata = api.registry.metadata;
|
||||
|
||||
const palletIndex = metadata.pallets.filter((a) => a.name.toString() === 'Assets')[0].index.toString();
|
||||
|
||||
// FIX ME: Might have to fix it later as it may not be applicable for all chains
|
||||
const palletInstance = { PalletInstance: palletIndex };
|
||||
const generalIndex = { GeneralIndex: feeAsset.id };
|
||||
|
||||
return {
|
||||
interior: { X2: [palletInstance, generalIndex] },
|
||||
parents: 0
|
||||
};
|
||||
};
|
||||
@ -1,8 +1,9 @@
|
||||
// Copyright 2017-2025 @polkadot/react-hooks authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Judgement } from '@polkadot/react-components/types';
|
||||
import type { SortedJudgements } from '@polkadot/react-components/util/types';
|
||||
import type { Judgement, Registrar } from '../types.js';
|
||||
import type { Registrar } from '../types.js';
|
||||
|
||||
export function matchRegistrarAccountsWithIndexes (
|
||||
judgementsWithRegistrarIndexes: SortedJudgements,
|
||||
|
||||
@ -7,7 +7,9 @@
|
||||
},
|
||||
"include": [
|
||||
"src/ctx/Api.tsx",
|
||||
"src/ctx/types.ts"
|
||||
"src/ctx/types.ts",
|
||||
"src/types.ts",
|
||||
"src/constants.ts",
|
||||
],
|
||||
"references": [
|
||||
{ "path": "../apps-config/tsconfig.build.json" }
|
||||
|
||||
@ -3,15 +3,15 @@
|
||||
|
||||
import type { SubmittableExtrinsic } from '@polkadot/api/promise/types';
|
||||
import type { DeriveBalancesAll } from '@polkadot/api-derive/types';
|
||||
import type { QueueTx } from '@polkadot/react-components/Status/types';
|
||||
import type { RuntimeDispatchInfo } from '@polkadot/types/interfaces';
|
||||
import type { BN } from '@polkadot/util';
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Trans } from 'react-i18next';
|
||||
|
||||
import { Expander, MarkWarning } from '@polkadot/react-components';
|
||||
import { useApi, useCall, useIsMountedRef } from '@polkadot/react-hooks';
|
||||
import { formatBalance, nextTick } from '@polkadot/util';
|
||||
import { BN, formatBalance, nextTick } from '@polkadot/util';
|
||||
|
||||
import { useTranslation } from './translate.js';
|
||||
|
||||
@ -22,9 +22,10 @@ interface Props {
|
||||
isHeader?: boolean;
|
||||
onChange?: (hasAvailable: boolean) => void;
|
||||
tip?: BN;
|
||||
signerOptions?: QueueTx['signerOptions'];
|
||||
}
|
||||
|
||||
function PaymentInfo ({ accountId, className = '', extrinsic, isHeader }: Props): React.ReactElement<Props> | null {
|
||||
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);
|
||||
@ -35,14 +36,30 @@ function PaymentInfo ({ accountId, className = '', extrinsic, isHeader }: Props)
|
||||
accountId && extrinsic && extrinsic.hasPaymentInfo &&
|
||||
nextTick(async (): Promise<void> => {
|
||||
try {
|
||||
const info = await extrinsic.paymentInfo(accountId);
|
||||
const info = await extrinsic.paymentInfo(accountId, signerOptions);
|
||||
|
||||
mountedRef.current && setDispatchInfo(info);
|
||||
if (signerOptions?.assetId) {
|
||||
const convertedFee = new BN((await api.call.assetConversionApi.quotePriceTokensForExactTokens(
|
||||
signerOptions?.assetId as string,
|
||||
{
|
||||
interior: 'Here',
|
||||
parents: 1
|
||||
} as unknown as string,
|
||||
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]);
|
||||
}, [api, accountId, extrinsic, mountedRef, signerOptions]);
|
||||
|
||||
if (!dispatchInfo || !extrinsic) {
|
||||
return null;
|
||||
@ -60,7 +77,10 @@ function PaymentInfo ({ accountId, className = '', extrinsic, isHeader }: Props)
|
||||
isHeader={isHeader}
|
||||
summary={
|
||||
<Trans i18nKey='feesForSubmission'>
|
||||
Fees of <span className='highlight'>{formatBalance(dispatchInfo.partialFee, { withSiFull: true })}</span> will be applied to the submission
|
||||
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(' ')}{' '}
|
||||
{signerOptions?.feeAsset?.metadata.symbol.toHuman()?.toString() ?? api.registry.chainTokens.at(0) }
|
||||
</span> will be applied to the submission
|
||||
</Trans>
|
||||
}
|
||||
/>
|
||||
|
||||
@ -20,7 +20,7 @@ interface Props {
|
||||
tip?: BN;
|
||||
}
|
||||
|
||||
function Transaction ({ accountId, className, currentItem: { extrinsic, isUnsigned, payload }, onError, tip }: Props): React.ReactElement<Props> | null {
|
||||
function Transaction ({ accountId, className, currentItem: { extrinsic, isUnsigned, payload, signerOptions }, onError, tip }: Props): React.ReactElement<Props> | null {
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (!extrinsic) {
|
||||
@ -43,6 +43,7 @@ function Transaction ({ accountId, className, currentItem: { extrinsic, isUnsign
|
||||
className='paymentInfo'
|
||||
extrinsic={extrinsic}
|
||||
isHeader
|
||||
signerOptions={signerOptions}
|
||||
tip={tip}
|
||||
/>
|
||||
)}
|
||||
|
||||
@ -339,7 +339,7 @@ function TxSigned ({ className, currentItem, isQueueSubmit, queueSize, requestAd
|
||||
if (senderInfo.signAddress) {
|
||||
const [tx, [status, pairOrAddress, options, isMockSign]] = await Promise.all([
|
||||
wrapTx(api, currentItem, senderInfo),
|
||||
extractParams(api, senderInfo.signAddress, { nonce: -1, tip, withSignedTransaction: true }, getLedger, setQrState)
|
||||
extractParams(api, senderInfo.signAddress, { nonce: -1, tip, withSignedTransaction: true, ...currentItem.signerOptions }, getLedger, setQrState)
|
||||
]);
|
||||
|
||||
queueSetTxStatus(currentItem.id, status);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user