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 f3824d794e..4df1f267dd 100644 --- a/src/app/modules/main/wallet_section/send/io_interface.nim +++ b/src/app/modules/main/wallet_section/send/io_interface.nim @@ -44,7 +44,7 @@ method stopUpdatesForSuggestedRoute*(self: AccessInterface) {.base.} = method suggestedRoutesReady*(self: AccessInterface, uuid: string, suggestedRoutes: SuggestedRoutesDto, errCode: string, errDescription: string) {.base.} = raise newException(ValueError, "No implementation available") -method authenticateAndTransferV2*(self: AccessInterface, fromAddr: string, uuid: string, slippagePercentage: float) {.base.} = +method authenticateAndTransfer*(self: AccessInterface, fromAddr: string, uuid: string, slippagePercentage: float) {.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 5d7ac62ffb..137add50a1 100644 --- a/src/app/modules/main/wallet_section/send/module.nim +++ b/src/app/modules/main/wallet_section/send/module.nim @@ -26,17 +26,13 @@ const authenticationCanceled* = "authenticationCanceled" # Shouldn't be public ever, use only within this module. type TmpSendTransactionDetails = object - fromAddr: string fromAddrPath: string - toAddr: string - assetKey: string - toAssetKey: string - paths: seq[TransactionPathDto] uuid: string sendType: SendType + pin: string + password: string + txHashBeingProcessed: string resolvedSignatures: TransactionsSignatures - tokenName: string - isOwnerToken: bool slippagePercentage: float type @@ -48,9 +44,7 @@ type controller: controller.Controller moduleLoaded: bool tmpSendTransactionDetails: TmpSendTransactionDetails - tmpPin: string - tmpPassword: string - tmpTxHashBeingProcessed: string + tmpKeepPinPass: bool # Forward declaration method getTokenBalance*(self: Module, address: string, chainId: int, tokensKey: string): CurrencyAmount @@ -79,12 +73,15 @@ method delete*(self: Module) = self.view.delete self.controller.delete -proc clearTmpData(self: Module) = - self.tmpPin = "" - self.tmpPassword = "" - self.tmpTxHashBeingProcessed = "" +proc clearTmpData(self: Module, keepPinPass = false) = + if keepPinPass: + self.tmpSendTransactionDetails = TmpSendTransactionDetails( + sendType: self.tmpSendTransactionDetails.sendType, + pin: self.tmpSendTransactionDetails.pin, + password: self.tmpSendTransactionDetails.password + ) + return self.tmpSendTransactionDetails = TmpSendTransactionDetails() - writeStackTrace() proc convertSendToNetworkToNetworkItem(self: Module, network: SendToNetwork): NetworkRouteItem = result = initNetworkRouteItem( @@ -183,11 +180,22 @@ method getNetworkItem*(self: Module, chainId: int): network_service_item.Network return nil return networks[0] -method authenticateAndTransferV2*(self: Module, fromAddr: string, uuid: string, slippagePercentage: float) = +proc buildTransactionsFromRoute(self: Module) = + let err = self.controller.buildTransactionsFromRoute(self.tmpSendTransactionDetails.uuid, self.tmpSendTransactionDetails.slippagePercentage) + if err.len > 0: + self.transactionWasSent(uuid = self.tmpSendTransactionDetails.uuid, chainId = 0, approvalTx = false, txHash = "", error = err) + self.clearTmpData() + +method authenticateAndTransfer*(self: Module, fromAddr: string, uuid: string, slippagePercentage: float) = self.tmpSendTransactionDetails.uuid = uuid self.tmpSendTransactionDetails.slippagePercentage = slippagePercentage self.tmpSendTransactionDetails.resolvedSignatures.clear() + if self.tmpKeepPinPass: + # no need to authenticate again, just send a swap tx + self.buildTransactionsFromRoute() + return + let kp = self.controller.getKeypairByAccountAddress(fromAddr) if kp.migratedToKeycard(): let accounts = kp.accounts.filter(acc => cmpIgnoreCase(acc.address, fromAddr) == 0) @@ -203,12 +211,9 @@ method onUserAuthenticated*(self: Module, password: string, pin: string) = self.transactionWasSent(uuid = self.tmpSendTransactionDetails.uuid, chainId = 0, approvalTx = false, txHash = "", error = authenticationCanceled) self.clearTmpData() else: - self.tmpPin = pin - self.tmpPassword = password - let err = self.controller.buildTransactionsFromRoute(self.tmpSendTransactionDetails.uuid, self.tmpSendTransactionDetails.slippagePercentage) - if err.len > 0: - self.transactionWasSent(uuid = self.tmpSendTransactionDetails.uuid, chainId = 0, approvalTx = false, txHash = "", error = err) - self.clearTmpData() + self.tmpSendTransactionDetails.pin = pin + self.tmpSendTransactionDetails.password = password + self.buildTransactionsFromRoute() proc sendSignedTransactions*(self: Module) = try: @@ -226,17 +231,17 @@ proc sendSignedTransactions*(self: Module) = self.clearTmpData() proc signOnKeycard(self: Module) = - self.tmpTxHashBeingProcessed = "" + self.tmpSendTransactionDetails.txHashBeingProcessed = "" for h, (r, s, v) in self.tmpSendTransactionDetails.resolvedSignatures.pairs: if r.len != 0 and s.len != 0 and v.len != 0: continue - self.tmpTxHashBeingProcessed = h - var txForKcFlow = self.tmpTxHashBeingProcessed + self.tmpSendTransactionDetails.txHashBeingProcessed = h + var txForKcFlow = self.tmpSendTransactionDetails.txHashBeingProcessed if txForKcFlow.startsWith("0x"): txForKcFlow = txForKcFlow[2..^1] - self.controller.runSignFlow(self.tmpPin, self.tmpSendTransactionDetails.fromAddrPath, txForKcFlow) + self.controller.runSignFlow(self.tmpSendTransactionDetails.pin, self.tmpSendTransactionDetails.fromAddrPath, txForKcFlow) break - if self.tmpTxHashBeingProcessed.len == 0: + if self.tmpSendTransactionDetails.txHashBeingProcessed.len == 0: self.sendSignedTransactions() self.clearTmpData() @@ -265,7 +270,7 @@ method prepareSignaturesForTransactions*(self:Module, txForSigning: RouterTransa self.tmpSendTransactionDetails.resolvedSignatures[h] = ("", "", "") self.signOnKeycard() else: - let finalPassword = hashPassword(self.tmpPassword) + let finalPassword = hashPassword(self.tmpSendTransactionDetails.password) for h in txForSigning.signingDetails.hashes: self.tmpSendTransactionDetails.resolvedSignatures[h] = ("", "", "") var @@ -288,19 +293,18 @@ method onTransactionSigned*(self: Module, keycardFlowType: string, keycardEvent: self.transactionWasSent(uuid = self.tmpSendTransactionDetails.uuid, chainId = 0, approvalTx = false, txHash = "", error = err) self.clearTmpData() return - self.tmpSendTransactionDetails.resolvedSignatures[self.tmpTxHashBeingProcessed] = (keycardEvent.txSignature.r, + self.tmpSendTransactionDetails.resolvedSignatures[self.tmpSendTransactionDetails.txHashBeingProcessed] = (keycardEvent.txSignature.r, keycardEvent.txSignature.s, keycardEvent.txSignature.v) self.signOnKeycard() method transactionWasSent*(self: Module, uuid: string, chainId: int = 0, approvalTx: bool = false, txHash: string = "", error: string = "") = + self.tmpKeepPinPass = approvalTx # need to automate the swap flow with approval if txHash.len == 0: self.view.sendTransactionSentSignal(uuid = self.tmpSendTransactionDetails.uuid, chainId = 0, approvalTx = false, txHash = "", error) return self.view.sendTransactionSentSignal(uuid, chainId, approvalTx, txHash, error) method suggestedRoutesReady*(self: Module, uuid: string, suggestedRoutes: SuggestedRoutesDto, errCode: string, errDescription: string) = - self.tmpSendTransactionDetails.paths = suggestedRoutes.best - self.tmpSendTransactionDetails.slippagePercentage = 0 let paths = suggestedRoutes.best.map(x => self.convertTransactionPathDtoToSuggestedRouteItem(x)) let suggestedRouteModel = newSuggestedRouteModel() suggestedRouteModel.setItems(paths) @@ -332,6 +336,7 @@ method suggestedRoutes*(self: Module, disabledToChainIDs: seq[int] = @[], lockedInAmounts: Table[string, string] = initTable[string, string](), extraParamsTable: Table[string, string] = initTable[string, string]()) = + self.tmpSendTransactionDetails.sendType = sendType self.controller.suggestedRoutes( uuid, sendType, @@ -403,4 +408,5 @@ method splitAndFormatAddressPrefix*(self: Module, text : string, updateInStore: return editedText method transactionSendingComplete*(self: Module, txHash: string, status: string) = + self.clearTmpData(self.tmpKeepPinPass) self.view.sendtransactionSendingCompleteSignal(txHash, status) diff --git a/src/app/modules/main/wallet_section/send/view.nim b/src/app/modules/main/wallet_section/send/view.nim index f80aeec63a..36fab7fe29 100644 --- a/src/app/modules/main/wallet_section/send/view.nim +++ b/src/app/modules/main/wallet_section/send/view.nim @@ -180,7 +180,7 @@ QtObject: slippagePercentage = slippagePercentageString.parseFloat() except: error "parsing slippage failed", slippage=slippagePercentageString - self.delegate.authenticateAndTransferV2(self.selectedSenderAccountAddress, uuid, slippagePercentage) + self.delegate.authenticateAndTransfer(self.selectedSenderAccountAddress, uuid, slippagePercentage) proc suggestedRoutesReady*(self: View, suggestedRoutes: QVariant, errCode: string, errDescription: string) {.signal.} proc setTransactionRoute*(self: View, routes: TransactionRoutes, errCode: string, errDescription: string) = diff --git a/src/app_service/service/ens/service.nim b/src/app_service/service/ens/service.nim index 6312505ae4..3b24c24860 100644 --- a/src/app_service/service/ens/service.nim +++ b/src/app_service/service/ens/service.nim @@ -129,7 +129,6 @@ QtObject: break if ensDto.username.len == 0: - error "Error updating ens username status", message = "no ens username found for transactionHash: " & transactionHash return let key = makeKey(ensDto.username, chainId) diff --git a/storybook/qmlTests/tests/tst_SwapModal.qml b/storybook/qmlTests/tests/tst_SwapModal.qml index b889db5f4e..83885b85e7 100644 --- a/storybook/qmlTests/tests/tst_SwapModal.qml +++ b/storybook/qmlTests/tests/tst_SwapModal.qml @@ -363,6 +363,8 @@ Item { verify(!!headerContentItemEmoji) compare(headerContentItemEmoji.asset.emoji, swapAdaptor.nonWatchAccounts.get(i).emoji) + waitForRendering(amountToSendInput) + verify(amountToSendInput.cursorVisible) } closeAndVerfyModal() @@ -1399,6 +1401,7 @@ Item { launchAndVerfyModal() waitForRendering(payPanel) waitForRendering(receivePanel) + waitForRendering(payAmountToSendInput) let paytokenSelectorContentItemText = findChild(payPanel, "tokenSelectorContentItemText") verify(!!paytokenSelectorContentItemText) diff --git a/storybook/qmlTests/tests/tst_SwapSignModal.qml b/storybook/qmlTests/tests/tst_SwapSignModal.qml index 710d2a8098..9a4cb74f3c 100644 --- a/storybook/qmlTests/tests/tst_SwapSignModal.qml +++ b/storybook/qmlTests/tests/tst_SwapSignModal.qml @@ -99,8 +99,7 @@ Item { controlUnderTest.toTokenAmount = "1.42" controlUnderTest.toTokenContractAddress = "0xdeadcaff" - // title & subtitle - compare(controlUnderTest.title, qsTr("Sign Swap")) + // subtitle compare(controlUnderTest.subtitle, qsTr("%1 to %2").arg(controlUnderTest.formatBigNumber(controlUnderTest.fromTokenAmount, controlUnderTest.fromTokenSymbol)) .arg(controlUnderTest.formatBigNumber(controlUnderTest.toTokenAmount, controlUnderTest.toTokenSymbol))) diff --git a/ui/app/AppLayouts/Wallet/popups/SignTransactionModalBase.qml b/ui/app/AppLayouts/Wallet/popups/SignTransactionModalBase.qml index 9661c670cc..62210c1b12 100644 --- a/ui/app/AppLayouts/Wallet/popups/SignTransactionModalBase.qml +++ b/ui/app/AppLayouts/Wallet/popups/SignTransactionModalBase.qml @@ -36,8 +36,12 @@ StatusDialog { property Component headerIconComponent property bool feesLoading + + property string signButtonText: qsTr("Sign") property bool signButtonEnabled: true + property string closeButtonText: qsTr("Close") + property date requestTimestamp: new Date() property int expirationSeconds property bool hasExpiryDate: false @@ -61,14 +65,14 @@ StatusDialog { visible: !root.hasExpiryDate || !countdownPill.isExpired icon.name: Constants.authenticationIconByType[root.loginType] disabledColor: Theme.palette.directColor8 - text: qsTr("Sign") + text: root.signButtonText onClicked: root.accept() // close and emit accepted() signal } StatusButton { objectName: "closeButton" id: closeButton visible: root.hasExpiryDate && countdownPill.isExpired - text: qsTr("Close") + text: root.closeButtonText onClicked: root.close() } } diff --git a/ui/app/AppLayouts/Wallet/popups/swap/SwapModal.qml b/ui/app/AppLayouts/Wallet/popups/swap/SwapModal.qml index 36df35f321..e871fa41c4 100644 --- a/ui/app/AppLayouts/Wallet/popups/swap/SwapModal.qml +++ b/ui/app/AppLayouts/Wallet/popups/swap/SwapModal.qml @@ -397,10 +397,12 @@ StatusDialog { : Constants.authenticationIconByType[root.loginType] text: { if(root.swapAdaptor.validSwapProposalReceived) { - if (root.swapAdaptor.approvalPending) { - return qsTr("Approving %1").arg(fromTokenSymbol) - } else if(root.swapAdaptor.swapOutputData.approvalNeeded) { - return qsTr("Approve %1").arg(fromTokenSymbol) + if(root.swapAdaptor.swapOutputData.approvalNeeded) { + if (root.swapAdaptor.approvalPending) { + return qsTr("Approving %1").arg(fromTokenSymbol) + } else if(!root.swapAdaptor.approvalSuccessful) { + return qsTr("Approve %1").arg(fromTokenSymbol) + } } } return qsTr("Swap") @@ -416,7 +418,7 @@ StatusDialog { onClicked: { if (root.swapAdaptor.validSwapProposalReceived) { d.addMetricsEvent("next button pressed") - if (root.swapAdaptor.swapOutputData.approvalNeeded) + if (root.swapAdaptor.swapOutputData.approvalNeeded && !root.swapAdaptor.approvalSuccessful) Global.openPopup(swapApproveModalComponent) else Global.openPopup(swapSignModalComponent) @@ -483,6 +485,9 @@ StatusDialog { SwapSignModal { destroyOnClose: true + title: root.swapAdaptor.swapOutputData.approvalNeeded && root.swapAdaptor.approvalSuccessful? qsTr("Swap") : qsTr("Sign Swap") + signButtonText: root.swapAdaptor.swapOutputData.approvalNeeded && root.swapAdaptor.approvalSuccessful? qsTr("Swap") : qsTr("Sign") + formatBigNumber: (number, symbol, noSymbolOption) => root.swapAdaptor.currencyStore.formatBigNumber(number, symbol, noSymbolOption) loginType: root.swapAdaptor.selectedAccount.migratedToKeycard ? Constants.LoginType.Keycard : root.loginType diff --git a/ui/app/AppLayouts/Wallet/popups/swap/SwapModalAdaptor.qml b/ui/app/AppLayouts/Wallet/popups/swap/SwapModalAdaptor.qml index 09618e6e70..2d72b88b99 100644 --- a/ui/app/AppLayouts/Wallet/popups/swap/SwapModalAdaptor.qml +++ b/ui/app/AppLayouts/Wallet/popups/swap/SwapModalAdaptor.qml @@ -87,8 +87,6 @@ QObject { readonly property bool isEthBalanceInsufficient: d.isEthBalanceInsufficient readonly property bool isTokenBalanceInsufficient: d.isTokenBalanceInsufficient - signal suggestedRoutesReady() - QtObject { id: d @@ -252,11 +250,10 @@ QObject { root.swapOutputData.hasError = true } root.swapOutputData.hasError = root.swapOutputData.hasError || root.swapOutputData.errCode !== "" - root.suggestedRoutesReady() } function onTransactionSent(uuid, chainId, approvalTx, txHash, error) { - if(root.swapOutputData.approvalNeeded) { + if(root.swapOutputData.approvalNeeded && !root.approvalSuccessful) { if (uuid !== d.uuid || !!error) { root.approvalPending = false root.approvalSuccessful = false @@ -272,10 +269,6 @@ QObject { root.approvalPending = false root.approvalSuccessful = status == "Success" // TODO: make a all tx statuses Constants (success, pending, failed) d.txHash = "" - - if (root.approvalSuccessful) { - root.swapOutputData.approvalNeeded = false - } } } } @@ -335,14 +328,10 @@ QObject { function sendApproveTx() { root.approvalPending = true - const accountAddress = root.swapFormData.selectedAccountAddress - root.swapStore.authenticateAndTransfer(d.uuid, "") } function sendSwapTx() { - const accountAddress = root.swapFormData.selectedAccountAddress - root.swapStore.authenticateAndTransfer(d.uuid, root.swapFormData.selectedSlippage) } } diff --git a/ui/app/AppLayouts/Wallet/popups/swap/SwapSignModal.qml b/ui/app/AppLayouts/Wallet/popups/swap/SwapSignModal.qml index e4da1f3358..349f11776a 100644 --- a/ui/app/AppLayouts/Wallet/popups/swap/SwapSignModal.qml +++ b/ui/app/AppLayouts/Wallet/popups/swap/SwapSignModal.qml @@ -45,7 +45,6 @@ SignTransactionModalBase { required property string serviceProviderURL required property string serviceProviderTandCUrl - title: qsTr("Sign Swap") //: e.g. (swap) 100 DAI to 100 USDT subtitle: qsTr("%1 to %2").arg(formatBigNumber(fromTokenAmount, fromTokenSymbol)).arg(formatBigNumber(toTokenAmount, toTokenSymbol))