From 808ebe85e6dc304edf4f70458cefe7746d5770b2 Mon Sep 17 00:00:00 2001 From: Sale Djenic Date: Thu, 23 Jan 2025 11:52:16 +0100 Subject: [PATCH] feat(wallet): transaction settings added to sign send modal --- src/app/modules/main/module.nim | 3 + .../wallet_section/send_new/controller.nim | 11 +- .../wallet_section/send_new/io_interface.nim | 10 +- .../main/wallet_section/send_new/module.nim | 27 +++ .../wallet_section/send_new/path_item.nim | 83 +++++++- .../wallet_section/send_new/path_model.nim | 36 ++++ .../main/wallet_section/send_new/view.nim | 12 +- src/app_service/service/transaction/dtoV2.nim | 21 ++ .../service/transaction/service.nim | 29 ++- src/backend/wallet.nim | 66 +++++- .../qmlTests/tests/tst_SendSignModal.qml | 31 +++ .../Popups/Dialog/StatusDialogHeader.qml | 32 +++ ui/StatusQ/src/assets.qrc | 1 + .../src/assets/img/icons/settings-advance.svg | 6 + .../popups/SignTransactionModalBase.qml | 13 ++ .../popups/simpleSend/SendSignModal.qml | 165 +++++++++++++++ .../Wallet/stores/TransactionStoreNew.qml | 8 + ui/app/mainui/SendModalHandler.qml | 200 +++++++++++++++++- 18 files changed, 737 insertions(+), 17 deletions(-) create mode 100644 ui/StatusQ/src/assets/img/icons/settings-advance.svg diff --git a/src/app/modules/main/module.nim b/src/app/modules/main/module.nim index 177694313a..8006961304 100644 --- a/src/app/modules/main/module.nim +++ b/src/app/modules/main/module.nim @@ -1725,6 +1725,9 @@ method displayEphemeralNotification*[T](self: Module[T], title: string, subTitle elif details.notificationType == NotificationType.CommunityMemberUnbanned: self.displayEphemeralWithActionNotification(title, "Visit community" , "communities", "", false, EphemeralNotificationType.Success.int, EphemeralActionType.NavigateToCommunityAdmin.int, details.sectionId) + else: + self.displayEphemeralNotification(title, subTitle, "", false, EphemeralNotificationType.Default.int, "", details) + method removeEphemeralNotification*[T](self: Module[T], id: int64) = self.view.ephemeralNotificationModel().removeItemWithId(id) diff --git a/src/app/modules/main/wallet_section/send_new/controller.nim b/src/app/modules/main/wallet_section/send_new/controller.nim index 1ca24d3fc4..f6e4077895 100644 --- a/src/app/modules/main/wallet_section/send_new/controller.nim +++ b/src/app/modules/main/wallet_section/send_new/controller.nim @@ -49,7 +49,7 @@ proc delete*(self: Controller) = proc init*(self: Controller) = self.events.on(SIGNAL_TRANSACTION_SENT) do(e:Args): let args = TransactionArgs(e) - var + var txHash = "" isApprovalTx = false if not args.sentTransaction.isNil: @@ -107,6 +107,15 @@ proc suggestedRoutes*(self: Controller, proc stopSuggestedRoutesAsyncCalculation*(self: Controller) = self.transactionService.stopSuggestedRoutesAsyncCalculation() +proc setFeeMode*(self: Controller, feeMode: int, routerInputParamsUuid: string, pathName: string, chainId: int, + isApprovalTx: bool, communityId: string): string = + return self.transactionService.setFeeMode(feeMode, routerInputParamsUuid, pathName, chainId, isApprovalTx, communityId) + +proc setCustomTxDetails*(self: Controller, nonce: int, gasAmount: int, maxFeesPerGas: string, priorityFee: string, + routerInputParamsUuid: string, pathName: string, chainId: int, isApprovalTx: bool, communityId: string): string = + return self.transactionService.setCustomTxDetails(nonce, gasAmount, maxFeesPerGas, priorityFee, routerInputParamsUuid, + pathName, chainId, isApprovalTx, communityId) + proc getCurrentNetworks*(self: Controller): seq[NetworkItem] = return self.networkService.getCurrentNetworks() diff --git a/src/app/modules/main/wallet_section/send_new/io_interface.nim b/src/app/modules/main/wallet_section/send_new/io_interface.nim index 335460e5b4..2155e0442a 100644 --- a/src/app/modules/main/wallet_section/send_new/io_interface.nim +++ b/src/app/modules/main/wallet_section/send_new/io_interface.nim @@ -44,6 +44,14 @@ method onUserAuthenticated*(self: AccessInterface, password: string, pin: string method suggestedRoutesReady*(self: AccessInterface, uuid: string, routes: seq[TransactionPathDtoV2], errCode: string, errDescription: string) {.base.} = raise newException(ValueError, "No implementation available") +method setFeeMode*(self: AccessInterface, feeMode: int, routerInputParamsUuid: string, pathName: string, chainId: int, + isApprovalTx: bool, communityId: string) {.base.} = + raise newException(ValueError, "No implementation available") + +method setCustomTxDetails*(self: AccessInterface, nonce: int, gasAmount: int, maxFeesPerGas: string, priorityFee: string, + routerInputParamsUuid: string, pathName: string, chainId: int, isApprovalTx: bool, communityId: string) {.base.} = + raise newException(ValueError, "No implementation available") + method transactionWasSent*(self: AccessInterface, uuid: string, chainId: int = 0, approvalTx: bool = false, txHash: string = "", error: string = "") {.base.} = raise newException(ValueError, "No implementation available") @@ -63,4 +71,4 @@ method onTransactionSigned*(self: AccessInterface, keycardFlowType: string, keyc raise newException(ValueError, "No implementation available") method transactionSendingComplete*(self: AccessInterface, txHash: string, status: string) {.base.} = - raise newException(ValueError, "No implementation available") + raise newException(ValueError, "No implementation available") \ No newline at end of file diff --git a/src/app/modules/main/wallet_section/send_new/module.nim b/src/app/modules/main/wallet_section/send_new/module.nim index 0d15ec046c..a9ce12d5db 100644 --- a/src/app/modules/main/wallet_section/send_new/module.nim +++ b/src/app/modules/main/wallet_section/send_new/module.nim @@ -5,6 +5,7 @@ import ../io_interface as delegate_interface import app/global/global_singleton import app/core/eventemitter +import app/core/notifications/notifications_manager import app_service/common/utils import app_service/common/wallet_constants @@ -116,12 +117,20 @@ proc convertTransactionPathDtoV2ToPathItem(self: Module, txPath: TransactionPath amountInLocked = txPath.amountInLocked, amountOut = $txPath.amountOut, suggestedMaxFeesPerGasLowLevel = $txPath.suggestedLevelsForMaxFeesPerGas.low, + suggestedPriorityFeePerGasLowLevel = $txPath.suggestedLevelsForMaxFeesPerGas.lowPriority, suggestedMaxFeesPerGasMediumLevel = $txPath.suggestedLevelsForMaxFeesPerGas.medium, + suggestedPriorityFeePerGasMediumLevel = $txPath.suggestedLevelsForMaxFeesPerGas.mediumPriority, suggestedMaxFeesPerGasHighLevel = $txPath.suggestedLevelsForMaxFeesPerGas.high, + suggestedPriorityFeePerGasHighLevel = $txPath.suggestedLevelsForMaxFeesPerGas.highPriority, suggestedMinPriorityFee = $txPath.suggestedMinPriorityFee, suggestedMaxPriorityFee = $txPath.suggestedMaxPriorityFee, currentBaseFee = $txPath.currentBaseFee, + suggestedTxNonce = $txPath.suggestedTxNonce, + suggestedTxGasAmount = $txPath.suggestedTxGasAmount, + suggestedApprovalTxNonce = $txPath.suggestedApprovalTxNonce, + suggestedApprovalGasAmount = $txPath.suggestedApprovalGasAmount, txNonce = $txPath.txNonce, + txGasFeeMode = txPath.txGasFeeMode, txMaxFeesPerGas = $txPath.txMaxFeesPerGas, txBaseFee = $txPath.txBaseFee, txPriorityFee = $txPath.txPriorityFee, @@ -135,6 +144,7 @@ proc convertTransactionPathDtoV2ToPathItem(self: Module, txPath: TransactionPath approvalAmountRequired = $txPath.approvalAmountRequired, approvalContractAddress = txPath.approvalContractAddress, approvalTxNonce = $txPath.approvalTxNonce, + approvalGasFeeMode = txPath.approvalGasFeeMode, approvalMaxFeesPerGas = $txPath.approvalMaxFeesPerGas, approvalBaseFee = $txPath.approvalBaseFee, approvalPriorityFee = $txPath.approvalPriorityFee, @@ -315,3 +325,20 @@ method stopUpdatesForSuggestedRoute*(self: Module) = method transactionSendingComplete*(self: Module, txHash: string, status: string) = self.view.sendtransactionSendingCompleteSignal(txHash, status) + +method setFeeMode*(self: Module, feeMode: int, routerInputParamsUuid: string, pathName: string, chainId: int, + isApprovalTx: bool, communityId: string) = + let err = self.controller.setFeeMode(feeMode, routerInputParamsUuid, pathName, chainId, isApprovalTx, communityId) + if err.len > 0: + # TODO: translate this, or find a better way to display error at this step (maybe within the popup) + var data = NotificationArgs(title: "Setting fee mode", message: err) + self.events.emit(SIGNAL_DISPLAY_APP_NOTIFICATION, data) + +method setCustomTxDetails*(self: Module, nonce: int, gasAmount: int, maxFeesPerGas: string, priorityFee: string, + routerInputParamsUuid: string, pathName: string, chainId: int, isApprovalTx: bool, communityId: string) = + let err = self.controller.setCustomTxDetails(nonce, gasAmount, maxFeesPerGas, priorityFee, routerInputParamsUuid, pathName, + chainId, isApprovalTx, communityId) + if err.len > 0: + # TODO: translate this, or find a better way to display error at this step (maybe within the popup) + var data = NotificationArgs(title: "Setting custom fee", message: err) + self.events.emit(SIGNAL_DISPLAY_APP_NOTIFICATION, data) \ No newline at end of file diff --git a/src/app/modules/main/wallet_section/send_new/path_item.nim b/src/app/modules/main/wallet_section/send_new/path_item.nim index b52b40bd5d..938a70d834 100644 --- a/src/app/modules/main/wallet_section/send_new/path_item.nim +++ b/src/app/modules/main/wallet_section/send_new/path_item.nim @@ -13,12 +13,20 @@ QtObject: amountInLocked: bool amountOut: string suggestedMaxFeesPerGasLowLevel: string + suggestedPriorityFeePerGasLowLevel: string suggestedMaxFeesPerGasMediumLevel: string + suggestedPriorityFeePerGasMediumLevel: string suggestedMaxFeesPerGasHighLevel: string + suggestedPriorityFeePerGasHighLevel: string suggestedMinPriorityFee: string suggestedMaxPriorityFee: string currentBaseFee: string + suggestedTxNonce: string + suggestedTxGasAmount: string + suggestedApprovalTxNonce: string + suggestedApprovalGasAmount: string txNonce: string + txGasFeeMode: int txMaxFeesPerGas: string txBaseFee: string txPriorityFee: string @@ -33,6 +41,7 @@ QtObject: approvalAmountRequired : string approvalContractAddress: string approvalTxNonce: string + approvalGasFeeMode: int approvalMaxFeesPerGas: string approvalBaseFee: string approvalPriorityFee: string @@ -51,12 +60,20 @@ QtObject: amountInLocked: bool, amountOut: string, suggestedMaxFeesPerGasLowLevel: string, + suggestedPriorityFeePerGasLowLevel: string, suggestedMaxFeesPerGasMediumLevel: string, + suggestedPriorityFeePerGasMediumLevel: string, suggestedMaxFeesPerGasHighLevel: string, + suggestedPriorityFeePerGasHighLevel: string, suggestedMinPriorityFee: string, suggestedMaxPriorityFee: string, currentBaseFee: string, + suggestedTxNonce: string, + suggestedTxGasAmount: string, + suggestedApprovalTxNonce: string, + suggestedApprovalGasAmount: string, txNonce: string, + txGasFeeMode: int, txMaxFeesPerGas: string, txBaseFee: string, txPriorityFee: string, @@ -71,6 +88,7 @@ QtObject: approvalAmountRequired: string, approvalContractAddress: string, approvalTxNonce: string, + approvalGasFeeMode: int, approvalMaxFeesPerGas: string, approvalBaseFee: string, approvalPriorityFee: string, @@ -89,12 +107,20 @@ QtObject: self.amountInLocked = amountInLocked self.amountOut = amountOut self.suggestedMaxFeesPerGasLowLevel = suggestedMaxFeesPerGasLowLevel + self.suggestedPriorityFeePerGasLowLevel = suggestedPriorityFeePerGasLowLevel self.suggestedMaxFeesPerGasMediumLevel = suggestedMaxFeesPerGasMediumLevel + self.suggestedPriorityFeePerGasMediumLevel = suggestedPriorityFeePerGasMediumLevel self.suggestedMaxFeesPerGasHighLevel = suggestedMaxFeesPerGasHighLevel + self.suggestedPriorityFeePerGasHighLevel = suggestedPriorityFeePerGasHighLevel self.suggestedMinPriorityFee = suggestedMinPriorityFee self.suggestedMaxPriorityFee = suggestedMaxPriorityFee self.currentBaseFee = currentBaseFee + self.suggestedTxNonce = suggestedTxNonce + self.suggestedTxGasAmount = suggestedTxGasAmount + self.suggestedApprovalTxNonce = suggestedApprovalTxNonce + self.suggestedApprovalGasAmount = suggestedApprovalGasAmount self.txNonce = txNonce + self.txGasFeeMode = txGasFeeMode self.txMaxFeesPerGas = txMaxFeesPerGas self.txBaseFee = txBaseFee self.txPriorityFee = txPriorityFee @@ -109,6 +135,7 @@ QtObject: self.approvalAmountRequired = approvalAmountRequired self.approvalContractAddress = approvalContractAddress self.approvalTxNonce = approvalTxNonce + self.approvalGasFeeMode = approvalGasFeeMode self.approvalMaxFeesPerGas = approvalMaxFeesPerGas self.approvalBaseFee = approvalBaseFee self.approvalPriorityFee = approvalPriorityFee @@ -130,12 +157,20 @@ QtObject: amountInLocked: bool, amountOut: string, suggestedMaxFeesPerGasLowLevel: string, + suggestedPriorityFeePerGasLowLevel: string, suggestedMaxFeesPerGasMediumLevel: string, + suggestedPriorityFeePerGasMediumLevel: string, suggestedMaxFeesPerGasHighLevel: string, + suggestedPriorityFeePerGasHighLevel: string, suggestedMinPriorityFee: string, suggestedMaxPriorityFee: string, currentBaseFee: string, + suggestedTxNonce: string, + suggestedTxGasAmount: string, + suggestedApprovalTxNonce: string, + suggestedApprovalGasAmount: string, txNonce: string, + txGasFeeMode: int, txMaxFeesPerGas: string, txBaseFee: string, txPriorityFee: string, @@ -150,6 +185,7 @@ QtObject: approvalAmountRequired: string, approvalContractAddress: string, approvalTxNonce: string, + approvalGasFeeMode: int, approvalMaxFeesPerGas: string, approvalBaseFee: string, approvalPriorityFee: string, @@ -160,11 +196,14 @@ QtObject: ): PathItem = new(result, delete) result.setup(processorName, fromChain, toChain, fromToken, toToken, amountIn, amountInLocked, amountOut, - suggestedMaxFeesPerGasLowLevel, suggestedMaxFeesPerGasMediumLevel, suggestedMaxFeesPerGasHighLevel, - suggestedMinPriorityFee, suggestedMaxPriorityFee, currentBaseFee, txNonce, txMaxFeesPerGas, txBaseFee, + suggestedMaxFeesPerGasLowLevel, suggestedPriorityFeePerGasLowLevel, suggestedMaxFeesPerGasMediumLevel, + suggestedPriorityFeePerGasMediumLevel, suggestedMaxFeesPerGasHighLevel, suggestedPriorityFeePerGasHighLevel, + suggestedMinPriorityFee, suggestedMaxPriorityFee, currentBaseFee, suggestedTxNonce, suggestedTxGasAmount, + suggestedApprovalTxNonce, suggestedApprovalGasAmount, txNonce, txGasFeeMode, txMaxFeesPerGas, txBaseFee, txPriorityFee, txGasAmount, txBonderFees, txTokenFees, txEstimatedTime, txFee, txL1Fee, txTotalFee, - approvalRequired, approvalAmountRequired, approvalContractAddress, approvalTxNonce, approvalMaxFeesPerGas, - approvalBaseFee, approvalPriorityFee, approvalGasAmount, approvalEstimatedTime, approvalFee, approvalL1Fee) + approvalRequired, approvalAmountRequired, approvalContractAddress, approvalTxNonce, approvalGasFeeMode, + approvalMaxFeesPerGas, approvalBaseFee, approvalPriorityFee, approvalGasAmount, approvalEstimatedTime, approvalFee, + approvalL1Fee) proc `$`*(self: PathItem): string = result = "PathItem(" @@ -177,12 +216,20 @@ QtObject: result &= "\namountInLocked: " & $self.amountInLocked result &= "\namountOut: " & $self.amountOut result &= "\nsuggestedMaxFeesPerGasLowLevel: " & $self.suggestedMaxFeesPerGasLowLevel + result &= "\nsuggestedPriorityFeePerGasLowLevel: " & $self.suggestedPriorityFeePerGasLowLevel result &= "\nsuggestedMaxFeesPerGasMediumLevel: " & $self.suggestedMaxFeesPerGasMediumLevel + result &= "\nsuggestedPriorityFeePerGasMediumLevel: " & $self.suggestedPriorityFeePerGasMediumLevel result &= "\nsuggestedMaxFeesPerGasHighLevel: " & $self.suggestedMaxFeesPerGasHighLevel + result &= "\nsuggestedPriorityFeePerGasHighLevel: " & $self.suggestedPriorityFeePerGasHighLevel result &= "\nsuggestedMinPriorityFee: " & $self.suggestedMinPriorityFee result &= "\nsuggestedMaxPriorityFee: " & $self.suggestedMaxPriorityFee result &= "\ncurrentBaseFee: " & $self.currentBaseFee + result &= "\nsuggestedTxNonce: " & $self.suggestedTxNonce + result &= "\nsuggestedTxGasAmount: " & $self.suggestedTxGasAmount + result &= "\nsuggestedApprovalTxNonce: " & $self.suggestedApprovalTxNonce + result &= "\nsuggestedApprovalGasAmount: " & $self.suggestedApprovalGasAmount result &= "\ntxNonce: " & $self.txNonce + result &= "\ntxGasFeeMode: " & $self.txGasFeeMode result &= "\ntxMaxFeesPerGas: " & $self.txMaxFeesPerGas result &= "\ntxBaseFee: " & $self.txBaseFee result &= "\ntxPriorityFee: " & $self.txPriorityFee @@ -197,6 +244,7 @@ QtObject: result &= "\napprovalAmountRequired: " & $self.approvalAmountRequired result &= "\napprovalContractAddress: " & $self.approvalContractAddress result &= "\napprovalTxNonce: " & $self.approvalTxNonce + result &= "\napprovalGasFeeMode: " & $self.approvalGasFeeMode result &= "\napprovalMaxFeesPerGas: " & $self.approvalMaxFeesPerGas result &= "\napprovalBaseFee: " & $self.approvalBaseFee result &= "\napprovalPriorityFee: " & $self.approvalPriorityFee @@ -233,12 +281,21 @@ QtObject: proc suggestedMaxFeesPerGasLowLevel*(self: PathItem): string = return self.suggestedMaxFeesPerGasLowLevel + proc suggestedPriorityFeePerGasLowLevel*(self: PathItem): string = + return self.suggestedPriorityFeePerGasLowLevel + proc suggestedMaxFeesPerGasMediumLevel*(self: PathItem): string = return self.suggestedMaxFeesPerGasMediumLevel + proc suggestedPriorityFeePerGasMediumLevel*(self: PathItem): string = + return self.suggestedPriorityFeePerGasMediumLevel + proc suggestedMaxFeesPerGasHighLevel*(self: PathItem): string = return self.suggestedMaxFeesPerGasHighLevel + proc suggestedPriorityFeePerGasHighLevel*(self: PathItem): string = + return self.suggestedPriorityFeePerGasHighLevel + proc suggestedMinPriorityFee*(self: PathItem): string = return self.suggestedMinPriorityFee @@ -248,9 +305,24 @@ QtObject: proc currentBaseFee*(self: PathItem): string = return self.currentBaseFee + proc suggestedTxNonce*(self: PathItem): string = + return self.suggestedTxNonce + + proc suggestedTxGasAmount*(self: PathItem): string = + return self.suggestedTxGasAmount + + proc suggestedApprovalTxNonce*(self: PathItem): string = + return self.suggestedApprovalTxNonce + + proc suggestedApprovalGasAmount*(self: PathItem): string = + return self.suggestedApprovalGasAmount + proc txNonce*(self: PathItem): string = return self.txNonce + proc txGasFeeMode*(self: PathItem): int = + return self.txGasFeeMode + proc txMaxFeesPerGas*(self: PathItem): string = return self.txMaxFeesPerGas @@ -293,6 +365,9 @@ QtObject: proc approvalTxNonce*(self: PathItem): string = return self.approvalTxNonce + proc approvalGasFeeMode*(self: PathItem): int = + return self.approvalGasFeeMode + proc approvalMaxFeesPerGas*(self: PathItem): string = return self.approvalMaxFeesPerGas diff --git a/src/app/modules/main/wallet_section/send_new/path_model.nim b/src/app/modules/main/wallet_section/send_new/path_model.nim index f8494d9d60..4846493d91 100644 --- a/src/app/modules/main/wallet_section/send_new/path_model.nim +++ b/src/app/modules/main/wallet_section/send_new/path_model.nim @@ -16,12 +16,20 @@ type AmountInLocked, AmountOut, SuggestedMaxFeesPerGasLowLevel, + SuggestedPriorityFeePerGasLowLevel, SuggestedMaxFeesPerGasMediumLevel, + SuggestedPriorityFeePerGasMediumLevel, SuggestedMaxFeesPerGasHighLevel, + SuggestedPriorityFeePerGasHighLevel, SuggestedMinPriorityFee, SuggestedMaxPriorityFee, CurrentBaseFee, + SuggestedTxNonce, + SuggestedTxGasAmount, + SuggestedApprovalTxNonce, + SuggestedApprovalGasAmount, TxNonce, + TxGasFeeMode, TxMaxFeesPerGas, TxBaseFee, TxPriorityFee, @@ -36,6 +44,7 @@ type ApprovalAmountRequired, ApprovalContractAddress, ApprovalTxNonce, + ApprovalGasFeeMode, ApprovalMaxFeesPerGas, ApprovalBaseFee, ApprovalPriorityFee, @@ -80,12 +89,20 @@ QtObject: ModelRole.AmountInLocked.int: "amountInLocked", ModelRole.AmountOut.int: "amountOut", ModelRole.SuggestedMaxFeesPerGasLowLevel.int: "suggestedMaxFeesPerGasLowLevel", + ModelRole.SuggestedPriorityFeePerGasLowLevel.int: "suggestedPriorityFeePerGasLowLevel", ModelRole.SuggestedMaxFeesPerGasMediumLevel.int: "suggestedMaxFeesPerGasMediumLevel", + ModelRole.SuggestedPriorityFeePerGasMediumLevel.int: "suggestedPriorityFeePerGasMediumLevel", ModelRole.SuggestedMaxFeesPerGasHighLevel.int: "suggestedMaxFeesPerGasHighLevel", + ModelRole.SuggestedPriorityFeePerGasHighLevel.int: "suggestedPriorityFeePerGasHighLevel", ModelRole.SuggestedMinPriorityFee.int: "suggestedMinPriorityFee", ModelRole.SuggestedMaxPriorityFee.int: "suggestedMaxPriorityFee", ModelRole.CurrentBaseFee.int: "currentBaseFee", + ModelRole.SuggestedTxNonce.int: "suggestedTxNonce", + ModelRole.SuggestedTxGasAmount.int: "suggestedTxGasAmount", + ModelRole.SuggestedApprovalTxNonce.int: "suggestedApprovalTxNonce", + ModelRole.SuggestedApprovalGasAmount.int: "suggestedApprovalGasAmount", ModelRole.TxNonce.int: "txNonce", + ModelRole.TxGasFeeMode.int: "txGasFeeMode", ModelRole.TxMaxFeesPerGas.int: "txMaxFeesPerGas", ModelRole.TxBaseFee.int: "txBaseFee", ModelRole.TxPriorityFee.int: "txPriorityFee", @@ -100,6 +117,7 @@ QtObject: ModelRole.ApprovalAmountRequired.int: "approvalAmountRequired", ModelRole.ApprovalContractAddress.int: "approvalContractAddress", ModelRole.ApprovalTxNonce.int: "approvalTxNonce", + ModelRole.ApprovalGasFeeMode.int: "approvalGasFeeMode", ModelRole.ApprovalMaxFeesPerGas.int: "approvalMaxFeesPerGas", ModelRole.ApprovalBaseFee.int: "approvalBaseFee", ModelRole.ApprovalPriorityFee.int: "approvalPriorityFee", @@ -146,18 +164,34 @@ QtObject: result = newQVariant(item.amountOut) of ModelRole.SuggestedMaxFeesPerGasLowLevel: result = newQVariant(item.suggestedMaxFeesPerGasLowLevel) + of ModelRole.SuggestedPriorityFeePerGasLowLevel: + result = newQVariant(item.suggestedPriorityFeePerGasLowLevel) of ModelRole.SuggestedMaxFeesPerGasMediumLevel: result = newQVariant(item.suggestedMaxFeesPerGasMediumLevel) + of ModelRole.SuggestedPriorityFeePerGasMediumLevel: + result = newQVariant(item.suggestedPriorityFeePerGasMediumLevel) of ModelRole.SuggestedMaxFeesPerGasHighLevel: result = newQVariant(item.suggestedMaxFeesPerGasHighLevel) + of ModelRole.SuggestedPriorityFeePerGasHighLevel: + result = newQVariant(item.suggestedPriorityFeePerGasHighLevel) of ModelRole.SuggestedMinPriorityFee: result = newQVariant(item.suggestedMinPriorityFee) of ModelRole.SuggestedMaxPriorityFee: result = newQVariant(item.suggestedMaxPriorityFee) of ModelRole.CurrentBaseFee: result = newQVariant(item.currentBaseFee) + of ModelRole.SuggestedTxNonce: + result = newQVariant(item.suggestedTxNonce) + of ModelRole.SuggestedTxGasAmount: + result = newQVariant(item.suggestedTxGasAmount) + of ModelRole.SuggestedApprovalTxNonce: + result = newQVariant(item.suggestedApprovalTxNonce) + of ModelRole.SuggestedApprovalGasAmount: + result = newQVariant(item.suggestedApprovalGasAmount) of ModelRole.TxNonce: result = newQVariant(item.txNonce) + of ModelRole.TxGasFeeMode: + result = newQVariant(item.txGasFeeMode) of ModelRole.TxMaxFeesPerGas: result = newQVariant(item.txMaxFeesPerGas) of ModelRole.TxBaseFee: @@ -186,6 +220,8 @@ QtObject: result = newQVariant(item.approvalContractAddress) of ModelRole.ApprovalTxNonce: result = newQVariant(item.approvalTxNonce) + of ModelRole.ApprovalGasFeeMode: + result = newQVariant(item.approvalGasFeeMode) of ModelRole.ApprovalMaxFeesPerGas: result = newQVariant(item.approvalMaxFeesPerGas) of ModelRole.ApprovalBaseFee: diff --git a/src/app/modules/main/wallet_section/send_new/view.nim b/src/app/modules/main/wallet_section/send_new/view.nim index d2bef8d5a7..e9b8164b94 100644 --- a/src/app/modules/main/wallet_section/send_new/view.nim +++ b/src/app/modules/main/wallet_section/send_new/view.nim @@ -74,7 +74,8 @@ QtObject: proc authenticateAndTransfer*(self: View, uuid: string, fromAddr: string, slippagePercentageString: string) {.slot.} = var slippagePercentage: float try: - slippagePercentage = slippagePercentageString.parseFloat() + if slippagePercentageString.len > 0: + slippagePercentage = slippagePercentageString.parseFloat() except: error "parsing slippage failed", slippage=slippagePercentageString self.delegate.authenticateAndTransfer(uuid, fromAddr, slippagePercentage) @@ -90,3 +91,12 @@ QtObject: proc transactionSent*(self: View, uuid: string, chainId: int, approvalTx: bool, txHash: string, error: string) {.signal.} proc sendTransactionSentSignal*(self: View, uuid: string, chainId: int, approvalTx: bool, txHash: string, error: string) = self.transactionSent(uuid, chainId, approvalTx, txHash, error) + + proc setFeeMode*(self: View, feeMode: int, routerInputParamsUuid: string, pathName: string, chainId: int, + isApprovalTx: bool, communityId: string) {.slot.} = + self.delegate.setFeeMode(feeMode, routerInputParamsUuid, pathName, chainId, isApprovalTx, communityId) + + proc setCustomTxDetails*(self: View, nonce: int, gasAmount: int, maxFeesPerGas: string, priorityFee: string, + routerInputParamsUuid: string, pathName: string, chainId: int, isApprovalTx: bool, communityId: string) {.slot.} = + self.delegate.setCustomTxDetails(nonce, gasAmount, maxFeesPerGas, priorityFee, routerInputParamsUuid, pathName, + chainId, isApprovalTx, communityId) \ No newline at end of file diff --git a/src/app_service/service/transaction/dtoV2.nim b/src/app_service/service/transaction/dtoV2.nim index dd67c32122..6268f76cee 100644 --- a/src/app_service/service/transaction/dtoV2.nim +++ b/src/app_service/service/transaction/dtoV2.nim @@ -16,8 +16,11 @@ import backend/network_types, ../token/dto type SuggestedLevelsForMaxFeesPerGasDto* = ref object low*: UInt256 + lowPriority*: UInt256 medium*: UInt256 + mediumPriority*: UInt256 high*: UInt256 + highPriority*: UInt256 type TransactionPathDtoV2* = ref object @@ -36,9 +39,14 @@ type suggestedMinPriorityFee*: UInt256 suggestedMaxPriorityFee*: UInt256 currentBaseFee*: UInt256 + suggestedTxNonce*: UInt256 + suggestedTxGasAmount*: uint64 + suggestedApprovalTxNonce*: UInt256 + suggestedApprovalGasAmount*: uint64 usedContractAddress*: string txNonce*: UInt256 + txGasFeeMode*: int txMaxFeesPerGas*: UInt256 txBaseFee*: UInt256 txPriorityFee*: UInt256 @@ -54,6 +62,7 @@ type approvalAmountRequired*: UInt256 approvalContractAddress*: string approvalTxNonce*: UInt256 + approvalGasFeeMode*: int approvalMaxFeesPerGas*: UInt256 approvalBaseFee*: UInt256 approvalPriorityFee*: UInt256 @@ -70,10 +79,16 @@ proc toSuggestedLevelsForMaxFeesPerGasDto*(jsonObj: JsonNode): SuggestedLevelsFo var value: string if jsonObj.getProp("low", value): result.low = stint.fromHex(UInt256, $value) + if jsonObj.getProp("lowPriority", value): + result.lowPriority = stint.fromHex(UInt256, $value) if jsonObj.getProp("medium", value): result.medium = stint.fromHex(UInt256, $value) + if jsonObj.getProp("mediumPriority", value): + result.mediumPriority = stint.fromHex(UInt256, $value) if jsonObj.getProp("high", value): result.high = stint.fromHex(UInt256, $value) + if jsonObj.getProp("highPriority", value): + result.highPriority = stint.fromHex(UInt256, $value) proc toTransactionPathDtoV2*(jsonObj: JsonNode): TransactionPathDtoV2 = result = TransactionPathDtoV2() @@ -91,8 +106,13 @@ proc toTransactionPathDtoV2*(jsonObj: JsonNode): TransactionPathDtoV2 = result.suggestedMinPriorityFee = stint.fromHex(UInt256, jsonObj{"SuggestedMinPriorityFee"}.getStr) result.suggestedMaxPriorityFee = stint.fromHex(UInt256, jsonObj{"SuggestedMaxPriorityFee"}.getStr) result.currentBaseFee = stint.fromHex(UInt256, jsonObj{"CurrentBaseFee"}.getStr) + result.suggestedTxNonce = stint.fromHex(UInt256, jsonObj{"SuggestedTxNonce"}.getStr) + discard jsonObj.getProp("SuggestedTxGasAmount", result.suggestedTxGasAmount) + result.suggestedApprovalTxNonce = stint.fromHex(UInt256, jsonObj{"SuggestedApprovalTxNonce"}.getStr) + discard jsonObj.getProp("SuggestedApprovalGasAmount", result.suggestedApprovalGasAmount) discard jsonObj.getProp("UsedContractAddress", result.usedContractAddress) result.txNonce = stint.fromHex(UInt256, jsonObj{"TxNonce"}.getStr) + discard jsonObj.getProp("TxGasFeeMode", result.txGasFeeMode) result.txMaxFeesPerGas = stint.fromHex(UInt256, jsonObj{"TxMaxFeesPerGas"}.getStr) result.txBaseFee = stint.fromHex(UInt256, jsonObj{"TxBaseFee"}.getStr) result.txPriorityFee = stint.fromHex(UInt256, jsonObj{"TxPriorityFee"}.getStr) @@ -106,6 +126,7 @@ proc toTransactionPathDtoV2*(jsonObj: JsonNode): TransactionPathDtoV2 = result.approvalAmountRequired = stint.fromHex(UInt256, jsonObj{"ApprovalAmountRequired"}.getStr) discard jsonObj.getProp("ApprovalContractAddress", result.approvalContractAddress) result.approvalTxNonce = stint.fromHex(UInt256, jsonObj{"ApprovalTxNonce"}.getStr) + discard jsonObj.getProp("ApprovalGasFeeMode", result.approvalGasFeeMode) result.approvalMaxFeesPerGas = stint.fromHex(UInt256, jsonObj{"ApprovalMaxFeesPerGas"}.getStr) result.approvalBaseFee = stint.fromHex(UInt256, jsonObj{"ApprovalBaseFee"}.getStr) result.approvalPriorityFee = stint.fromHex(UInt256, jsonObj{"ApprovalPriorityFee"}.getStr) diff --git a/src/app_service/service/transaction/service.nim b/src/app_service/service/transaction/service.nim index 394ebef8cb..3523cdf110 100644 --- a/src/app_service/service/transaction/service.nim +++ b/src/app_service/service/transaction/service.nim @@ -282,7 +282,7 @@ QtObject: transactionType = parseEnum[PendingTransactionTypeDto](watchTxResult["trxType"].getStr) except: discard - + self.events.emit(transactionType.event, ev) transactions.checkRecentHistory(@[chainId], @[address]) @@ -484,6 +484,33 @@ QtObject: except CatchableError as e: error "stopSuggestedRoutesAsyncCalculation", exception=e.msg + proc setFeeMode*(self: Service, feeMode: int, routerInputParamsUuid: string, pathName: string, chainId: int, + isApprovalTx: bool, communityId: string): string = + try: + let err = wallet.setFeeMode(feeMode, routerInputParamsUuid, pathName, chainId, isApprovalTx, communityId) + if err.len > 0: + raise newException(CatchableError, err) + except CatchableError as e: + error "setFeeMode", exception=e.msg + return e.msg + + proc setCustomTxDetails*(self: Service, nonce: int, gasAmount: int, maxFeesPerGas: string, priorityFee: string, + routerInputParamsUuid: string, pathName: string, chainId: int, isApprovalTx: bool, communityId: string): string = + try: + let + bigMaxFeesPerGas = common_utils.stringToUint256(maxFeesPerGas) + bigPriorityFee = common_utils.stringToUint256(priorityFee) + maxFeesPerGasHex = "0x" & eth_utils.stripLeadingZeros(bigMaxFeesPerGas.toHex) + priorityFeeHex = "0x" & eth_utils.stripLeadingZeros(bigPriorityFee.toHex) + + let err = wallet.setCustomTxDetails(nonce, gasAmount, maxFeesPerGasHex, priorityFeeHex, routerInputParamsUuid, pathName, + chainId, isApprovalTx, communityId) + if err.len > 0: + raise newException(CatchableError, err) + except CatchableError as e: + error "setCustomTxDetails", exception=e.msg + return e.msg + proc getEstimatedTime*(self: Service, chainId: int, maxFeePerGas: string): EstimatedTime = try: let response = backend.getTransactionEstimatedTime(chainId, maxFeePerGas).result.getInt diff --git a/src/backend/wallet.nim b/src/backend/wallet.nim index 126615ad13..e0e0556041 100644 --- a/src/backend/wallet.nim +++ b/src/backend/wallet.nim @@ -13,6 +13,7 @@ const GasFeeLow* = 0 GasFeeMedium* = 1 GasFeeHigh* = 2 + GasFeeCustom* = 3 const ExtraKeyUsername* = "username" @@ -148,7 +149,7 @@ proc prepareDataForSuggestedRoutes( "toTokenID": toToken, "disabledFromChainIDs": disabledFromChainIDs, "disabledToChainIDs": disabledToChainIDs, - "gasFeeMode": GasFeeMedium, + "gasFeeMode": GasFeeLow, "fromLockedAmount": lockedInAmounts, "communityRouteInputParams": communityRouteInputParameters, } @@ -202,5 +203,68 @@ proc suggestedRoutesAsyncForCommunities*(uuid: string, sendType: int, accountFro proc stopSuggestedRoutesAsyncCalculation*(): string {.raises: [RpcException].} = let rpcResponse = core.callPrivateRPC("wallet_stopSuggestedRoutesAsyncCalculation") + if isErrorResponse(rpcResponse): + return rpcResponse.error.message + +## Sets the fee mode for the provided path (should not be user for custom fee mode) +## `feeMode` is the fee mode to set for the path, corresponds to the status go `fees.GasFeeMode` type (0: GasFeeLow, 1: GasFeeMedium, 2: GasFeeHigh and 3: GasFeeCustom) +## `routerInputParamsUuid` is the uuid of the router input params +## `pathName` is the name of the path +## `chainId` is the chain id of the network +## `isApprovalTx` is a flag that indicates if the tx is an approval tx - optional +## `communityId` is the id of the community - optional +## returns the error message if any, or an empty string +proc setFeeMode*(feeMode: int, routerInputParamsUuid: string, pathName: string, chainId: int, isApprovalTx: bool = false, + communityId: string = ""): string {.raises: [RpcException].} = + var pathTxIdentity = %* { + "routerInputParamsUuid": routerInputParamsUuid, + "pathName": pathName, + "chainID": chainId, + } + if isApprovalTx: + pathTxIdentity["isApprovalTx"] = %* true + if communityId != "": + pathTxIdentity["communityID"] = %* communityId + + let payload = %* [pathTxIdentity, feeMode] + let rpcResponse = core.callPrivateRPC("wallet_setFeeMode", payload) + if isErrorResponse(rpcResponse): + return rpcResponse.error.message + +## Sets the fee mode for the provided path (should not be user for any but the custom fee mode) +## `nonce` is the nonce of the tx +## `gasAmount` is the gas amount of the tx +## `maxFeesPerGas` is the max fees per gas of the tx +## `priorityFee` is the priority fee of the tx +## `routerInputParamsUuid` is the uuid of the router input params +## `pathName` is the name of the path +## `chainId` is the chain id of the network +## `isApprovalTx` is a flag that indicates if the tx is an approval tx - optional +## `communityId` is the id of the community - optional +## returns the error message if any, or an empty string +proc setCustomTxDetails*(nonce: int, gasAmount: int, maxFeesPerGas: string, priorityFee: string, + routerInputParamsUuid: string, pathName: string, chainId: int, isApprovalTx: bool = false, + communityId: string = ""): string {.raises: [RpcException].} = + + let pathTxCustomParams = %* { + "gasFeeMode": GasFeeCustom, + "nonce": nonce, + "gasAmount": gasAmount, + "maxFeesPerGas": maxFeesPerGas, + "priorityFee": priorityFee, + } + + var pathTxIdentity = %* { + "routerInputParamsUuid": routerInputParamsUuid, + "pathName": pathName, + "chainID": chainId, + } + if isApprovalTx: + pathTxIdentity["isApprovalTx"] = %* true + if communityId != "": + pathTxIdentity["communityID"] = %* communityId + + let payload = %* [pathTxIdentity, pathTxCustomParams] + let rpcResponse = core.callPrivateRPC("wallet_setCustomTxDetails", payload) if isErrorResponse(rpcResponse): return rpcResponse.error.message \ No newline at end of file diff --git a/storybook/qmlTests/tests/tst_SendSignModal.qml b/storybook/qmlTests/tests/tst_SendSignModal.qml index d221c6f2f4..4feba13484 100644 --- a/storybook/qmlTests/tests/tst_SendSignModal.qml +++ b/storybook/qmlTests/tests/tst_SendSignModal.qml @@ -7,6 +7,7 @@ import Models 1.0 import StatusQ.Core.Utils 0.1 as SQUtils import StatusQ.Core.Theme 0.1 +import StatusQ.Controls 0.1 import AppLayouts.Wallet.popups.simpleSend 1.0 @@ -44,6 +45,36 @@ Item { networkIconPath: Theme.svg("network/Network=Ethereum") networkBlockExplorerUrl: "https://etherscan.io/" + currentBaseFee: "8.2" + currentSuggestedMinPriorityFee: "0.06" + currentSuggestedMaxPriorityFee: "5.1" + currentGasAmount: "31500" + currentNonce: 21 + + normalPrice: "1.45 EUR" + normalBaseFee: "10000" + normalPriorityFee: "1000" + normalTime: "~60s" + fastPrice: "1.65 EUR" + fastBaseFee: "100000" + fastPriorityFee: "10000" + fastTime: "~40s" + urgentPrice: "1.85 EUR" + urgentBaseFee: "1000000" + urgentPriorityFee: "100000" + urgentTime: "~15s" + + customBaseFee: "10000" + customPriorityFee: "1000" + customGasAmount: "35000" + customNonce: 22 + + selectedFeeMode: StatusFeeOption.Type.Normal + + fnGetPriceInCurrencyForFee: function(feeInWei) { + return "0.25 USD" + } + fiatFees: "1.54 EUR" cryptoFees: "0.001 ETH" estimatedTime: qsTr("> 5 minutes") diff --git a/ui/StatusQ/src/StatusQ/Popups/Dialog/StatusDialogHeader.qml b/ui/StatusQ/src/StatusQ/Popups/Dialog/StatusDialogHeader.qml index ce11c13ece..74f427644a 100644 --- a/ui/StatusQ/src/StatusQ/Popups/Dialog/StatusDialogHeader.qml +++ b/ui/StatusQ/src/StatusQ/Popups/Dialog/StatusDialogHeader.qml @@ -14,6 +14,13 @@ Rectangle { property alias leftComponent: leftComponentLoader.sourceComponent + property bool internalPopupActive + property color internalOverlayColor + property int popupFullHeight + property Component internalPopupComponent + + signal closeInternalPopup() + color: Theme.palette.statusModal.backgroundColor radius: 8 @@ -73,4 +80,29 @@ Rectangle { samples: 37 color: Theme.palette.dropShadow } + + Rectangle { + id: internalOverlay + anchors.fill: parent + anchors.bottomMargin: -1 * root.popupFullHeight + root.height + visible: root.internalPopupActive + radius: root.radius + color: root.internalOverlayColor + + MouseArea { + anchors.fill: parent + anchors.bottomMargin: popupLoader.height + onClicked: { + root.closeInternalPopup() + } + } + } + + Loader { + id: popupLoader + anchors.bottom: parent.bottom + anchors.bottomMargin: internalOverlay.anchors.bottomMargin + active: root.internalPopupActive + sourceComponent: root.internalPopupComponent + } } diff --git a/ui/StatusQ/src/assets.qrc b/ui/StatusQ/src/assets.qrc index a1f84f721e..1d38dfd798 100644 --- a/ui/StatusQ/src/assets.qrc +++ b/ui/StatusQ/src/assets.qrc @@ -281,6 +281,7 @@ assets/img/icons/security.svg assets/img/icons/seed-phrase.svg assets/img/icons/send.svg + assets/img/icons/settings-advance.svg assets/img/icons/settings-advanced.svg assets/img/icons/settings.svg assets/img/icons/share-android.svg diff --git a/ui/StatusQ/src/assets/img/icons/settings-advance.svg b/ui/StatusQ/src/assets/img/icons/settings-advance.svg new file mode 100644 index 0000000000..6aca648d3c --- /dev/null +++ b/ui/StatusQ/src/assets/img/icons/settings-advance.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/ui/app/AppLayouts/Wallet/popups/SignTransactionModalBase.qml b/ui/app/AppLayouts/Wallet/popups/SignTransactionModalBase.qml index a81e79e866..dc8fdd1ac2 100644 --- a/ui/app/AppLayouts/Wallet/popups/SignTransactionModalBase.qml +++ b/ui/app/AppLayouts/Wallet/popups/SignTransactionModalBase.qml @@ -100,6 +100,12 @@ StatusDialog { default property alias contents: contentsLayout.data + property bool internalPopupActive: false + property color internalOverlayColor: Theme.palette.backdropColor + property Component internalPopupComponent + + signal closeInternalPopup() + width: 480 padding: 0 @@ -117,6 +123,13 @@ StatusDialog { actions.closeButton.onClicked: root.close() leftComponent: root.headerIconComponent + + internalPopupActive: root.internalPopupActive + internalOverlayColor: root.internalOverlayColor + popupFullHeight: root.height + internalPopupComponent: root.internalPopupComponent + + onCloseInternalPopup: root.closeInternalPopup() } footer: StatusDialogFooter { diff --git a/ui/app/AppLayouts/Wallet/popups/simpleSend/SendSignModal.qml b/ui/app/AppLayouts/Wallet/popups/simpleSend/SendSignModal.qml index be9a8df2d7..552df3212b 100644 --- a/ui/app/AppLayouts/Wallet/popups/simpleSend/SendSignModal.qml +++ b/ui/app/AppLayouts/Wallet/popups/simpleSend/SendSignModal.qml @@ -9,6 +9,7 @@ import StatusQ.Controls 0.1 import StatusQ.Components 0.1 import AppLayouts.Wallet.panels 1.0 +import AppLayouts.Wallet.views 1.0 import AppLayouts.Wallet.popups 1.0 import utils 1.0 @@ -81,6 +82,52 @@ SignTransactionModalBase { /** Input property holding function openSea explorer url for the collectible **/ required property var fnGetOpenSeaExplorerUrl + /** Transaction settings related parameters **/ + required property int selectedFeeMode + + required property string currentBaseFee + required property string currentSuggestedMinPriorityFee + required property string currentSuggestedMaxPriorityFee + required property string currentGasAmount + required property int currentNonce + + required property string normalPrice + required property string normalBaseFee + required property string normalPriorityFee + required property string normalTime + + required property string fastPrice + required property string fastTime + required property string fastBaseFee + required property string fastPriorityFee + + required property string urgentPrice + required property string urgentTime + required property string urgentBaseFee + required property string urgentPriorityFee + + required property string customBaseFee + required property string customPriorityFee + required property string customGasAmount + required property int customNonce + + /** required function which receives fee in wei and recalculate it currency selected currency and format to locale string **/ + required property var fnGetPriceInCurrencyForFee + + /** Signal to updated tx settings **/ + signal updateTxSettings(int selectedFeeMode, string customNonce, string customGasAmount, string maxFeesPerGas, string priorityFee) + + /** Recalculates all values currently displayed in the transaction settings panel **/ + function refreshTxSettings() { + d.refreshTxSettings() + } + + QtObject { + id: d + + signal refreshTxSettings() + } + title: qsTr("Sign Send") //: e.g. (Send) 100 DAI to batista.eth subtitle: { @@ -173,9 +220,124 @@ SignTransactionModalBase { loading: root.feesLoading } } + StatusFlatButton { + tooltip.text: qsTr("Edit transaction settings") + icon.name: "settings-advance" + textColor: hovered? Theme.palette.directColor1 : Theme.palette.baseColor1 + onClicked: { + root.internalPopupActive = true + } + } } } + property Component internalPopup: TransactionSettings { + + fnGetPriceInCurrencyForFee: root.fnGetPriceInCurrencyForFee + + selectedFeeMode: root.selectedFeeMode + + currentBaseFee: root.currentBaseFee + currentSuggestedMinPriorityFee: root.currentSuggestedMinPriorityFee + currentSuggestedMaxPriorityFee: root.currentSuggestedMaxPriorityFee + currentGasAmount: root.currentGasAmount + currentNonce: root.currentNonce + + normalPrice: root.normalPrice + normalTime: root.normalTime + fastPrice: root.fastPrice + fastTime: root.fastTime + urgentPrice: root.urgentPrice + urgentTime: root.urgentTime + + function updateCustomFields() { + // by default custom follows normal fee option + if (selectedFeeMode !== StatusFeeOption.Type.Custom) { + customBaseFee = "" + customPriorityFee = "" + customGasAmount = "" + customNonce = "" + return + } + + if (!customBaseFeeDirty) { + if (selectedFeeMode === root.selectedFeeMode) { + customBaseFee = !!root.customBaseFee? Utils.weiToGWei(root.customBaseFee).toFixed() : "0" + } else { + customBaseFee = Utils.weiToGWei(root.normalBaseFee).toFixed() + } + customBaseFeeDirty = false + } + + if (!customPriorityFeeDirty) { + if (selectedFeeMode === root.selectedFeeMode) { + customPriorityFee = !!root.customPriorityFee? Utils.weiToGWei(root.customPriorityFee).toFixed() : "0" + } else { + customPriorityFee = Utils.weiToGWei(root.normalPriorityFee).toFixed() + } + customPriorityFeeDirty = false + } + + if (!customGasAmountDirty) { + if (selectedFeeMode === root.selectedFeeMode) { + customGasAmount = root.customGasAmount + } else { + customGasAmount = root.currentGasAmount + } + customGasAmountDirty = false + } + + if (!customNonceDirty) { + if (selectedFeeMode === root.selectedFeeMode) { + customNonce = root.customNonce + } else { + customNonce = root.currentNonce + } + customNonceDirty = false + } + } + + Component.onCompleted: { + d.refreshTxSettings.connect(updateCustomFields) + if (root.selectedFeeMode === StatusFeeOption.Type.Custom) { + updateCustomFields() + recalculateCustomPrice() + } + } + + onSelectedFeeModeChanged: { + updateCustomFields() + } + + onCancelClicked: { + root.internalPopupActive = false + } + + onConfirmClicked: { + let priorityFee = "" + let maxFeesPerGas = "" + if (selectedFeeMode === StatusFeeOption.Type.Custom) { + if (!!customPriorityFee && !!customBaseFee) { + const baseFeeWei = Utils.gweiToWei(customBaseFee) + const priorityFeeWei = Utils.gweiToWei(customPriorityFee) + + priorityFee = priorityFeeWei.toFixed() + maxFeesPerGas = SQUtils.AmountsArithmetic.sum(baseFeeWei, priorityFeeWei).toFixed() + } + } + + root.updateTxSettings(selectedFeeMode, customNonce, customGasAmount, maxFeesPerGas, priorityFee) + + root.internalPopupActive = false + } + } + + internalPopupComponent: internalPopup + + onCloseInternalPopup: { + root.internalPopupActive = false + } + // Send Asset SignInfoBox { objectName: "sendAssetBox" @@ -203,6 +365,7 @@ SignTransactionModalBase { } ] visible: !root.isCollectible + enabled: !root.internalPopupActive } // Send Collectible @@ -229,6 +392,7 @@ SignTransactionModalBase { onOpenLink: (link) => root.openLinkWithConfirmation(link) } visible: root.isCollectible + enabled: !root.internalPopupActive } // From @@ -268,6 +432,7 @@ SignTransactionModalBase { onOpenLink: (link) => root.openLinkWithConfirmation(link) } ] + enabled: !root.internalPopupActive } // Network diff --git a/ui/app/AppLayouts/Wallet/stores/TransactionStoreNew.qml b/ui/app/AppLayouts/Wallet/stores/TransactionStoreNew.qml index 0646878952..2b87199ef8 100644 --- a/ui/app/AppLayouts/Wallet/stores/TransactionStoreNew.qml +++ b/ui/app/AppLayouts/Wallet/stores/TransactionStoreNew.qml @@ -25,6 +25,14 @@ QtObject { _walletSectionSendInst.stopUpdatesForSuggestedRoute() } + function setFeeMode(feeMode, routerInputParamsUuid, pathName, chainId, isApprovalTx, communityId) { + _walletSectionSendInst.setFeeMode(feeMode, routerInputParamsUuid, pathName, chainId, isApprovalTx, communityId) + } + + function setCustomTxDetails(nonce, gasAmount, maxFeesPerGas, priorityFee, routerInputParamsUuid, pathName, chainId, isApprovalTx, communityId) { + _walletSectionSendInst.setCustomTxDetails(nonce, gasAmount, maxFeesPerGas, priorityFee, routerInputParamsUuid, pathName, chainId, isApprovalTx, communityId) + } + Component.onCompleted: { _walletSectionSendInst.suggestedRoutesReady.connect(suggestedRoutesReady) _walletSectionSendInst.transactionSent.connect(transactionSent) diff --git a/ui/app/mainui/SendModalHandler.qml b/ui/app/mainui/SendModalHandler.qml index 7b192270bc..271171e0e1 100644 --- a/ui/app/mainui/SendModalHandler.qml +++ b/ui/app/mainui/SendModalHandler.qml @@ -7,6 +7,7 @@ import StatusQ 0.1 import StatusQ.Core 0.1 import StatusQ.Core.Theme 0.1 import StatusQ.Core.Utils 0.1 as SQUtils +import StatusQ.Controls 0.1 import AppLayouts.Wallet.stores 1.0 as WalletStores import AppLayouts.Wallet.popups.simpleSend 1.0 @@ -470,7 +471,7 @@ QtObject { } if(isValidParameter(root.simpleSendParams.stickersPackId)) { stickersPackId = root.simpleSendParams.stickersPackId - } + } if(isValidParameter(root.simpleSendParams.transferOwnership)) { transferOwnership = root.simpleSendParams.transferOwnership } @@ -564,6 +565,8 @@ QtObject { property string uuid property var fetchedPathModel + signal refreshTxSettings() + readonly property string extraParamsJson: { if (!!simpleSendModal.stickersPackId) { return JSON.stringify({[Constants.suggestedRoutesExtraParamsProperties.packId]: simpleSendModal.stickersPackId}) @@ -611,6 +614,7 @@ QtObject { simpleSendModal.routerErrorDetails = "%1 - %2".arg(errCode).arg( WalletUtils.getRouterErrorDetailsOnCode(errCode, errDescription)) fetchedPathModel = pathModel + handler.refreshTxSettings() } function transactionSent(returnedUuid, chainId, approvalTx, txHash, error) { @@ -817,18 +821,163 @@ QtObject { networkIconPath: Theme.svg(signSendAdaptor.selectedNetwork.iconUrl) networkBlockExplorerUrl: signSendAdaptor.selectedNetwork.blockExplorerURL - readonly property var totalFeesEth: { - let tatlFeeInWei = "0" + selectedFeeMode: { if (!!txPathUnderReviewEntry.item) { if (handler.reviewApprovalForTxPathUnderReview) { - tatlFeeInWei = SQUtils.AmountsArithmetic.sum(SQUtils.AmountsArithmetic.fromString(txPathUnderReviewEntry.item.approvalFee), - SQUtils.AmountsArithmetic.fromString(txPathUnderReviewEntry.item.approvalL1Fee)) + return txPathUnderReviewEntry.item.approvalGasFeeMode + } + return txPathUnderReviewEntry.item.txGasFeeMode + } + return StatusFeeOption.Type.Normal + } + + currentBaseFee: !!txPathUnderReviewEntry.item? txPathUnderReviewEntry.item.currentBaseFee : "" + currentSuggestedMinPriorityFee: !!txPathUnderReviewEntry.item? txPathUnderReviewEntry.item.suggestedMinPriorityFee : "" + currentSuggestedMaxPriorityFee: !!txPathUnderReviewEntry.item? txPathUnderReviewEntry.item.suggestedMaxPriorityFee : "" + currentGasAmount: { + if (!!txPathUnderReviewEntry.item) { + if (handler.reviewApprovalForTxPathUnderReview) { + return txPathUnderReviewEntry.item.suggestedApprovalGasAmount + } + return txPathUnderReviewEntry.item.suggestedTxGasAmount + } + return "" + } + currentNonce: { + if (!!txPathUnderReviewEntry.item) { + if (handler.reviewApprovalForTxPathUnderReview) { + return txPathUnderReviewEntry.item.suggestedApprovalTxNonce + } + return txPathUnderReviewEntry.item.suggestedTxNonce + } + return 0 + } + + fnGetPriceInCurrencyForFee: function(feeInWei) { + if (!feeInWei) { + return "" + } + const feesEth = Utils.weiToEth(feeInWei) + const ethToken = SQUtils.ModelUtils.getByKey(root.plainTokensBySymbolModel, "key", Constants.ethToken) + const ethFiatValue = !!ethToken ? ethToken.marketDetails.currencyPrice.amount: 1 + return root.fnFormatCurrencyAmount(ethFiatValue*feesEth, root.currentCurrency).toString() + } + + normalPrice: { + if (!!txPathUnderReviewEntry.item) { + if (handler.reviewApprovalForTxPathUnderReview) { + const feeInWei = SQUtils.AmountsArithmetic.times( + SQUtils.AmountsArithmetic.fromString(txPathUnderReviewEntry.item.suggestedMaxFeesPerGasLowLevel), + SQUtils.AmountsArithmetic.fromString(txPathUnderReviewEntry.item.approvalGasAmount)).toFixed() + return fnGetPriceInCurrencyForFee(feeInWei) + } + const feeInWei = SQUtils.AmountsArithmetic.times( + SQUtils.AmountsArithmetic.fromString(txPathUnderReviewEntry.item.suggestedMaxFeesPerGasLowLevel), + SQUtils.AmountsArithmetic.fromString(txPathUnderReviewEntry.item.txGasAmount)).toFixed() + return fnGetPriceInCurrencyForFee(feeInWei) + } + return "" + } + normalBaseFee: !!txPathUnderReviewEntry.item? + SQUtils.AmountsArithmetic.sub(SQUtils.AmountsArithmetic.fromString(txPathUnderReviewEntry.item.suggestedMaxFeesPerGasLowLevel), + SQUtils.AmountsArithmetic.fromString(txPathUnderReviewEntry.item.suggestedPriorityFeePerGasLowLevel)).toFixed() + : "" + normalPriorityFee: !!txPathUnderReviewEntry.item? txPathUnderReviewEntry.item.suggestedPriorityFeePerGasLowLevel : "" + normalTime: "~60s" // TODO: update this when we figure out how to estimate time more granularly + + fastPrice: { + if (!!txPathUnderReviewEntry.item) { + if (handler.reviewApprovalForTxPathUnderReview) { + const feeInWei = SQUtils.AmountsArithmetic.times( + SQUtils.AmountsArithmetic.fromString(txPathUnderReviewEntry.item.suggestedMaxFeesPerGasMediumLevel), + SQUtils.AmountsArithmetic.fromString(txPathUnderReviewEntry.item.approvalGasAmount)).toFixed() + return fnGetPriceInCurrencyForFee(feeInWei) + } + const feeInWei = SQUtils.AmountsArithmetic.times( + SQUtils.AmountsArithmetic.fromString(txPathUnderReviewEntry.item.suggestedMaxFeesPerGasMediumLevel), + SQUtils.AmountsArithmetic.fromString(txPathUnderReviewEntry.item.txGasAmount)).toFixed() + return fnGetPriceInCurrencyForFee(feeInWei) + } + return "" + } + fastBaseFee: !!txPathUnderReviewEntry.item? + SQUtils.AmountsArithmetic.sub(SQUtils.AmountsArithmetic.fromString(txPathUnderReviewEntry.item.suggestedMaxFeesPerGasMediumLevel), + SQUtils.AmountsArithmetic.fromString(txPathUnderReviewEntry.item.suggestedPriorityFeePerGasMediumLevel)).toFixed() + : "" + fastPriorityFee: !!txPathUnderReviewEntry.item? txPathUnderReviewEntry.item.suggestedPriorityFeePerGasMediumLevel : "" + fastTime: "~40s" // TODO: update this when we figure out how to estimate time more granularly + + urgentPrice: { + if (!!txPathUnderReviewEntry.item) { + if (handler.reviewApprovalForTxPathUnderReview) { + const feeInWei = SQUtils.AmountsArithmetic.times( + SQUtils.AmountsArithmetic.fromString(txPathUnderReviewEntry.item.suggestedMaxFeesPerGasHighLevel), + SQUtils.AmountsArithmetic.fromString(txPathUnderReviewEntry.item.approvalGasAmount)).toFixed() + return fnGetPriceInCurrencyForFee(feeInWei) + } + const feeInWei = SQUtils.AmountsArithmetic.times( + SQUtils.AmountsArithmetic.fromString(txPathUnderReviewEntry.item.suggestedMaxFeesPerGasHighLevel), + SQUtils.AmountsArithmetic.fromString(txPathUnderReviewEntry.item.txGasAmount)).toFixed() + return fnGetPriceInCurrencyForFee(feeInWei) + } + return "" + } + urgentBaseFee: !!txPathUnderReviewEntry.item? + SQUtils.AmountsArithmetic.sub(SQUtils.AmountsArithmetic.fromString(txPathUnderReviewEntry.item.suggestedMaxFeesPerGasHighLevel), + SQUtils.AmountsArithmetic.fromString(txPathUnderReviewEntry.item.suggestedPriorityFeePerGasHighLevel)).toFixed() + : "" + urgentPriorityFee: !!txPathUnderReviewEntry.item? txPathUnderReviewEntry.item.suggestedPriorityFeePerGasHighLevel : "" + urgentTime: "~15s" // TODO: update this when we figure out how to estimate time more granularly + + customBaseFee: { + if (!!txPathUnderReviewEntry.item) { + if (handler.reviewApprovalForTxPathUnderReview) { + return txPathUnderReviewEntry.item.approvalBaseFee + } + return txPathUnderReviewEntry.item.txBaseFee + } + return "" + } + customPriorityFee: { + if (!!txPathUnderReviewEntry.item) { + if (handler.reviewApprovalForTxPathUnderReview) { + return txPathUnderReviewEntry.item.approvalPriorityFee + } + return txPathUnderReviewEntry.item.txPriorityFee + } + return "" + } + customGasAmount: { + if (!!txPathUnderReviewEntry.item) { + if (handler.reviewApprovalForTxPathUnderReview) { + return txPathUnderReviewEntry.item.approvalGasAmount + } + return txPathUnderReviewEntry.item.txGasAmount + } + return "" + } + customNonce: { + if (!!txPathUnderReviewEntry.item) { + if (handler.reviewApprovalForTxPathUnderReview) { + return txPathUnderReviewEntry.item.approvalTxNonce + } + return txPathUnderReviewEntry.item.txNonce + } + return "" + } + + readonly property var totalFeesEth: { + let totalFeeInWei = "0" + if (!!txPathUnderReviewEntry.item) { + if (handler.reviewApprovalForTxPathUnderReview) { + totalFeeInWei = SQUtils.AmountsArithmetic.sum(SQUtils.AmountsArithmetic.fromString(txPathUnderReviewEntry.item.approvalFee), + SQUtils.AmountsArithmetic.fromString(txPathUnderReviewEntry.item.approvalL1Fee)).toFixed() } else { - tatlFeeInWei = SQUtils.AmountsArithmetic.sum(SQUtils.AmountsArithmetic.fromString(txPathUnderReviewEntry.item.txFee), - SQUtils.AmountsArithmetic.fromString(txPathUnderReviewEntry.item.txL1Fee)) + totalFeeInWei = SQUtils.AmountsArithmetic.sum(SQUtils.AmountsArithmetic.fromString(txPathUnderReviewEntry.item.txFee), + SQUtils.AmountsArithmetic.fromString(txPathUnderReviewEntry.item.txL1Fee)).toFixed() } } - return SQUtils.AmountsArithmetic.div(SQUtils.AmountsArithmetic.fromString(tatlFeeInWei), SQUtils.AmountsArithmetic.fromNumber(1, Constants.ethTokenDecimals)) + return Utils.weiToEth(totalFeeInWei) } fiatFees: { @@ -865,11 +1014,46 @@ QtObject { fnGetOpenSeaExplorerUrl: root.fnGetOpenSeaUrl + Component.onCompleted: { + handler.refreshTxSettings.connect(refreshTxSettings) + } + onOpened: handler.sendMetricsEvent("sign modal opened") + onRejected:{ handler.sendMetricsEvent("sign modal rejected") handler.handleReject() } + + onUpdateTxSettings: { + let pathName = "" + let chainId = 0 + if (!!txPathUnderReviewEntry.item) { + pathName = txPathUnderReviewEntry.item.processorName + chainId = txPathUnderReviewEntry.item.fromChain + } + + if (selectedFeeMode === StatusFeeOption.Type.Custom) { + root.transactionStoreNew.setCustomTxDetails(customNonce, + customGasAmount, + maxFeesPerGas, + priorityFee, + handler.uuid, + pathName, + chainId, + handler.reviewApprovalForTxPathUnderReview, + "") + return + } + + root.transactionStoreNew.setFeeMode(selectedFeeMode, + handler.uuid, + pathName, + chainId, + handler.reviewApprovalForTxPathUnderReview, + "") + } + onAccepted: { handler.sendMetricsEvent("sign modal accepted") if (handler.reviewingLastTxPath) {