From f597ef4e6a31e3064b57e6bae23ccfb1850d64b2 Mon Sep 17 00:00:00 2001 From: Khushboo Mehta Date: Thu, 31 Aug 2023 12:27:15 +0200 Subject: [PATCH] feat(@desktop/wallet): Glue for sending nft fixed #12128 --- .../main/wallet_section/send/controller.nim | 4 +- .../main/wallet_section/send/io_interface.nim | 2 +- .../main/wallet_section/send/module.nim | 6 +- .../wallet_section/send/network_model.nim | 26 ++++--- .../modules/main/wallet_section/send/view.nim | 16 ++--- .../service/eth/dto/transaction.nim | 10 ++- .../service/transaction/async_tasks.nim | 2 +- src/app_service/service/transaction/dto.nim | 10 +++ .../service/transaction/service.nim | 47 +++++++++---- ui/app/AppLayouts/Wallet/WalletLayout.qml | 14 +++- .../AppLayouts/Wallet/panels/WalletFooter.qml | 16 ++--- ui/app/mainui/AppMain.qml | 6 +- ui/imports/shared/controls/GasSelector.qml | 2 +- ui/imports/shared/popups/SendModal.qml | 69 ++++++++++++------- ui/imports/shared/stores/TransactionStore.qml | 35 +++++----- ui/imports/shared/views/NetworkSelector.qml | 13 ++-- .../views/NetworksSimpleRoutingView.qml | 7 +- ui/imports/shared/views/RecipientView.qml | 15 ++-- ui/imports/utils/Constants.qml | 3 +- vendor/status-go | 2 +- 20 files changed, 197 insertions(+), 108 deletions(-) diff --git a/src/app/modules/main/wallet_section/send/controller.nim b/src/app/modules/main/wallet_section/send/controller.nim index fa5c6d337f..21f0d1614a 100644 --- a/src/app/modules/main/wallet_section/send/controller.nim +++ b/src/app/modules/main/wallet_section/send/controller.nim @@ -93,8 +93,8 @@ proc suggestedRoutes*(self: Controller, account: string, amount: Uint256, token: return suggestedRoutes.toJson() proc transfer*(self: Controller, from_addr: string, to_addr: string, tokenSymbol: string, - value: string, uuid: string, selectedRoutes: seq[TransactionPathDto], password: string) = - self.transactionService.transfer(from_addr, to_addr, tokenSymbol, value, uuid, selectedRoutes, password) + value: string, uuid: string, selectedRoutes: seq[TransactionPathDto], password: string, sendType: int) = + self.transactionService.transfer(from_addr, to_addr, tokenSymbol, value, uuid, selectedRoutes, password, sendType) proc areTestNetworksEnabled*(self: Controller): bool = return self.walletAccountService.areTestNetworksEnabled() diff --git a/src/app/modules/main/wallet_section/send/io_interface.nim b/src/app/modules/main/wallet_section/send/io_interface.nim index c7203d7f4a..fac02aca35 100644 --- a/src/app/modules/main/wallet_section/send/io_interface.nim +++ b/src/app/modules/main/wallet_section/send/io_interface.nim @@ -30,7 +30,7 @@ method suggestedRoutesReady*(self: AccessInterface, suggestedRoutes: SuggestedRo raise newException(ValueError, "No implementation available") method authenticateAndTransfer*(self: AccessInterface, from_addr: string, to_addr: string, - tokenSymbol: string, value: string, uuid: string) {.base.} = + tokenSymbol: string, value: string, uuid: string, sendType: int) {.base.} = raise newException(ValueError, "No implementation available") method onUserAuthenticated*(self: AccessInterface, password: string) {.base.} = diff --git a/src/app/modules/main/wallet_section/send/module.nim b/src/app/modules/main/wallet_section/send/module.nim index bda1c77da5..7d5cefb8c6 100644 --- a/src/app/modules/main/wallet_section/send/module.nim +++ b/src/app/modules/main/wallet_section/send/module.nim @@ -30,6 +30,7 @@ type TmpSendTransactionDetails = object value: string paths: seq[TransactionPathDto] uuid: string + sendType: int type Module* = ref object of io_interface.AccessInterface @@ -241,12 +242,13 @@ method viewDidLoad*(self: Module) = method getTokenBalanceOnChain*(self: Module, address: string, chainId: int, symbol: string): CurrencyAmount = return self.controller.getTokenBalanceOnChain(address, chainId, symbol) -method authenticateAndTransfer*(self: Module, from_addr: string, to_addr: string, tokenSymbol: string, value: string, uuid: string) = +method authenticateAndTransfer*(self: Module, from_addr: string, to_addr: string, tokenSymbol: string, value: string, uuid: string, sendType: int) = self.tmpSendTransactionDetails.fromAddr = from_addr self.tmpSendTransactionDetails.toAddr = to_addr self.tmpSendTransactionDetails.tokenSymbol = tokenSymbol self.tmpSendTransactionDetails.value = value self.tmpSendTransactionDetails.uuid = uuid + self.tmpSendTransactionDetails.sendType = sendType if singletonInstance.userProfile.getIsKeycardUser(): let keyUid = singletonInstance.userProfile.getKeyUid() @@ -282,7 +284,7 @@ method onUserAuthenticated*(self: Module, password: string) = self.controller.transfer( self.tmpSendTransactionDetails.fromAddr, self.tmpSendTransactionDetails.toAddr, self.tmpSendTransactionDetails.tokenSymbol, self.tmpSendTransactionDetails.value, self.tmpSendTransactionDetails.uuid, - self.tmpSendTransactionDetails.paths, password + self.tmpSendTransactionDetails.paths, password, self.tmpSendTransactionDetails.sendType ) method transactionWasSent*(self: Module, chainId: int, txHash, uuid, error: string) = diff --git a/src/app/modules/main/wallet_section/send/network_model.nim b/src/app/modules/main/wallet_section/send/network_model.nim index 2309dfa2e0..804b558019 100644 --- a/src/app/modules/main/wallet_section/send/network_model.nim +++ b/src/app/modules/main/wallet_section/send/network_model.nim @@ -178,21 +178,21 @@ QtObject: self.items[i].amountOut = path.getAmountOut() self.dataChanged(index, index, @[ModelRole.AmountOut.int]) - proc getDisabledNetworkChainIds*(self: NetworkModel): seq[int] = + proc getRouteDisabledNetworkChainIds*(self: NetworkModel): seq[int] = var disbaledChains: seq[int] = @[] for item in self.items: if not item.getIsEnabled(): disbaledChains.add(item.getChainId()) return disbaledChains - proc getLockedChainIds*(self: NetworkModel): string = + proc getRouteLockedChainIds*(self: NetworkModel): string = var jsonObject = newJObject() for item in self.items: if item.getLocked(): jsonObject[$item.getChainId()] = %* ("0x" & item.getLockedAmount()) return $jsonObject - proc updatePreferredChains*(self: NetworkModel, chainIds: string) = + proc updateRoutePreferredChains*(self: NetworkModel, chainIds: string) = try: for i in 0 ..< self.items.len: let index = self.createIndex(i, 0, nil) @@ -211,28 +211,28 @@ QtObject: except: discard - proc getPreferredNetworkChainIds*(self: NetworkModel): seq[int] = + proc getRoutePreferredNetworkChainIds*(self: NetworkModel): seq[int] = var preferredChains: seq[int] = @[] for item in self.items: if item.getIsPreferred(): preferredChains.add(item.getChainId()) return preferredChains - proc disableUnpreferredChains*(self: NetworkModel) = + proc disableRouteUnpreferredChains*(self: NetworkModel) = for i in 0 ..< self.items.len: if not self.items[i].getIsPreferred(): let index = self.createIndex(i, 0, nil) self.items[i].isEnabled = false self.dataChanged(index, index, @[ModelRole.IsEnabled.int]) - proc enableUnpreferredChains*(self: NetworkModel) = + proc enableRouteUnpreferredChains*(self: NetworkModel) = for i in 0 ..< self.items.len: if not self.items[i].getIsPreferred(): let index = self.createIndex(i, 0, nil) self.items[i].isEnabled = true self.dataChanged(index, index, @[ModelRole.IsEnabled.int]) - proc setAllNetworksAsPreferredChains*(self: NetworkModel) {.slot.} = + proc setAllNetworksAsRoutePreferredChains*(self: NetworkModel) {.slot.} = for i in 0 ..< self.items.len: let index = self.createIndex(i, 0, nil) self.items[i].isPreferred = true @@ -256,20 +256,28 @@ QtObject: return item.getChainName() return "" - proc toggleDisabledChains*(self: NetworkModel, chainId: int) {.slot.} = + proc toggleRouteDisabledChains*(self: NetworkModel, chainId: int) {.slot.} = for i in 0 ..< self.items.len: if(self.items[i].getChainId() == chainId): let index = self.createIndex(i, 0, nil) self.items[i].isEnabled = not self.items[i].getIsEnabled() self.dataChanged(index, index, @[ModelRole.IsEnabled.int]) - proc setDisabledChains*(self: NetworkModel, chainId: int, disabled: bool) {.slot.} = + proc setRouteDisabledChains*(self: NetworkModel, chainId: int, disabled: bool) {.slot.} = for i in 0 ..< self.items.len: if(self.items[i].getChainId() == chainId): let index = self.createIndex(i, 0, nil) self.items[i].isEnabled = not disabled self.dataChanged(index, index, @[ModelRole.IsEnabled.int]) + proc setRouteEnabledFromChains*(self: NetworkModel, chainId: int) {.slot.} = + for i in 0 ..< self.items.len: + let index = self.createIndex(i, 0, nil) + self.items[i].isEnabled = false + if(self.items[i].getChainId() == chainId): + self.items[i].isEnabled = true + self.dataChanged(index, index, @[ModelRole.IsEnabled.int]) + proc lockCard*(self: NetworkModel, chainId: int, amount: string, lock: bool) {.slot.} = for i in 0 ..< self.items.len: if(self.items[i].getChainId() == chainId): diff --git a/src/app/modules/main/wallet_section/send/view.nim b/src/app/modules/main/wallet_section/send/view.nim index 306934d55a..b82c9fdfa8 100644 --- a/src/app/modules/main/wallet_section/send/view.nim +++ b/src/app/modules/main/wallet_section/send/view.nim @@ -146,9 +146,9 @@ QtObject: proc updateNetworksDisabledChains(self: View) = # if the setting to show unpreferred chains is toggled, add all unpreferred chains to disabled chains list if not self.showUnPreferredChains: - self.toNetworksModel.disableUnpreferredChains() + self.toNetworksModel.disableRouteUnpreferredChains() else: - self.toNetworksModel.enableUnpreferredChains() + self.toNetworksModel.enableRouteUnpreferredChains() proc updateNetworksTokenBalance(self: View) = for chainId in self.toNetworksModel.getAllNetworksChainIds(): @@ -173,8 +173,8 @@ QtObject: self.transactionSent(chainId, txHash, uuid, error) proc authenticateAndTransfer*(self: View, from_addr: string, to_addr: string, tokenSymbol: string, - value: string, uuid: string) {.slot.} = - self.delegate.authenticateAndTransfer(from_addr, to_addr, tokenSymbol, value, uuid) + value: string, uuid: string, sendType: int) {.slot.} = + self.delegate.authenticateAndTransfer(from_addr, to_addr, tokenSymbol, value, uuid, sendType) proc suggestedRoutesReady*(self: View, suggestedRoutes: QVariant) {.signal.} proc setTransactionRoute*(self: View, routes: TransactionRoutes) = @@ -190,8 +190,8 @@ QtObject: discard return self.delegate.suggestedRoutes(self.selectedSenderAccount.address(), - parsedAmount, self.selectedAssetSymbol, self.fromNetworksModel.getDisabledNetworkChainIds(), - self.toNetworksModel.getDisabledNetworkChainIds(), self.toNetworksModel.getPreferredNetworkChainIds(), sendType, self.fromNetworksModel.getLockedChainIds()) + parsedAmount, self.selectedAssetSymbol, self.fromNetworksModel.getRouteDisabledNetworkChainIds(), + self.toNetworksModel.getRouteDisabledNetworkChainIds(), self.toNetworksModel.getRoutePreferredNetworkChainIds(), sendType, self.fromNetworksModel.getRouteLockedChainIds()) proc switchSenderAccountByAddress*(self: View, address: string) = let (account, index) = self.senderAccounts.getItemByAddress(address) @@ -223,8 +223,8 @@ QtObject: self.setSelectetReceiveAccount(account) self.delegate.setSelectedReceiveAccountIndex(idx) - proc updatePreferredChains*(self: View, chainIds: string) {.slot.} = - self.toNetworksModel.updatePreferredChains(chainIds) + proc updateRoutePreferredChains*(self: View, chainIds: string) {.slot.} = + self.toNetworksModel.updateRoutePreferredChains(chainIds) proc getSelectedSenderAccountAddress*(self: View): string = return self.selectedSenderAccount.address() diff --git a/src/app_service/service/eth/dto/transaction.nim b/src/app_service/service/eth/dto/transaction.nim index 8aa3e67c1c..42472387fe 100644 --- a/src/app_service/service/eth/dto/transaction.nim +++ b/src/app_service/service/eth/dto/transaction.nim @@ -23,6 +23,8 @@ type amountOutMin*: Option[UInt256] # (optional) amountOutMin in case of a bridge hop transaction bonderFee*: Option[string] # (optional) bonderFee in case of a bridge hop transaction + tokenID*: Option[UInt256] # (optional) chainID in case of a ERC721 transaction + proc `%`*(x: TransactionDataDto): JsonNode = result = newJobject() result["from"] = %x.source @@ -55,18 +57,22 @@ proc `%`*(x: TransactionDataDto): JsonNode = result["amountOutMin"] = %x.amountOutMin.unsafeGet if x.bonderFee.isSome: result["bonderFee"] = %x.bonderFee.unsafeGet + if x.tokenID.isSome: + result["tokenID"] = %x.tokenID.unsafeGet type TransactionBridgeDto* = object bridgeName*: string chainID*: int - simpleTx*: TransactionDataDto + transferTx*: TransactionDataDto hopTx*: TransactionDataDto cbridgeTx*: TransactionDataDto + eRC721TransferTx*: TransactionDataDto proc `%`*(x: TransactionBridgeDto): JsonNode = result = newJobject() result["bridgeName"] = %x.bridgeName result["chainID"] = %x.chainID - result["simpleTx"] = %x.simpleTx + result["transferTx"] = %x.transferTx result["hopTx"] = %x.hopTx result["cbridgeTx"] = %x.cbridgeTx + result["eRC721TransferTx"] = %x.eRC721TransferTx diff --git a/src/app_service/service/transaction/async_tasks.nim b/src/app_service/service/transaction/async_tasks.nim index 5cc4b5b7ee..b98885a299 100644 --- a/src/app_service/service/transaction/async_tasks.nim +++ b/src/app_service/service/transaction/async_tasks.nim @@ -68,7 +68,7 @@ proc addFirstSimpleBridgeTxFlag(paths: seq[TransactionPathDto]) : seq[Transactio if not firstSimplePath: firstSimplePath = true path.isFirstSimpleTx = true - if path.bridgeName != "Simple": + if path.bridgeName != "Transfer": if not firstBridgePath: firstBridgePath = false path.isFirstBridgeTx = true diff --git a/src/app_service/service/transaction/dto.nim b/src/app_service/service/transaction/dto.nim index 4f29268c5d..07daf7be7b 100644 --- a/src/app_service/service/transaction/dto.nim +++ b/src/app_service/service/transaction/dto.nim @@ -9,6 +9,16 @@ import ../../common/conversion as service_conversion import ./backend/transactions +type + SendType* {.pure.} = enum + Transfer + ENSRegister + ENSRelease + ENSSetPubKey + StickersBuy + Bridge + ERC721Transfer + type PendingTransactionTypeDto* {.pure.} = enum RegisterENS = "RegisterENS", diff --git a/src/app_service/service/transaction/service.nim b/src/app_service/service/transaction/service.nim index a83a45e621..0ffd3b9d5a 100644 --- a/src/app_service/service/transaction/service.nim +++ b/src/app_service/service/transaction/service.nim @@ -44,8 +44,9 @@ const SIGNAL_HISTORY_ERROR* = "historyError" const SIGNAL_CRYPTO_SERVICES_READY* = "cryptoServicesReady" const SIGNAL_TRANSACTION_DECODED* = "transactionDecoded" -const SIMPLE_TX_BRIDGE_NAME = "Simple" +const SIMPLE_TX_BRIDGE_NAME = "Transfer" const HOP_TX_BRIDGE_NAME = "Hop" +const ERC721_TRANSFER_NAME = "ERC721Transfer" type EstimatedTime* {.pure.} = enum @@ -214,16 +215,17 @@ QtObject: txData.data = data var path = TransactionBridgeDto(bridgeName: SIMPLE_TX_BRIDGE_NAME, chainID: route.fromNetwork.chainId) - path.simpleTx = txData + path.transferTx = txData return path proc createPath*(self: Service, route: TransactionPathDto, txData: TransactionDataDto, tokenSymbol: string, to_addr: string): TransactionBridgeDto = var path = TransactionBridgeDto(bridgeName: route.bridgeName, chainID: route.fromNetwork.chainId) var hopTx = TransactionDataDto() var cbridgeTx = TransactionDataDto() + var eRC721TransferTx = TransactionDataDto() if(route.bridgeName == SIMPLE_TX_BRIDGE_NAME): - path.simpleTx = txData + path.transferTx = txData elif(route.bridgeName == HOP_TX_BRIDGE_NAME): hopTx = txData hopTx.chainID = route.toNetwork.chainId.some @@ -232,6 +234,12 @@ QtObject: hopTx.amount = route.amountIn.some hopTx.bonderFee = route.bonderFees.some path.hopTx = hopTx + elif(route.bridgeName == ERC721_TRANSFER_NAME): + eRC721TransferTx = txData + eRC721TransferTx.chainID = route.toNetwork.chainId.some + eRC721TransferTx.recipient = parseAddress(to_addr).some + eRC721TransferTx.tokenID = stint.u256(tokenSymbol).some + path.eRC721TransferTx = eRC721TransferTx else: cbridgeTx = txData cbridgeTx.chainID = route.toNetwork.chainId.some @@ -239,7 +247,6 @@ QtObject: cbridgeTx.recipient = parseAddress(to_addr).some cbridgeTx.amount = route.amountIn.some path.cbridgeTx = cbridgeTx - return path proc transferEth*( @@ -303,19 +310,31 @@ QtObject: uuid: string, routes: seq[TransactionPathDto], password: string, + sendType: int ) = try: + let isERC721Transfer = sendType == ord(ERC721Transfer) var paths: seq[TransactionBridgeDto] = @[] var chainID = 0 if(routes.len > 0): chainID = routes[0].fromNetwork.chainID - let network = self.networkService.getNetwork(chainID) + var amountToSend: Stuint[256] + var toAddress: Address + var tokenSym = tokenSymbol + if isERC721Transfer: + amountToSend = value.parse(Stuint[256]) + let contract_tokenId = tokenSym.split(":") + if contract_tokenId.len == 2: + toAddress = parseAddress(contract_tokenId[0]) + tokenSym = contract_tokenId[1] + else: + let network = self.networkService.getNetwork(chainID) + let token = self.tokenService.findTokenBySymbol(network.chainId, tokenSym) + amountToSend = conversion.eth2Wei(parseFloat(value), token.decimals) + toAddress = token.address - let token = self.tokenService.findTokenBySymbol(network.chainId, tokenSymbol) - let amountToSend = conversion.eth2Wei(parseFloat(value), token.decimals) - let toAddress = token.address let transfer = Transfer( to: parseAddress(to_addr), value: amountToSend, @@ -336,14 +355,15 @@ QtObject: $route.gasAmount, gasFees, route.gasFees.eip1559Enabled, $route.gasFees.maxPriorityFeePerGas, $route.gasFees.maxFeePerGasM) txData.data = data - paths.add(self.createPath(route, txData, tokenSymbol, to_addr)) + paths.add(self.createPath(route, txData, tokenSym, to_addr)) - let response = transactions.createMultiTransaction( + var response: RpcResponse[JsonNode] + response = transactions.createMultiTransaction( MultiTransactionCommandDto( fromAddress: from_addr, toAddress: to_addr, - fromAsset: tokenSymbol, - toAsset: tokenSymbol, + fromAsset: tokenSym, + toAsset: tokenSym, fromAmount: "0x" & amountToSend.toHex, multiTxType: transactions.MultiTransactionType.MultiTransactionSend, ), @@ -368,6 +388,7 @@ QtObject: uuid: string, selectedRoutes: seq[TransactionPathDto], password: string, + sendType: int ) = try: var chainID = 0 @@ -383,7 +404,7 @@ QtObject: if(isEthTx): self.transferEth(from_addr, to_addr, tokenSymbol, value, uuid, selectedRoutes, password) else: - self.transferToken(from_addr, to_addr, tokenSymbol, value, uuid, selectedRoutes, password) + self.transferToken(from_addr, to_addr, tokenSymbol, value, uuid, selectedRoutes, password, sendType) except Exception as e: self.events.emit(SIGNAL_TRANSACTION_SENT, TransactionSentArgs(chainId: 0, txHash: "", uuid: uuid, error: fmt"Error sending token transfer transaction: {e.msg}")) diff --git a/ui/app/AppLayouts/Wallet/WalletLayout.qml b/ui/app/AppLayouts/Wallet/WalletLayout.qml index 04f4175677..7e59409f11 100644 --- a/ui/app/AppLayouts/Wallet/WalletLayout.qml +++ b/ui/app/AppLayouts/Wallet/WalletLayout.qml @@ -144,12 +144,24 @@ Item { footer: WalletFooter { visible: !root.showAllAccounts - sendModal: root.sendModalPopup width: parent.width height: root.showAllAccounts ? implicitHeight : 61 walletStore: RootStore networkConnectionStore: root.networkConnectionStore onLaunchShareAddressModal: Global.openPopup(receiveModalComponent) + onLaunchSendModal: { + root.sendModalPopup.sendType = Constants.SendType.Transfer + root.sendModalPopup.preSelectedHoldingID = walletStore.currentViewedHoldingID + root.sendModalPopup.preSelectedHoldingType = walletStore.currentViewedHoldingType + root.sendModalPopup.open() + } + onLaunchBridgeModal: { + root.sendModalPopup.isBridgeTx = true + root.sendModalPopup.sendType = Constants.SendType.Bridge + root.sendModalPopup.preSelectedHoldingID = walletStore.currentViewedHoldingID + root.sendModalPopup.preSelectedHoldingType = walletStore.currentViewedHoldingType + root.sendModalPopup.open() + } } } diff --git a/ui/app/AppLayouts/Wallet/panels/WalletFooter.qml b/ui/app/AppLayouts/Wallet/panels/WalletFooter.qml index de96d23678..f29638f564 100644 --- a/ui/app/AppLayouts/Wallet/panels/WalletFooter.qml +++ b/ui/app/AppLayouts/Wallet/panels/WalletFooter.qml @@ -15,11 +15,12 @@ import "../popups" Rectangle { id: root - property var sendModal property var walletStore property var networkConnectionStore signal launchShareAddressModal() + signal launchSendModal() + signal launchBridgeModal() color: Theme.palette.statusAppLayout.rightPanelBackgroundColor @@ -39,11 +40,7 @@ Rectangle { icon: "send" text: qsTr("Send") interactive: networkConnectionStore.sendBuyBridgeEnabled - onClicked: function() { - sendModal.preSelectedHoldingID = walletStore.currentViewedHoldingID - sendModal.preSelectedHoldingType = walletStore.currentViewedHoldingType - sendModal.open() - } + onClicked: root.launchSendModal() tooltipText: networkConnectionStore.sendBuyBridgeToolTipText visible: !walletStore.overview.isWatchOnlyAccount } @@ -61,12 +58,7 @@ Rectangle { buttonType: DisabledTooltipButton.Flat text: qsTr("Bridge") interactive: networkConnectionStore.sendBuyBridgeEnabled - onClicked: function() { - sendModal.isBridgeTx = true - sendModal.preSelectedHoldingID = walletStore.currentViewedHoldingID - sendModal.preSelectedHoldingType = walletStore.currentViewedHoldingType - sendModal.open() - } + onClicked: root.launchBridgeModal() tooltipText: networkConnectionStore.sendBuyBridgeToolTipText visible: !walletStore.overview.isWatchOnlyAccount } diff --git a/ui/app/mainui/AppMain.qml b/ui/app/mainui/AppMain.qml index caf9a1e66a..8d356303ed 100644 --- a/ui/app/mainui/AppMain.qml +++ b/ui/app/mainui/AppMain.qml @@ -1348,12 +1348,13 @@ Item { property bool isBridgeTx property string preSelectedHoldingID property var preSelectedHoldingType - + property int sendType: -1 sourceComponent: SendModal { onlyAssets: false onClosed: { sendModal.closed() sendModal.isBridgeTx = false + sendModal.sendType = -1 sendModal.preSelectedHoldingID = "" sendModal.preSelectedHoldingType = Constants.HoldingType.Unknown } @@ -1365,6 +1366,9 @@ Item { if(isBridgeTx) { item.isBridgeTx = sendModal.isBridgeTx } + if(sendModal.sendType >= 0) { + item.sendType = sendModal.sendType + } if(preSelectedHoldingType !== Constants.HoldingType.Unknown) { item.preSelectedHoldingID = sendModal.preSelectedHoldingID item.preSelectedHoldingType = sendModal.preSelectedHoldingType diff --git a/ui/imports/shared/controls/GasSelector.qml b/ui/imports/shared/controls/GasSelector.qml index d95b680fcd..b3944ffbf1 100644 --- a/ui/imports/shared/controls/GasSelector.qml +++ b/ui/imports/shared/controls/GasSelector.qml @@ -117,7 +117,7 @@ Item { property double tokenFees: modelData.tokenFees property double tokenFeesFiat: root.getFiatValue(tokenFees, root.selectedTokenSymbol, root.currentCurrency) subTitle: root.formatCurrencyAmount(tokenFees, root.selectedTokenSymbol) - visible: modelData.bridgeName !== "Simple" + visible: modelData.bridgeName !== "Transfer" statusListItemSubTitle.width: 100 statusListItemSubTitle.elide: Text.ElideMiddle components: [ diff --git a/ui/imports/shared/popups/SendModal.qml b/ui/imports/shared/popups/SendModal.qml index 1168112788..8568778b46 100644 --- a/ui/imports/shared/popups/SendModal.qml +++ b/ui/imports/shared/popups/SendModal.qml @@ -24,8 +24,6 @@ import "../views" StatusDialog { id: popup - property bool isBridgeTx: false - property string preSelectedRecipient property string preDefinedAmountToSend property var preSelectedHolding @@ -44,7 +42,8 @@ StatusDialog { property var bestRoutes property alias addressText: recipientLoader.addressText property bool isLoading: false - property int sendType: isBridgeTx ? Constants.SendType.Bridge : Constants.SendType.Transfer + property int sendType: Constants.SendType.Transfer + property MessageDialog sendingError: MessageDialog { id: sendingError title: qsTr("Error sending the transaction") @@ -60,20 +59,21 @@ StatusDialog { recipientAddress, d.selectedSymbol, amountToSendInput.cryptoValueToSend, - d.uuid) + d.uuid, + sendType) } property var recalculateRoutesAndFees: Backpressure.debounce(popup, 600, function() { - if(!!popup.selectedAccount && d.isSelectedHoldingValidAsset && recipientLoader.ready && amountToSendInput.inputNumberValid) { + if(!!popup.selectedAccount && !!d.selectedHolding && recipientLoader.ready && amountToSendInput.inputNumberValid) { popup.isLoading = true - let amount = Math.round(amountToSendInput.cryptoValueToSend * Math.pow(10, d.selectedHolding.decimals)) + let amount = d.isERC721Transfer ? 1: Math.round(amountToSendInput.cryptoValueToSend * Math.pow(10, d.selectedHolding.decimals)) popup.store.suggestedRoutes(amount.toString(16), popup.sendType) } }) QtObject { id: d - readonly property int errorType: !amountToSendInput.input.valid ? Constants.SendAmountExceedsBalance : + readonly property int errorType: !amountToSendInput.input.valid && !isERC721Transfer ? Constants.SendAmountExceedsBalance : (popup.bestRoutes && popup.bestRoutes.count === 0 && !!amountToSendInput.input.text && recipientLoader.ready && !popup.isLoading) ? Constants.NoRoute : Constants.NoError @@ -88,7 +88,8 @@ StatusDialog { property string totalTimeEstimate property double totalFeesInFiat property double totalAmountToReceive - + readonly property bool isBridgeTx: popup.sendType === Constants.SendType.Bridge + readonly property bool isERC721Transfer: popup.sendType === Constants.SendType.ERC721Transfer property var selectedHolding: null property var selectedHoldingType: Constants.HoldingType.Unknown readonly property bool isSelectedHoldingValidAsset: !!selectedHolding && selectedHoldingType === Constants.HoldingType.Asset @@ -102,8 +103,8 @@ StatusDialog { } function setSelectedHolding(holding, holdingType) { - d.selectedHolding = holding d.selectedHoldingType = holdingType + d.selectedHolding = holding let selectorHolding = store.holdingToSelectorHolding(holding, holdingType) holdingSelector.setSelectedItem(selectorHolding, holdingType) } @@ -114,11 +115,25 @@ StatusDialog { } function setHoveredHolding(holding, holdingType) { - d.hoveredHolding = holding d.hoveredHoldingType = holdingType + d.hoveredHolding = holding let selectorHolding = store.holdingToSelectorHolding(holding, holdingType) holdingSelector.setHoveredItem(selectorHolding, holdingType) } + + onSelectedHoldingChanged: { + if (d.selectedHoldingType === Constants.HoldingType.Asset) { + popup.sendType = Constants.SendType.Transfer + store.setSelectedAssetSymbol(selectedHolding.symbol) + } else if (d.selectedHoldingType === Constants.HoldingType.Collectible) { + popup.sendType = Constants.SendType.ERC721Transfer + amountToSendInput.input.text = 1 + store.setSelectedAssetSymbol(selectedHolding.contractAddress+":"+selectedHolding.tokenId) + store.setRouteEnabledFromChains(selectedHolding.chainId) + store.updateRoutePreferredChains(selectedHolding.chainId) + } + recalculateRoutesAndFees() + } } width: 556 @@ -152,12 +167,14 @@ StatusDialog { recipientLoader.selectedRecipient = {address: popup.preSelectedRecipient} } - if(popup.isBridgeTx) { + if(d.isBridgeTx) { recipientLoader.selectedRecipientType = TabAddressSelectorView.Type.Address recipientLoader.selectedRecipient = {address: popup.selectedAccount.address} } } + onClosed: popup.store.resetStoredProperties() + header: AccountsModalHeader { anchors.top: parent.top anchors.topMargin: -height - 18 @@ -215,7 +232,7 @@ StatusDialog { id: modalHeader Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft verticalAlignment: Text.AlignVCenter - text: popup.isBridgeTx ? qsTr("Bridge") : qsTr("Send") + text: d.isBridgeTx ? qsTr("Bridge") : qsTr("Send") font.pixelSize: 28 lineHeight: 38 lineHeightMode: Text.FixedHeight @@ -230,7 +247,8 @@ StatusDialog { assetsModel: popup.selectedAccount && popup.selectedAccount.assets ? popup.selectedAccount.assets : null collectiblesModel: popup.selectedAccount ? popup.nestedCollectiblesModel : null currentCurrencySymbol: RootStore.currencyStore.currentCurrencySymbol - visible: !!d.selectedHolding || !!d.hoveredHolding + visible: (!!d.selectedHolding && d.selectedHoldingType !== Constants.HoldingType.Unknown) || + (!!d.hoveredHolding && d.hoveredHoldingType !== Constants.HoldingType.Unknown) getNetworkIcon: function(chainId){ return RootStore.getNetworkIcon(chainId) } @@ -242,9 +260,9 @@ StatusDialog { StatusListItemTag { Layout.alignment: Qt.AlignVCenter | Qt.AlignRight Layout.preferredHeight: 22 - visible: d.isSelectedHoldingValidAsset || d.isHoveredHoldingValidAsset + visible: d.isSelectedHoldingValidAsset || d.isHoveredHoldingValidAsset && !d.isERC721Transfer title: { - if(d.isHoveredHoldingValidAsset) { + if(d.isHoveredHoldingValidAsset && !!d.hoveredHolding.symbol) { const balance = popup.currencyStore.formatCurrencyAmount((amountToSendInput.inputIsFiat ? d.hoveredHolding.totalCurrencyBalance.amount : d.hoveredHolding.totalBalance.amount) , d.hoveredHolding.symbol) return qsTr("Max: %1").arg(balance) } @@ -288,11 +306,11 @@ StatusDialog { } } RowLayout { - visible: d.isSelectedHoldingValidAsset + visible: d.isSelectedHoldingValidAsset && !d.isERC721Transfer AmountToSend { id: amountToSendInput Layout.fillWidth:true - isBridgeTx: popup.isBridgeTx + isBridgeTx: d.isBridgeTx interactive: popup.interactive selectedSymbol: d.selectedSymbol maxInputBalance: d.maxInputBalance @@ -314,10 +332,11 @@ StatusDialog { id: amountToReceive Layout.alignment: Qt.AlignRight Layout.fillWidth:true - visible: !!popup.bestRoutes && popup.bestRoutes !== undefined && popup.bestRoutes.count > 0 && amountToSendInput.inputNumberValid + visible: !!popup.bestRoutes && popup.bestRoutes !== undefined && + popup.bestRoutes.count > 0 && amountToSendInput.inputNumberValid isLoading: popup.isLoading selectedSymbol: d.selectedSymbol - isBridgeTx: popup.isBridgeTx + isBridgeTx: d.isBridgeTx cryptoValueToReceive: d.totalAmountToReceive inputIsFiat: amountToSendInput.inputIsFiat minCryptoDecimals: amountToSendInput.minReceiveCryptoDecimals @@ -366,7 +385,7 @@ StatusDialog { anchors.right: parent.right anchors.leftMargin: Style.current.bigPadding anchors.rightMargin: Style.current.bigPadding - visible: !isBridgeTx && !!d.selectedHolding + visible: !d.isBridgeTx && !!d.selectedHolding StatusBaseText { id: label elide: Text.ElideRight @@ -378,7 +397,8 @@ StatusDialog { id: recipientLoader Layout.fillWidth: true store: popup.store - isBridgeTx: popup.isBridgeTx + isERC721Transfer: d.isERC721Transfer + isBridgeTx: d.isBridgeTx interactive: popup.interactive selectedAsset: d.selectedHolding onIsLoading: popup.isLoading = true @@ -398,7 +418,7 @@ StatusDialog { recipientLoader.selectedRecipientType = type recipientLoader.selectedRecipient = recipient } - visible: !recipientLoader.ready && !isBridgeTx && !!d.selectedHolding + visible: !recipientLoader.ready && !d.isBridgeTx && !!d.selectedHolding } NetworkSelector { @@ -419,7 +439,8 @@ StatusDialog { visible: recipientLoader.ready && !!d.selectedHolding && amountToSendInput.inputNumberValid errorType: d.errorType isLoading: popup.isLoading - isBridgeTx: popup.isBridgeTx + isBridgeTx: d.isBridgeTx + isERC721Transfer: d.isERC721Transfer } FeesView { @@ -442,7 +463,7 @@ StatusDialog { } footer: SendModalFooter { - nextButtonText: popup.isBridgeTx ? qsTr("Bridge") : qsTr("Send") + nextButtonText: d.isBridgeTx ? qsTr("Bridge") : qsTr("Send") maxFiatFees: popup.isLoading ? "..." : popup.currencyStore.formatCurrencyAmount(d.totalFeesInFiat, popup.currencyStore.currentCurrency) totalTimeEstimate: popup.isLoading? "..." : d.totalTimeEstimate pending: d.isPendingTx || popup.isLoading diff --git a/ui/imports/shared/stores/TransactionStore.qml b/ui/imports/shared/stores/TransactionStore.qml index 7eba0c629e..ab6a183f04 100644 --- a/ui/imports/shared/stores/TransactionStore.qml +++ b/ui/imports/shared/stores/TransactionStore.qml @@ -45,8 +45,8 @@ QtObject { globalUtils.copyToClipboard(text) } - function authenticateAndTransfer(from, to, tokenSymbol, amount, uuid) { - walletSectionSendInst.authenticateAndTransfer(from, to, tokenSymbol, amount, uuid) + function authenticateAndTransfer(from, to, tokenSymbol, amount, uuid, sendType) { + walletSectionSendInst.authenticateAndTransfer(from, to, tokenSymbol, amount, uuid, sendType) } function suggestedRoutes(amount, sendType) { @@ -180,15 +180,19 @@ QtObject { } function toggleFromDisabledChains(chainId) { - fromNetworksModel.toggleDisabledChains(chainId) + fromNetworksModel.toggleRouteDisabledChains(chainId) } function toggleToDisabledChains(chainId) { - toNetworksModel.toggleDisabledChains(chainId) + toNetworksModel.toggleRouteDisabledChains(chainId) } - function setDisabledChains(chainId, disabled) { - toNetworksModel.setDisabledChains(chainId, disabled) + function setRouteDisabledChains(chainId, disabled) { + toNetworksModel.setRouteDisabledChains(chainId, disabled) + } + + function setRouteEnabledFromChains(chainId) { + fromNetworksModel.setRouteEnabledFromChains(chainId) } function setSelectedAssetSymbol(symbol) { @@ -199,16 +203,16 @@ QtObject { return fromNetworksModel.getNetworkName(chainId) } - function updatePreferredChains(chainIds) { - walletSectionSendInst.updatePreferredChains(chainIds) + function updateRoutePreferredChains(chainIds) { + walletSectionSendInst.updateRoutePreferredChains(chainIds) } function toggleShowUnPreferredChains() { walletSectionSendInst.toggleShowUnPreferredChains() } - function setAllNetworksAsPreferredChains() { - toNetworksModel.setAllNetworksAsPreferredChains() + function setAllNetworksAsRoutePreferredChains() { + toNetworksModel.setAllNetworksAsRoutePreferredChains() } function lockCard(chainId, amount, lock) { @@ -220,7 +224,7 @@ QtObject { } // TODO: move to nim - function splitAndFormatAddressPrefix(text, isBridgeTx) { + function splitAndFormatAddressPrefix(text, updateInStore) { let address = "" let tempPreferredChains = [] let chainFound = false @@ -236,18 +240,17 @@ QtObject { let chainColor = fromNetworksModel.getNetworkColor(word) if(!!chainColor) { chainFound = true - if(!isBridgeTx) - tempPreferredChains.push(fromNetworksModel.getNetworkChainId(word)) + tempPreferredChains.push(fromNetworksModel.getNetworkChainId(word)) editedText += `%2`.arg(chainColor).arg(word)+':' } } } - if(!isBridgeTx) { + if(updateInStore) { if(!chainFound) - updatePreferredChains(networksModule.getMainnetChainId()) + updateRoutePreferredChains(networksModule.getMainnetChainId()) else - updatePreferredChains(tempPreferredChains.join(":")) + updateRoutePreferredChains(tempPreferredChains.join(":")) } editedText +="

