diff --git a/ui/app/AppLayouts/Wallet/stores/CollectiblesStore.qml b/ui/app/AppLayouts/Wallet/stores/CollectiblesStore.qml index aae6ac1b24..9e26c082eb 100644 --- a/ui/app/AppLayouts/Wallet/stores/CollectiblesStore.qml +++ b/ui/app/AppLayouts/Wallet/stores/CollectiblesStore.qml @@ -6,6 +6,7 @@ import StatusQ.Models 0.1 import utils 1.0 import SortFilterProxyModel 0.2 +import StatusQ.Core.Utils 0.1 as SQUtils QtObject { id: root @@ -107,4 +108,26 @@ QtObject { function getDetailedCollectible(chainId, contractAddress, tokenId) { walletSection.collectibleDetailsController.getDetailedCollectible(chainId, contractAddress, tokenId) } + + function hasNFT(ownerAddress, chainId, tokenId, tokenAddress) { + const uid = getUidForData(tokenId, tokenAddress, chainId) + ownerAddress = ownerAddress.toLowerCase() + const ownership = SQUtils.ModelUtils.getByKey(_allCollectiblesModel, "uid", uid, "ownership") + if (!ownership) + return false + + for (let i = 0; i < ownership.count; i++) { + const accountAddress = SQUtils.ModelUtils.get(ownership, i, "accountAddress").toLowerCase() + if (accountAddress !== ownerAddress) + continue + const tokenBalanceStr = SQUtils.ModelUtils.get(ownership, i, "balance").toLowerCase() + if (tokenBalanceStr !== "") + return true + } + return false + } + + function getUidForData(tokenId, tokenAddress, chainId) { + return _allCollectiblesModel.getUidForData(tokenId, tokenAddress, chainId) + } } diff --git a/ui/app/AppLayouts/Wallet/stores/RootStore.qml b/ui/app/AppLayouts/Wallet/stores/RootStore.qml index b8e33775cd..c6c615557f 100644 --- a/ui/app/AppLayouts/Wallet/stores/RootStore.qml +++ b/ui/app/AppLayouts/Wallet/stores/RootStore.qml @@ -315,17 +315,9 @@ QtObject { function getAssetForSendTx(tx) { if (tx.isNFT) { - return { - uid: tx.tokenID, - chainId: tx.chainId, - name: tx.nftName, - imageUrl: tx.nftImageUrl, - collectionUid: "", - collectionName: "" - } - } else { - return tx.symbol + return collectiblesStore.getUidForData(tx.tokenID, tx.tokenAddress, tx.chainId) } + return tx.symbol } function isTxRepeatable(tx) { @@ -336,12 +328,14 @@ QtObject { if (!res || res.walletType === Constants.watchWalletType) return false - if (tx.isNFT) { - // TODO #12275: check if account owns enough NFT - } else { - // TODO #12275: Check if account owns enough tokens + if (!tx.amount) { + // Ignore incorrect transactions + return false } + if (tx.isNFT && !root.collectiblesStore.hasNFT(tx.sender, tx.chainId, tx.tokenID, tx.tokenAddress)) { + return false + } return true } diff --git a/ui/app/AppLayouts/Wallet/views/TransactionDetailView.qml b/ui/app/AppLayouts/Wallet/views/TransactionDetailView.qml index 0198f13037..2ba56367a5 100644 --- a/ui/app/AppLayouts/Wallet/views/TransactionDetailView.qml +++ b/ui/app/AppLayouts/Wallet/views/TransactionDetailView.qml @@ -767,7 +767,7 @@ Item { property alias tx: d.transaction visible: { - if (!d.isTransactionValid || root.overview.isWatchOnlyAccount) + if (!d.isTransactionValid || root.overview.isWatchOnlyAccount || root.overview.isAllAccounts) return false return WalletStores.RootStore.isTxRepeatable(tx) @@ -780,7 +780,8 @@ Item { tx.recipient, asset, tx.isNFT, - tx.amount) + tx.amount, + tx.chainId) root.sendModal.preSelectedAccountAddress = req.preSelectedAccount.address root.sendModal.preSelectedRecipient = req.preSelectedRecipient @@ -789,6 +790,7 @@ Item { root.sendModal.preSelectedHoldingType = req.preSelectedHoldingType root.sendModal.preSelectedSendType = req.preSelectedSendType root.sendModal.preDefinedAmountToSend = req.preDefinedAmountToSend + root.sendModal.preSelectedChainId = req.preSelectedChainId root.sendModal.onlyAssets = false root.sendModal.open() } diff --git a/ui/app/mainui/AppMain.qml b/ui/app/mainui/AppMain.qml index e69e40a879..e975fd9c82 100644 --- a/ui/app/mainui/AppMain.qml +++ b/ui/app/mainui/AppMain.qml @@ -1626,6 +1626,7 @@ Item { property int preSelectedHoldingType: Constants.TokenType.Unknown property int preSelectedSendType: Constants.SendType.Unknown property string preDefinedAmountToSend + property int preSelectedChainId: 0 property bool onlyAssets: false sourceComponent: SendPopups.SendModal { @@ -1644,6 +1645,7 @@ Item { sendModal.preSelectedAccountAddress = "" sendModal.preSelectedRecipient = undefined sendModal.preDefinedAmountToSend = "" + sendModal.preSelectedChainId = 0 } } onLoaded: { @@ -1651,8 +1653,9 @@ Item { item.preSelectedAccountAddress = sendModal.preSelectedAccountAddress } if (!!sendModal.preSelectedRecipient) { - item.preSelectedRecipient = sendModal.preSelectedRecipient + // NOTE Should be assigned in that order: type then recipient item.preSelectedRecipientType = sendModal.preSelectedRecipientType + item.preSelectedRecipient = sendModal.preSelectedRecipient } if(sendModal.preSelectedSendType !== Constants.SendType.Unknown) { item.preSelectedSendType = sendModal.preSelectedSendType @@ -1664,6 +1667,9 @@ Item { if(preDefinedAmountToSend != "") { item.preDefinedAmountToSend = preDefinedAmountToSend } + if(!!sendModal.preSelectedChainId) { + item.preSelectedChainId = sendModal.preSelectedChainId + } } } diff --git a/ui/imports/shared/controls/TransactionDelegate.qml b/ui/imports/shared/controls/TransactionDelegate.qml index aadc1e1d3a..3190e05c79 100644 --- a/ui/imports/shared/controls/TransactionDelegate.qml +++ b/ui/imports/shared/controls/TransactionDelegate.qml @@ -200,7 +200,7 @@ StatusListItem { readonly property bool isLightTheme: Style.current.name === Constants.lightThemeName property color animatedBgColor - property int txType: walletRootStore.transactionType(modelData) + property int txType: walletRootStore.transactionType(root.modelData) function addressesEqual(address1, address2) { return address1.toUpperCase() == address2.toUpperCase() diff --git a/ui/imports/shared/popups/send/Helpers.qml b/ui/imports/shared/popups/send/Helpers.qml index c423f01e13..33c6c13c19 100644 --- a/ui/imports/shared/popups/send/Helpers.qml +++ b/ui/imports/shared/popups/send/Helpers.qml @@ -29,9 +29,9 @@ QtObject { preSelectedRecipientType: Helpers.RecipientAddressObjectType.Address, preSelectedRecipient: null, preSelectedHoldingType: Constants.TokenType.Unknown, - preSelectedHolding: null, preSelectedHoldingID: "", preDefinedAmountToSend: "", + preSelectedChainId: 0, preSelectedSendType: Constants.SendType.Transfer } } @@ -43,7 +43,8 @@ QtObject { recipientAddress, token, isCollectible, - amount) { + amount, + chainId) { let req = createSendModalRequirements() req.preSelectedSendType = Constants.SendType.Transfer @@ -70,14 +71,9 @@ QtObject { req.preSelectedRecipient = recipientAddress } - // Holdings related properties: - if (isCollectible) { - req.preSelectedHoldingType = Constants.TokenType.ERC721 - req.preSelectedHolding = token - } else { - req.preSelectedHoldingType = Constants.TokenType.ERC20 - req.preSelectedHoldingID = token - } + req.preSelectedHoldingType = isCollectible ? Constants.TokenType.ERC721 : Constants.TokenType.ERC20 + req.preSelectedHoldingID = token + req.preSelectedChainId = chainId req.preDefinedAmountToSend = LocaleUtils.numberToLocaleString(amount) diff --git a/ui/imports/shared/popups/send/SendModal.qml b/ui/imports/shared/popups/send/SendModal.qml index 5e075a2be7..3ab0fa6c6c 100644 --- a/ui/imports/shared/popups/send/SendModal.qml +++ b/ui/imports/shared/popups/send/SendModal.qml @@ -40,6 +40,7 @@ StatusDialog { property alias preSelectedRecipientType: recipientInputLoader.selectedRecipientType property string preDefinedAmountToSend + property int preSelectedChainId: 0 property string stickersPackId // token symbol @@ -146,6 +147,10 @@ StatusDialog { readonly property bool isSelectedHoldingValidAsset: !!selectedHolding && selectedHoldingType === Constants.TokenType.ERC20 onSelectedHoldingChanged: { + if (!selectedHolding) { + return + } + if (d.selectedHoldingType === Constants.TokenType.ERC20) { if(!d.ensOrStickersPurpose && store.sendType !== Constants.SendType.Bridge) store.setSendType(Constants.SendType.Transfer) @@ -251,6 +256,11 @@ StatusDialog { amountToSend.setValue(delocalized) } + + if (!!popup.preSelectedChainId) { + popup.preDefinedAmountToSend = popup.preDefinedAmountToSend.replace(Qt.locale().decimalPoint, '.') + store.updateRoutePreferredChains(popup.preSelectedChainId) + } if (!!popup.stickersPackId) { d.extraParamsJson = "{\"%1\":\"%2\"}".arg(Constants.suggestedRoutesExtraParamsProperties.packId).arg(popup.stickersPackId) diff --git a/ui/imports/shared/views/HistoryView.qml b/ui/imports/shared/views/HistoryView.qml index aeb6ca8622..3106eeef7e 100644 --- a/ui/imports/shared/views/HistoryView.qml +++ b/ui/imports/shared/views/HistoryView.qml @@ -324,15 +324,17 @@ ColumnLayout { tx.recipient, asset, tx.isNFT, - tx.amount) + tx.amount, + tx.chainId) root.sendModal.preSelectedAccountAddress = req.preSelectedAccount.address root.sendModal.preSelectedRecipient = req.preSelectedRecipient root.sendModal.preSelectedRecipientType = req.preSelectedRecipientType - root.sendModal.preSelectedHoldingID = req.preSelectedHoldingID ?? req.preSelectedHolding.uid + root.sendModal.preSelectedHoldingID = req.preSelectedHoldingID root.sendModal.preSelectedHoldingType = req.preSelectedHoldingType root.sendModal.preSelectedSendType = req.preSelectedSendType root.sendModal.preDefinedAmountToSend = req.preDefinedAmountToSend + root.sendModal.preSelectedChainId = req.preSelectedChainId root.sendModal.onlyAssets = false root.sendModal.open() }