diff --git a/packages/extension-base/src/background/KoniTypes.ts b/packages/extension-base/src/background/KoniTypes.ts index 1aaf2d95a2b..d59212a6adc 100644 --- a/packages/extension-base/src/background/KoniTypes.ts +++ b/packages/extension-base/src/background/KoniTypes.ts @@ -754,6 +754,7 @@ export enum TransferTxErrorType { INVALID_TOKEN = 'INVALID_TOKEN', TRANSFER_ERROR = 'TRANSFER_ERROR', RECEIVER_NOT_ENOUGH_EXISTENTIAL_DEPOSIT = 'RECEIVER_NOT_ENOUGH_EXISTENTIAL_DEPOSIT', + RECEIVER_ACCOUNT_INACTIVE = 'RECEIVER_ACCOUNT_INACTIVE' } export type TransactionErrorType = BasicTxErrorType | TransferTxErrorType | StakingTxErrorType | YieldValidationStatus | SwapErrorType diff --git a/packages/extension-base/src/core/logic-validation/transfer.ts b/packages/extension-base/src/core/logic-validation/transfer.ts index 3724409ae7f..9cebe6a600f 100644 --- a/packages/extension-base/src/core/logic-validation/transfer.ts +++ b/packages/extension-base/src/core/logic-validation/transfer.ts @@ -6,7 +6,7 @@ import { TransactionError } from '@subwallet/extension-base/background/errors/Tr import { _Address, AmountData, BasicTxErrorType, BasicTxWarningCode, ExtrinsicDataTypeMap, ExtrinsicType, FeeData, TransferTxErrorType } from '@subwallet/extension-base/background/KoniTypes'; import { TransactionWarning } from '@subwallet/extension-base/background/warnings/TransactionWarning'; import { XCM_MIN_AMOUNT_RATIO } from '@subwallet/extension-base/constants'; -import { _canAccountBeReaped } from '@subwallet/extension-base/core/substrate/system-pallet'; +import { _canAccountBeReaped, _isAccountActive } from '@subwallet/extension-base/core/substrate/system-pallet'; import { FrameSystemAccountInfo } from '@subwallet/extension-base/core/substrate/types'; import { _TRANSFER_CHAIN_GROUP } from '@subwallet/extension-base/services/chain-service/constants'; import { _EvmApi } from '@subwallet/extension-base/services/chain-service/types'; @@ -49,7 +49,7 @@ export function validateTransferRequest (tokenInfo: _ChainAsset, from: _Address, return [errors, keypair, transferValue]; } -export function additionalValidateTransfer (tokenInfo: _ChainAsset, nativeTokenInfo: _ChainAsset, extrinsicType: ExtrinsicType, receiverTransferTokenTotalBalance: string, transferAmount: string, senderTransferTokenTransferable?: string, _receiverNativeTotal?: string): [TransactionWarning[], TransactionError[]] { +export function additionalValidateTransfer (tokenInfo: _ChainAsset, nativeTokenInfo: _ChainAsset, extrinsicType: ExtrinsicType, receiverTransferTokenTotalBalance: string, transferAmount: string, senderTransferTokenTransferable?: string, _receiverNativeTotal?: string, isReceiverActive?: unknown): [TransactionWarning[], TransactionError[]] { const minAmount = _getTokenMinAmount(tokenInfo); const nativeMinAmount = _getTokenMinAmount(nativeTokenInfo); const warnings: TransactionWarning[] = []; @@ -73,6 +73,13 @@ export function additionalValidateTransfer (tokenInfo: _ChainAsset, nativeTokenI } } + // Check if receiver's account is active + if (isReceiverActive && _isAccountActive(isReceiverActive as FrameSystemAccountInfo)) { + const error = new TransactionError(TransferTxErrorType.RECEIVER_ACCOUNT_INACTIVE, t('The recipient account may be inactive. Change recipient account and try again')); + + errors.push(error); + } + // Check ed for receiver after sending if (new BigN(receiverTransferTokenTotalBalance).plus(transferAmount).lt(minAmount)) { const atLeast = new BigN(minAmount).minus(receiverTransferTokenTotalBalance).plus((tokenInfo.decimals || 0) === 0 ? 0 : 1); @@ -419,7 +426,7 @@ export function checkBalanceWithTransactionFee (validationResponse: SWTransactio } // todo: only system.pallet has metadata, we should add for other pallets and mechanisms as well - const isNeedCheckRemainingBalance = !isTransferAll && extrinsicType === ExtrinsicType.TRANSFER_BALANCE && nativeTokenAvailable.metadata && _canAccountBeReaped(nativeTokenAvailable.metadata as FrameSystemAccountInfo); + const isNeedCheckRemainingBalance = !isTransferAll && extrinsicType === ExtrinsicType.TRANSFER_BALANCE && nativeTokenAvailable.metadata && _canAccountBeReaped(nativeTokenAvailable.metadata); const isRemainingBalanceValid = bnNativeTokenAvailable.minus(bnNativeTokenTransferAmount).minus(bnFee).lt(_getTokenMinAmount(nativeTokenInfo)); if (isNeedCheckRemainingBalance && isRemainingBalanceValid) { diff --git a/packages/extension-base/src/core/substrate/system-pallet.ts b/packages/extension-base/src/core/substrate/system-pallet.ts index d1b2c12e1e8..57fa499955b 100644 --- a/packages/extension-base/src/core/substrate/system-pallet.ts +++ b/packages/extension-base/src/core/substrate/system-pallet.ts @@ -24,7 +24,7 @@ export function _canAccountBeReaped (accountInfo: FrameSystemAccountInfo): boole } export function _isAccountActive (accountInfo: FrameSystemAccountInfo): boolean { - return accountInfo.providers === 0 && accountInfo.consumers === 0; + return accountInfo.consumers === 0 && accountInfo.providers === 0 && accountInfo.sufficients === 0; } export function _getSystemPalletTotalBalance (accountInfo: FrameSystemAccountInfo): bigint { diff --git a/packages/extension-base/src/koni/background/handlers/Extension.ts b/packages/extension-base/src/koni/background/handlers/Extension.ts index 5299a811b87..11273d3c0bd 100644 --- a/packages/extension-base/src/koni/background/handlers/Extension.ts +++ b/packages/extension-base/src/koni/background/handlers/Extension.ts @@ -1826,6 +1826,7 @@ export default class KoniExtension { const additionalValidator = async (inputTransaction: SWTransactionResponse): Promise => { let senderTransferTokenTransferable: string | undefined; let receiverNativeTotal: string | undefined; + let isReceiverActive: unknown; // Check ed for sender if (!isTransferNativeToken) { @@ -1836,11 +1837,12 @@ export default class KoniExtension { senderTransferTokenTransferable = _senderTransferTokenTransferable.value; receiverNativeTotal = _receiverNativeTotal.value; + isReceiverActive = _receiverNativeTotal.metadata; } const { value: receiverTransferTokenTransferable } = await this.getAddressTotalBalance({ address: to, networkKey, token: tokenSlug, extrinsicType }); // todo: shouldn't be just transferable, locked also counts - const [warnings, errors] = additionalValidateTransfer(transferTokenInfo, nativeTokenInfo, extrinsicType, receiverTransferTokenTransferable, transferAmount.value, senderTransferTokenTransferable, receiverNativeTotal); + const [warnings, errors] = additionalValidateTransfer(transferTokenInfo, nativeTokenInfo, extrinsicType, receiverTransferTokenTransferable, transferAmount.value, senderTransferTokenTransferable, receiverNativeTotal, isReceiverActive); warnings.length && inputTransaction.warnings.push(...warnings); errors.length && inputTransaction.errors.push(...errors);