" diff --git a/ui/imports/shared/views/NetworkSelector.qml b/ui/imports/shared/views/NetworkSelector.qml index dfe4d95772..a88605ccce 100644 --- a/ui/imports/shared/views/NetworkSelector.qml +++ b/ui/imports/shared/views/NetworkSelector.qml @@ -30,6 +30,7 @@ Item { property bool errorMode: advancedNetworkRoutingPage.errorMode property bool interactive: true property bool isBridgeTx: false + property bool isERC721Transfer: false property var toNetworksList property int errorType: Constants.NoError @@ -45,6 +46,7 @@ Item { id: tabBar anchors.top: parent.top anchors.horizontalCenter: parent.horizontalCenter + visible: !root.isERC721Transfer StatusSwitchTabButton { text: qsTr("Simple") } @@ -58,12 +60,12 @@ Item { StackLayout { id: stackLayout - anchors.top: tabBar.bottom - anchors.topMargin: Style.current.bigPadding + anchors.top: !root.isERC721Transfer ? tabBar.bottom: parent.top + anchors.topMargin: !root.isERC721Transfer ? Style.current.bigPadding: 0 height: currentIndex == 0 ? networksSimpleRoutingPage.height + networksSimpleRoutingPage.anchors.margins + Style.current.bigPadding: advancedNetworkRoutingPage.height + advancedNetworkRoutingPage.anchors.margins + Style.current.bigPadding width: parent.width - currentIndex: tabBar.currentIndex === 0 ? 0 : 1 + currentIndex: root.isERC721Transfer ? 0: tabBar.currentIndex === 0 ? 0 : 1 Rectangle { id: simple @@ -76,6 +78,7 @@ Item { anchors.margins: Style.current.padding width: stackLayout.width - Style.current.bigPadding isBridgeTx: root.isBridgeTx + isERC721Transfer: root.isERC721Transfer amountToSend: root.amountToSend minReceiveCryptoDecimals: root.minReceiveCryptoDecimals isLoading: root.isLoading @@ -85,7 +88,7 @@ Item { errorType: root.errorType toNetworksList: root.toNetworksList weiToEth: function(wei) { - if(root.selectedAsset !== undefined) + if(!!selectedAsset && root.selectedAsset !== undefined) return parseFloat(store.getWei2Eth(wei, root.selectedAsset.decimals)) } formatCurrencyAmount: root.currencyStore.formatCurrencyAmount @@ -119,7 +122,7 @@ Item { isBridgeTx: root.isBridgeTx errorType: root.errorType weiToEth: function(wei) { - if(selectedAsset !== undefined) + if(!!selectedAsset && selectedAsset !== undefined) return parseFloat(store.getWei2Eth(wei, selectedAsset.decimals)) } } diff --git a/ui/imports/shared/views/NetworksSimpleRoutingView.qml b/ui/imports/shared/views/NetworksSimpleRoutingView.qml index bfc4f6d0a2..bfd355c74f 100644 --- a/ui/imports/shared/views/NetworksSimpleRoutingView.qml +++ b/ui/imports/shared/views/NetworksSimpleRoutingView.qml @@ -20,6 +20,7 @@ RowLayout { property int minReceiveCryptoDecimals: 0 property bool isLoading: false property bool isBridgeTx: false + property bool isERC721Transfer: false property var selectedAccount property var toNetworksList property var weiToEth: function(wei) {} @@ -100,6 +101,8 @@ RowLayout { implicitWidth: 410 title: model.chainName subTitle: { + if(root.isERC721Transfer) + return "" let amountOut = root.weiToEth(model.amountOut) return root.formatCurrencyAmount(amountOut, store.selectedAssetSymbol, {"minDecimals": root.minReceiveCryptoDecimals}) } @@ -140,7 +143,7 @@ RowLayout { onClicked: gasRectangle.toggle() } onCheckedChanged: { - store.setDisabledChains(chainId, !gasRectangle.checked) + store.setRouteDisabledChains(chainId, !gasRectangle.checked) if(checked) root.reCalculateSuggestedRoute() } @@ -150,7 +153,7 @@ RowLayout { height: card.height } Component.onCompleted: { - store.setDisabledChains(chainId, !gasRectangle.checked) + store.setRouteDisabledChains(chainId, !gasRectangle.checked) if(index === (repeater.count -1)) root.reCalculateSuggestedRoute() } diff --git a/ui/imports/shared/views/RecipientView.qml b/ui/imports/shared/views/RecipientView.qml index 1ecf7f46f4..65f69fbbd5 100644 --- a/ui/imports/shared/views/RecipientView.qml +++ b/ui/imports/shared/views/RecipientView.qml @@ -16,6 +16,7 @@ Loader { id: root property var store + property bool isERC721Transfer property bool isBridgeTx: false property bool interactive: true property var selectedAsset @@ -35,10 +36,12 @@ Loader { onSelectedRecipientChanged: { root.isLoading() d.waitTimer.restart() - if(!root.isBridgeTx) - root.store.updatePreferredChains(root.selectedRecipient.preferredSharingChainIds) - else - root.store.setAllNetworksAsPreferredChains() + if(!isERC721Transfer) { + if(!root.isBridgeTx) + root.store.updateRoutePreferredChains(root.selectedRecipient.preferredSharingChainIds) + else + root.store.setAllNetworksAsRoutePreferredChains() + } if(!!root.selectedRecipient && root.selectedRecipientType !== TabAddressSelectorView.Type.None) { switch(root.selectedRecipientType) { case TabAddressSelectorView.Type.SavedAddress: { @@ -88,10 +91,10 @@ Loader { if(!!root.item.input) root.item.input.text = root.resolvedENSAddress root.addressText = root.resolvedENSAddress - store.splitAndFormatAddressPrefix(root.address, root.isBridgeTx) + store.splitAndFormatAddressPrefix(root.address, !root.isBridgeTx && !isERC721Transfer) } else { let address = d.getAddress() - let result = store.splitAndFormatAddressPrefix(address, root.isBridgeTx) + let result = store.splitAndFormatAddressPrefix(address, !root.isBridgeTx && !isERC721Transfer) if(!!result.address) { root.addressText = result.address if(!!root.item.input) diff --git a/ui/imports/utils/Constants.qml b/ui/imports/utils/Constants.qml index 8335472dff..cb09e62c94 100644 --- a/ui/imports/utils/Constants.qml +++ b/ui/imports/utils/Constants.qml @@ -983,7 +983,8 @@ QtObject { ENSRelease, ENSSetPubKey, StickersBuy, - Bridge + Bridge, + ERC721Transfer } enum ErrorType { diff --git a/vendor/status-go b/vendor/status-go index 83d1354845..ea29c03465 160000 --- a/vendor/status-go +++ b/vendor/status-go @@ -1 +1 @@ -Subproject commit 83d13548458c60d14eba6efdd92a928f9f0050a1 +Subproject commit ea29c034658d8b82c24fde0e369060642b38d4c3