From ccd5a416cef7cb4bf3bfdb32d86e06bfa7273f76 Mon Sep 17 00:00:00 2001 From: Sale Djenic Date: Tue, 5 Mar 2024 15:08:49 +0100 Subject: [PATCH] fix(wallet): erc20 transfer with multiple path Fixes: #13759 --- .../main/wallet_section/send/controller.nim | 4 +- .../main/wallet_section/send/io_interface.nim | 2 +- .../main/wallet_section/send/module.nim | 7 +- .../modules/main/wallet_section/send/view.nim | 5 +- .../service/transaction/service.nim | 112 +++++++++--------- ui/imports/shared/popups/send/SendModal.qml | 4 +- .../shared/stores/send/TransactionStore.qml | 6 +- 7 files changed, 71 insertions(+), 69 deletions(-) diff --git a/src/app/modules/main/wallet_section/send/controller.nim b/src/app/modules/main/wallet_section/send/controller.nim index f7d4f35570..f457e803bf 100644 --- a/src/app/modules/main/wallet_section/send/controller.nim +++ b/src/app/modules/main/wallet_section/send/controller.nim @@ -115,9 +115,9 @@ proc suggestedRoutes*(self: Controller, accountFrom: string, accountTo: string, return suggestedRoutes.toJson() proc transfer*(self: Controller, from_addr: string, to_addr: string, assetKey: string, - value: string, uuid: string, selectedRoutes: seq[TransactionPathDto], password: string, sendType: SendType, + uuid: string, selectedRoutes: seq[TransactionPathDto], password: string, sendType: SendType, usePassword: bool, doHashing: bool, tokenName: string, isOwnerToken: bool) = - self.transactionService.transfer(from_addr, to_addr, assetKey, value, uuid, selectedRoutes, password, sendType, + self.transactionService.transfer(from_addr, to_addr, assetKey, uuid, selectedRoutes, password, sendType, usePassword, doHashing, tokenName, isOwnerToken) proc proceedWithTransactionsSignatures*(self: Controller, fromAddr: string, toAddr: string, uuid: string, 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 430fd54901..bfc94ae223 100644 --- a/src/app/modules/main/wallet_section/send/io_interface.nim +++ b/src/app/modules/main/wallet_section/send/io_interface.nim @@ -32,7 +32,7 @@ method suggestedRoutesReady*(self: AccessInterface, suggestedRoutes: SuggestedRo raise newException(ValueError, "No implementation available") method authenticateAndTransfer*(self: AccessInterface, from_addr: string, to_addr: string, - assetKey: string, value: string, uuid: string, sendType: SendType, selectedTokenName: string, selectedTokenIsOwnerToken: bool) {.base.} = + assetKey: string, uuid: string, sendType: SendType, selectedTokenName: string, selectedTokenIsOwnerToken: bool) {.base.} = raise newException(ValueError, "No implementation available") method onUserAuthenticated*(self: AccessInterface, password: string, pin: 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 c22d295277..e113c6d542 100644 --- a/src/app/modules/main/wallet_section/send/module.nim +++ b/src/app/modules/main/wallet_section/send/module.nim @@ -33,7 +33,6 @@ type TmpSendTransactionDetails = object fromAddrPath: string toAddr: string assetKey: string - value: string paths: seq[TransactionPathDto] uuid: string sendType: SendType @@ -255,11 +254,11 @@ method viewDidLoad*(self: Module) = method getTokenBalance*(self: Module, address: string, chainId: int, tokensKey: string): CurrencyAmount = return self.controller.getTokenBalance(address, chainId, tokensKey) -method authenticateAndTransfer*(self: Module, fromAddr: string, toAddr: string, assetKey: string, value: string, uuid: string, sendType: SendType, selectedTokenName: string, selectedTokenIsOwnerToken: bool) = +method authenticateAndTransfer*(self: Module, fromAddr: string, toAddr: string, assetKey: string, uuid: string, + sendType: SendType, selectedTokenName: string, selectedTokenIsOwnerToken: bool) = self.tmpSendTransactionDetails.fromAddr = fromAddr self.tmpSendTransactionDetails.toAddr = toAddr self.tmpSendTransactionDetails.assetKey = assetKey - self.tmpSendTransactionDetails.value = value self.tmpSendTransactionDetails.uuid = uuid self.tmpSendTransactionDetails.sendType = sendType self.tmpSendTransactionDetails.fromAddrPath = "" @@ -287,7 +286,7 @@ method onUserAuthenticated*(self: Module, password: string, pin: string) = let usePassword = self.tmpSendTransactionDetails.fromAddrPath.len == 0 self.controller.transfer( self.tmpSendTransactionDetails.fromAddr, self.tmpSendTransactionDetails.toAddr, - self.tmpSendTransactionDetails.assetKey, self.tmpSendTransactionDetails.value, self.tmpSendTransactionDetails.uuid, + self.tmpSendTransactionDetails.assetKey, self.tmpSendTransactionDetails.uuid, self.tmpSendTransactionDetails.paths, password, self.tmpSendTransactionDetails.sendType, usePassword, doHashing, self.tmpSendTransactionDetails.tokenName, self.tmpSendTransactionDetails.isOwnerToken ) diff --git a/src/app/modules/main/wallet_section/send/view.nim b/src/app/modules/main/wallet_section/send/view.nim index b1550e20bc..c087d84847 100644 --- a/src/app/modules/main/wallet_section/send/view.nim +++ b/src/app/modules/main/wallet_section/send/view.nim @@ -205,8 +205,9 @@ QtObject: proc sendTransactionSentSignal*(self: View, chainId: int, txHash: string, uuid: string, error: string) = self.transactionSent(chainId, txHash, uuid, error) - proc authenticateAndTransfer*(self: View, value: string, uuid: string) {.slot.} = - self.delegate.authenticateAndTransfer(self.selectedSenderAccount.address(), self.selectedRecipient, self.selectedAssetKey, value, uuid, self.sendType, self.selectedTokenName, self.selectedTokenIsOwnerToken) + proc authenticateAndTransfer*(self: View, uuid: string) {.slot.} = + self.delegate.authenticateAndTransfer(self.selectedSenderAccount.address(), self.selectedRecipient, self.selectedAssetKey, + uuid, self.sendType, self.selectedTokenName, self.selectedTokenIsOwnerToken) proc suggestedRoutesReady*(self: View, suggestedRoutes: QVariant) {.signal.} proc setTransactionRoute*(self: View, routes: TransactionRoutes) = diff --git a/src/app_service/service/transaction/service.nim b/src/app_service/service/transaction/service.nim index 5f778f630b..93e4c4b4c2 100644 --- a/src/app_service/service/transaction/service.nim +++ b/src/app_service/service/transaction/service.nim @@ -327,14 +327,13 @@ QtObject: from_addr: string, to_addr: string, tokenSymbol: string, - value: string, uuid: string, routes: seq[TransactionPathDto], password: string ) = try: var paths: seq[TransactionBridgeDto] = @[] - let amountToSend = value.parse(Uint256) + var totalAmountToSend: UInt256 let toAddress = parseAddress(to_addr) for route in routes: @@ -347,6 +346,7 @@ QtObject: if route.approvalRequired: paths.add(self.createApprovalPath(route, from_addr, toAddress, gasFees)) + totalAmountToSend += route.amountIn txData = ens_utils.buildTransaction(parseAddress(from_addr), route.amountIn, $route.gasAmount, gasFees, route.gasFees.eip1559Enabled, $route.gasFees.maxPriorityFeePerGas, $route.gasFees.maxFeePerGasM) txData.to = parseAddress(to_addr).some @@ -359,7 +359,7 @@ QtObject: toAddress: to_addr, fromAsset: tokenSymbol, toAsset: tokenSymbol, - fromAmount: "0x" & amountToSend.toHex, + fromAmount: "0x" & totalAmountToSend.toHex, multiTxType: transactions.MultiTransactionType.MultiTransactionSend, ), paths, @@ -376,7 +376,6 @@ QtObject: from_addr: string, to_addr: string, tokenSymbol: string, - value: string, uuid: string, routes: seq[TransactionPathDto], password: string, @@ -384,78 +383,81 @@ QtObject: tokenName: string, isOwnerToken: bool ) = - try: - var paths: seq[TransactionBridgeDto] = @[] - var chainID = 0 - - if(routes.len > 0): - chainID = routes[0].fromNetwork.chainID - - var toAddress: Address - var tokenSym = tokenSymbol - let amountToSend = value.parse(Uint256) - - if self.isCollectiblesTransfer(sendType): - 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.getTokenBySymbolByTokensKey(tokenSym) - if token != nil: - for addressPerChain in token.addressPerChainId: - if addressPerChain.chainId == network.chainId: - toAddress = parseAddress(addressPerChain.address) - - let transfer = Transfer( - to: parseAddress(to_addr), - value: amountToSend, + var + toContractAddress: Address + paths: seq[TransactionBridgeDto] = @[] + totalAmountToSend: UInt256 + mtCommand = MultiTransactionCommandDto( + fromAddress: from_addr, + toAddress: to_addr, + fromAsset: tokenSymbol, + toAsset: tokenSymbol, + multiTxType: transactions.MultiTransactionType.MultiTransactionSend, ) - let data = ERC20_procS.toTable["transfer"].encodeAbi(transfer) + if self.isCollectiblesTransfer(sendType): + let contract_tokenId = mtCommand.toAsset.split(":") + if contract_tokenId.len == 2: + toContractAddress = parseAddress(contract_tokenId[0]) + mtCommand.fromAsset = contract_tokenId[1] + mtCommand.toAsset = contract_tokenId[1] + + try: for route in routes: var txData = TransactionDataDto() var gasFees: string = "" - if(not route.gasFees.eip1559Enabled): + if not self.isCollectiblesTransfer(sendType): + let token = self.tokenService.getTokenBySymbolByTokensKey(mtCommand.toAsset) + if token != nil: + for addressPerChain in token.addressPerChainId: + if addressPerChain.chainId == route.toNetwork.chainId: + toContractAddress = parseAddress(addressPerChain.address) + break + + if not route.gasFees.eip1559Enabled: gasFees = $route.gasFees.gasPrice if route.approvalRequired: - paths.add(self.createApprovalPath(route, from_addr, toAddress, gasFees)) + let approvalPath = self.createApprovalPath(route, mtCommand.fromAddress, toContractAddress, gasFees) + paths.add(approvalPath) - txData = ens_utils.buildTokenTransaction(parseAddress(from_addr), toAddress, - $route.gasAmount, gasFees, route.gasFees.eip1559Enabled, $route.gasFees.maxPriorityFeePerGas, $route.gasFees.maxFeePerGasM) + totalAmountToSend += route.amountIn + let transfer = Transfer( + to: parseAddress(mtCommand.toAddress), + value: route.amountIn, + ) + let data = ERC20_procS.toTable["transfer"].encodeAbi(transfer) + + txData = ens_utils.buildTokenTransaction( + parseAddress(mtCommand.fromAddress), + toContractAddress, + $route.gasAmount, + gasFees, + route.gasFees.eip1559Enabled, + $route.gasFees.maxPriorityFeePerGas, + $route.gasFees.maxFeePerGasM + ) txData.data = data - paths.add(self.createPath(route, txData, tokenSym, to_addr)) + let path = self.createPath(route, txData, mtCommand.toAsset, mtCommand.toAddress) + paths.add(path) - var response: RpcResponse[JsonNode] - response = transactions.createMultiTransaction( - MultiTransactionCommandDto( - fromAddress: from_addr, - toAddress: to_addr, - fromAsset: tokenSym, - toAsset: tokenSym, - fromAmount: "0x" & amountToSend.toHex, - multiTxType: transactions.MultiTransactionType.MultiTransactionSend, - ), - paths, - password, - ) + mtCommand.fromAmount = "0x" & totalAmountToSend.toHex + + let response = transactions.createMultiTransaction(mtCommand, paths, password) if password != "": - self.sendTransactionSentSignal(from_addr, to_addr, uuid, routes, response, err="", tokenName, isOwnerToken) + self.sendTransactionSentSignal(mtCommand.fromAddress, mtCommand.toAddress, uuid, routes, response, err="", tokenName, isOwnerToken) except Exception as e: - self.sendTransactionSentSignal(from_addr, to_addr, uuid, @[], RpcResponse[JsonNode](), fmt"Error sending token transfer transaction: {e.msg}") + self.sendTransactionSentSignal(mtCommand.fromAddress, mtCommand.toAddress, uuid, @[], RpcResponse[JsonNode](), fmt"Error sending token transfer transaction: {e.msg}") proc transfer*( self: Service, fromAddr: string, toAddr: string, assetKey: string, - value: string, uuid: string, selectedRoutes: seq[TransactionPathDto], password: string, @@ -490,9 +492,9 @@ QtObject: isEthTx = true if(isEthTx): - self.transferEth(fromAddr, toAddr, tokenSymbol, value, uuid, selectedRoutes, finalPassword) + self.transferEth(fromAddr, toAddr, tokenSymbol, uuid, selectedRoutes, finalPassword) else: - self.transferToken(fromAddr, toAddr, tokenSymbol, value, uuid, selectedRoutes, finalPassword, sendType, tokenName, isOwnerToken) + self.transferToken(fromAddr, toAddr, tokenSymbol, uuid, selectedRoutes, finalPassword, sendType, tokenName, isOwnerToken) 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/imports/shared/popups/send/SendModal.qml b/ui/imports/shared/popups/send/SendModal.qml index b849695790..fd8f9a2826 100644 --- a/ui/imports/shared/popups/send/SendModal.qml +++ b/ui/imports/shared/popups/send/SendModal.qml @@ -53,7 +53,7 @@ StatusDialog { property var sendTransaction: function() { d.isPendingTx = true - popup.store.authenticateAndTransfer(amountToSendInput.cryptoValueToSend, d.uuid) + popup.store.authenticateAndTransfer(d.uuid) } property var recalculateRoutesAndFees: Backpressure.debounce(popup, 600, function() { @@ -146,7 +146,7 @@ StatusDialog { if(symbol !== "ETH") { return value } - + return value - Math.max(0.0001, Math.min(0.01, value * 0.1)) } } diff --git a/ui/imports/shared/stores/send/TransactionStore.qml b/ui/imports/shared/stores/send/TransactionStore.qml index 3ffef5b97b..b5e02848d9 100644 --- a/ui/imports/shared/stores/send/TransactionStore.qml +++ b/ui/imports/shared/stores/send/TransactionStore.qml @@ -62,8 +62,8 @@ QtObject { globalUtils.copyToClipboard(text) } - function authenticateAndTransfer(amount, uuid) { - walletSectionSendInst.authenticateAndTransfer(amount, uuid) + function authenticateAndTransfer(uuid) { + walletSectionSendInst.authenticateAndTransfer(uuid) } function suggestedRoutes(amount) { @@ -155,7 +155,7 @@ QtObject { function assetToSelectorAsset(asset) { return asset } - + function collectibleToSelectorCollectible(collectible) { return { uid: collectible.uid,