diff --git a/src/renderer/entities/transaction/ui/Fee/Fee.tsx b/src/renderer/entities/transaction/ui/Fee/Fee.tsx index b02738faa..7ff0041da 100644 --- a/src/renderer/entities/transaction/ui/Fee/Fee.tsx +++ b/src/renderer/entities/transaction/ui/Fee/Fee.tsx @@ -13,7 +13,7 @@ type Props = { api: ApiPromise | null; multiply?: number; asset: Asset; - transaction?: Transaction; + transaction?: Transaction | null; className?: string; onFeeChange?: (fee: string) => void; onFeeLoading?: (loading: boolean) => void; diff --git a/src/renderer/entities/transaction/ui/XcmFee/XcmFee.tsx b/src/renderer/entities/transaction/ui/XcmFee/XcmFee.tsx index d0b07f92a..c00b6c875 100644 --- a/src/renderer/entities/transaction/ui/XcmFee/XcmFee.tsx +++ b/src/renderer/entities/transaction/ui/XcmFee/XcmFee.tsx @@ -15,7 +15,7 @@ type Props = { multiply?: number; asset: Asset; config: XcmConfig; - transaction?: Transaction | DecodedTransaction; + transaction?: Transaction | DecodedTransaction | null; className?: string; onFeeChange?: (fee: string) => void; onFeeLoading?: (loading: boolean) => void; diff --git a/src/renderer/features/governance/lib/createFeeCalculator.ts b/src/renderer/features/governance/lib/createFeeCalculator.ts deleted file mode 100644 index 447174060..000000000 --- a/src/renderer/features/governance/lib/createFeeCalculator.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { type ApiPromise } from '@polkadot/api'; -import { type SignerOptions } from '@polkadot/api/types/submittable'; -import { BN, BN_ZERO } from '@polkadot/util'; -import { type Store, combine, createEffect, createStore, sample } from 'effector'; - -import { type Transaction } from '@/shared/core'; -import { nonNullable, nullable } from '@/shared/lib/utils'; -import { transactionService } from '@/entities/transaction'; - -type Params = { - $transaction: Store; - $api: Store; -}; - -// TODO discuss api for factories -export const createFeeCalculator = ({ $transaction, $api }: Params) => { - type RequestParams = { - api: ApiPromise; - transaction: Transaction; - signerOptions?: Partial; - }; - - const $source = combine({ transaction: $transaction, api: $api }, ({ transaction, api }) => { - if (nullable(transaction) || nullable(api)) return null; - - return { transaction, api }; - }); - - const $fee = createStore(BN_ZERO); - - const fetchFeeFx = createEffect(({ api, transaction, signerOptions }: RequestParams) => { - return transactionService.getTransactionFee(transaction, api, signerOptions).then((x) => new BN(x)); - }); - - sample({ - clock: $source, - filter: nullable, - fn: () => BN_ZERO, - target: $fee, - }); - - sample({ - clock: $source, - filter: nonNullable, - target: fetchFeeFx, - }); - - sample({ - clock: fetchFeeFx.doneData, - source: $transaction, - filter: nonNullable, - fn: (_, fee) => fee, - target: $fee, - }); - - return { $: $fee, $pending: fetchFeeFx.pending }; -}; diff --git a/src/renderer/features/governance/lib/createMultipleTxStore.ts b/src/renderer/features/governance/lib/createMultipleTxStore.ts index 39f95e624..90c8bfcd7 100644 --- a/src/renderer/features/governance/lib/createMultipleTxStore.ts +++ b/src/renderer/features/governance/lib/createMultipleTxStore.ts @@ -3,11 +3,10 @@ import { type Store, combine, createStore } from 'effector'; import { type Account, type Chain, type Transaction, type Wallet } from '@/shared/core'; import { nullable } from '@/shared/lib/utils'; +import { createFeeCalculator } from '@/shared/transactions'; import { transactionService } from '@/entities/transaction'; import { accountUtils, walletUtils } from '@/entities/wallet'; -import { createFeeCalculator } from './createFeeCalculator'; - type Params = { $api: Store; $chain: Store; diff --git a/src/renderer/features/governance/lib/createTransactionForm.ts b/src/renderer/features/governance/lib/createTransactionForm.ts index 4efff6f53..cb3fe19ab 100644 --- a/src/renderer/features/governance/lib/createTransactionForm.ts +++ b/src/renderer/features/governance/lib/createTransactionForm.ts @@ -14,12 +14,11 @@ import { type Wallet, } from '@/shared/core'; import { nullable, toAddress, transferableAmountBN } from '@/shared/lib/utils'; +import { createTxStore } from '@/shared/transactions'; import { balanceUtils } from '@/entities/balance'; import { transactionService } from '@/entities/transaction'; import { accountUtils, walletModel, walletUtils } from '@/entities/wallet'; -import { createTxStore } from './createTxStore'; - export type BasicFormParams = { account: Account | null; signatory: Account | null; diff --git a/src/renderer/features/governance/lib/createTxStore.ts b/src/renderer/features/governance/lib/createTxStore.ts deleted file mode 100644 index 73324629e..000000000 --- a/src/renderer/features/governance/lib/createTxStore.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { type ApiPromise } from '@polkadot/api'; -import { type Store, combine, createStore } from 'effector'; - -import { type Account, type Chain, type Transaction, type Wallet } from '@/shared/core'; -import { nullable } from '@/shared/lib/utils'; -import { transactionService } from '@/entities/transaction'; -import { accountUtils, walletUtils } from '@/entities/wallet'; - -import { createFeeCalculator } from './createFeeCalculator'; - -type Params = { - $api: Store; - $chain: Store; - $coreTx: Store; - $activeWallet: Store; - $wallets: Store; - $account: Store; - $signatory?: Store; -}; - -export const createTxStore = ({ $api, $chain, $coreTx, $activeWallet, $wallets, $account, $signatory }: Params) => { - const $txWrappers = combine( - { - wallet: $activeWallet, - wallets: $wallets, - chain: $chain, - account: $account, - signatory: $signatory ?? createStore(null), - }, - ({ wallet, account, wallets, signatory, chain }) => { - if (nullable(wallet) || nullable(chain) || nullable(account)) return []; - - const filteredWallets = walletUtils.getWalletsFilteredAccounts(wallets, { - walletFn: (w) => !walletUtils.isProxied(w) && !walletUtils.isWatchOnly(w), - accountFn: (a, w) => { - const isBase = accountUtils.isBaseAccount(a); - const isPolkadotVault = walletUtils.isPolkadotVault(w); - - return (!isBase || !isPolkadotVault) && accountUtils.isChainAndCryptoMatch(a, chain); - }, - }); - - return transactionService.getTxWrappers({ - wallet, - wallets: filteredWallets || [], - account, - signatories: signatory ? [signatory] : [account], - }); - }, - ); - - const $wrappedTx = combine( - { api: $api, chain: $chain, coreTx: $coreTx, txWrappers: $txWrappers }, - ({ api, chain, coreTx, txWrappers }) => { - if (nullable(api) || nullable(chain) || nullable(coreTx)) return null; - - return transactionService.getWrappedTransaction({ - api, - addressPrefix: chain.addressPrefix, - transaction: coreTx, - txWrappers, - }); - }, - ); - - const $isMultisig = $txWrappers.map(transactionService.hasMultisig); - const $isProxy = $txWrappers.map(transactionService.hasProxy); - - const { $: $fee, $pending: $pendingFee } = createFeeCalculator({ - $api: $api, - $transaction: $wrappedTx.map((x) => x?.wrappedTx ?? null), - }); - - return { - $coreTx, - $wrappedTx, - $txWrappers, - $isMultisig, - $isProxy, - $fee, - $pendingFee, - }; -}; diff --git a/src/renderer/shared/transactions/createTxStore.ts b/src/renderer/shared/transactions/createTxStore.ts index 73324629e..8fd40fa24 100644 --- a/src/renderer/shared/transactions/createTxStore.ts +++ b/src/renderer/shared/transactions/createTxStore.ts @@ -12,7 +12,7 @@ type Params = { $api: Store; $chain: Store; $coreTx: Store; - $activeWallet: Store; + $activeWallet: Store; $wallets: Store; $account: Store; $signatory?: Store; diff --git a/src/renderer/widgets/Transfer/model/form-model.ts b/src/renderer/widgets/Transfer/model/form-model.ts index 04f7e3817..687430238 100644 --- a/src/renderer/widgets/Transfer/model/form-model.ts +++ b/src/renderer/widgets/Transfer/model/form-model.ts @@ -13,8 +13,10 @@ import { type ProxiedAccount, type ProxyTxWrapper, type Transaction, + TransactionType, } from '@/shared/core'; import { + TEST_ACCOUNTS, ZERO_BALANCE, formatAmount, getAssetId, @@ -24,6 +26,7 @@ import { transferableAmount, validateAddress, } from '@/shared/lib/utils'; +import { createTxStore } from '@/shared/transactions'; import { balanceModel, balanceUtils } from '@/entities/balance'; import { networkModel, networkUtils } from '@/entities/network'; import { transactionBuilder, transactionService } from '@/entities/transaction'; @@ -155,36 +158,82 @@ const $transferForm = createForm({ // Computed -const $txWrappers = combine( +const $api = combine( { - wallet: walletModel.$activeWallet, - wallets: walletModel.$wallets, - account: $transferForm.fields.account.$value, + apis: networkModel.$apis, network: $networkStore, - signatories: $selectedSignatories, }, - ({ wallet, account, wallets, network, signatories }) => { - if (!wallet || !network || !account.id) return []; + ({ apis, network }) => { + if (!network) return null; - const filteredWallets = walletUtils.getWalletsFilteredAccounts(wallets, { - walletFn: (w) => !walletUtils.isProxied(w) && !walletUtils.isWatchOnly(w), - accountFn: (a, w) => { - const isBase = accountUtils.isBaseAccount(a); - const isPolkadotVault = walletUtils.isPolkadotVault(w); + return apis[network.chain.chainId] ?? null; + }, +); - return (!isBase || !isPolkadotVault) && accountUtils.isChainAndCryptoMatch(a, network.chain); - }, - }); +const $isChainConnected = combine( + { + network: $networkStore, + statuses: networkModel.$connectionStatuses, + }, + ({ network, statuses }) => { + if (!network) return false; - return transactionService.getTxWrappers({ - wallet, - wallets: filteredWallets || [], - account, - signatories, + return networkUtils.isConnectedStatus(statuses[network.chain.chainId]); + }, +); + +const $coreTx = combine( + { + network: $networkStore, + isXcm: $isXcm, + form: $transferForm.$values, + xcmData: xcmTransferModel.$xcmData, + isConnected: $isChainConnected, + }, + ({ network, isXcm, form, xcmData, isConnected }): Transaction | null => { + if (!network || !isConnected || (isXcm && !xcmData) || !validateAddress(form.destination)) return null; + + return transactionBuilder.buildTransfer({ + chain: network.chain, + asset: network.asset, + accountId: form.account.accountId, + amount: form.amount, + destination: form.destination, + xcmData, }); }, ); +const { $wrappedTx: $transaction, $txWrappers } = createTxStore({ + $api, + $activeWallet: walletModel.$activeWallet, + $wallets: walletModel.$wallets, + $chain: $transferForm.fields.xcmChain.$value, + $coreTx, + $account: $transferForm.fields.account.$value || null, + $signatory: $transferForm.fields.signatory.$value || null, +}); + +const $fakeTx = combine( + { + network: $networkStore, + isConnected: $isChainConnected, + isXcm: $isXcm, + xcmData: xcmTransferModel.$xcmData, + }, + ({ isConnected, network, isXcm, xcmData }): Transaction | null => { + if (!network || !isConnected) return null; + console.log(xcmData, isXcm); + + return { + chainId: network.chain.chainId, + address: toAddress(TEST_ACCOUNTS[0], { prefix: network.chain.addressPrefix }), + type: TransactionType.TRANSFER, + args: { destination: toAddress(TEST_ACCOUNTS[0], { prefix: network.chain.addressPrefix }), ...xcmData?.args }, + }; + }, +); + const $realAccount = combine( { txWrappers: $txWrappers, @@ -309,73 +358,6 @@ const $chains = combine( }, ); -const $isChainConnected = combine( - { - network: $networkStore, - statuses: networkModel.$connectionStatuses, - }, - ({ network, statuses }) => { - if (!network) return false; - - return networkUtils.isConnectedStatus(statuses[network.chain.chainId]); - }, -); - -const $api = combine( - { - apis: networkModel.$apis, - network: $networkStore, - }, - ({ apis, network }) => { - if (!network) return null; - - return apis[network.chain.chainId] ?? null; - }, -); - -const $pureTx = combine( - { - network: $networkStore, - isXcm: $isXcm, - form: $transferForm.$values, - xcmData: xcmTransferModel.$xcmData, - isConnected: $isChainConnected, - }, - ({ network, isXcm, form, xcmData, isConnected }): Transaction | undefined => { - if (!network || !isConnected || (isXcm && !xcmData) || !validateAddress(form.destination)) return undefined; - - return transactionBuilder.buildTransfer({ - chain: network.chain, - asset: network.asset, - accountId: form.account.accountId, - amount: form.amount, - destination: form.destination, - xcmData, - }); - }, - { skipVoid: false }, -); - -const $transaction = combine( - { - api: $api, - networkStore: $networkStore, - pureTx: $pureTx, - txWrappers: $txWrappers, - }, - ({ api, networkStore, pureTx, txWrappers }) => { - if (!networkStore || !pureTx || !api) return undefined; - - return transactionService.getWrappedTransaction({ - api, - addressPrefix: networkStore.chain.addressPrefix, - transaction: pureTx, - txWrappers, - }); - }, - { skipVoid: false }, -); - const $destinationAccounts = combine( { isXcm: $isXcm, @@ -647,9 +629,10 @@ export const formModel = { $fee, $multisigDeposit, + $coreTx, + $fakeTx, $api, $networkStore, - $pureTx, $transaction, $isMultisig, $isXcm, diff --git a/src/renderer/widgets/Transfer/ui/TransferForm.tsx b/src/renderer/widgets/Transfer/ui/TransferForm.tsx index eda77f439..2ca217dee 100644 --- a/src/renderer/widgets/Transfer/ui/TransferForm.tsx +++ b/src/renderer/widgets/Transfer/ui/TransferForm.tsx @@ -277,9 +277,9 @@ const FeeSection = () => { const api = useUnit(formModel.$api); const network = useUnit(formModel.$networkStore); const transaction = useUnit(formModel.$transaction); - const pureTx = useUnit(formModel.$pureTx); + const coreTx = useUnit(formModel.$coreTx); + const fakeTx = useUnit(formModel.$fakeTx); const isMultisig = useUnit(formModel.$isMultisig); - const isXcm = useUnit(formModel.$isXcm); const xcmConfig = useUnit(formModel.$xcmConfig); const xcmApi = useUnit(formModel.$xcmApi); @@ -302,7 +302,7 @@ const FeeSection = () => { @@ -312,7 +312,7 @@ const FeeSection = () => { api={xcmApi} config={xcmConfig} asset={network.asset} - transaction={pureTx} + transaction={coreTx || fakeTx} onFeeChange={formModel.events.xcmFeeChanged} onFeeLoading={formModel.events.isXcmFeeLoadingChanged} />