diff --git a/src/app/profile/views/ens_manager.nim b/src/app/profile/views/ens_manager.nim index 6cf6f66061..e146b2e240 100644 --- a/src/app/profile/views/ens_manager.nim +++ b/src/app/profile/views/ens_manager.nim @@ -11,7 +11,7 @@ import ../../../status/wallet import sets import web3/ethtypes import ../../../status/tasks/[qt, task_runner_impl] - +import chronicles type EnsRoles {.pure.} = enum UserName = UserRole + 1 @@ -45,11 +45,21 @@ const detailsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} = arg = decode[DetailsTaskArg](argEncoded) address = status_ens.address(arg.username) pubkey = status_ens.pubkey(arg.username) - json = %* { - "ensName": arg.username, - "address": address, - "pubkey": pubkey - } + + var isStatus:bool = false + var expirationTime:int = 0 + if arg.username.endsWith(domain): + isStatus = true + var success = false + expirationTime = status_ens.getExpirationTime(arg.username.replace(domain, ""), success) + + let json = %* { + "ensName": arg.username, + "address": address, + "pubkey": pubkey, + "isStatus": isStatus, + "expirationTime": expirationTime + } arg.finish(json) proc details[T](self: T, slot: string, username: string) = @@ -87,8 +97,9 @@ QtObject: let pendingTransactions = self.status.wallet.getPendingTransactions() if (pendingTransactions == ""): return + for trx in pendingTransactions.parseJson{"result"}.getElems(): - if trx["type"].getStr == $PendingTransactionType.RegisterENS: + if trx["type"].getStr == $PendingTransactionType.RegisterENS or trx["type"].getStr == $PendingTransactionType.SetPubKey: self.usernames.add trx["additionalData"].getStr self.pendingUsernames.incl trx["additionalData"].getStr @@ -106,6 +117,19 @@ QtObject: self.usernames.add(username) self.endInsertRows() + proc remove*(self: EnsManager, username: string) = + var idx = -1 + var i = 0 + for u in self.usernames: + if u == username: + idx = i + break + i = i + 1 + if idx == -1: return + self.beginRemoveRows(newQModelIndex(), idx, idx) + self.usernames.delete(idx) + self.endRemoveRows() + proc getPreferredUsername(self: EnsManager): string {.slot.} = result = self.status.settings.getSetting[:string](Setting.PreferredUsername, "") @@ -138,12 +162,12 @@ QtObject: self.loading(true) self.details("setDetails", username) - proc detailsObtained(self: EnsManager, ensName: string, address: string, pubkey: string) {.signal.} + proc detailsObtained(self: EnsManager, ensName: string, address: string, pubkey: string, isStatus: bool, expirationTime: int) {.signal.} proc setDetails(self: EnsManager, details: string): string {.slot.} = self.loading(false) let detailsJson = details.parseJson - self.detailsObtained(detailsJson["ensName"].getStr, detailsJson["address"].getStr, detailsJson["pubkey"].getStr) + self.detailsObtained(detailsJson["ensName"].getStr, detailsJson["address"].getStr, detailsJson["pubkey"].getStr, detailsJson["isStatus"].getBool, detailsJson["expirationTime"].getInt) method rowCount(self: EnsManager, index: QModelIndex = nil): int = return self.usernames.len @@ -228,6 +252,22 @@ QtObject: self.pendingUsernames.incl(ensUsername) self.add ensUsername + proc releaseEstimate(self: EnsManager, ensUsername: string, address: string): int {.slot.} = + var success: bool + result = releaseEstimateGas(ensUsername, address, success) + if not success: + result = 100000 + + proc release*(self: EnsManager, username: string, address: string, gas: string, gasPrice: string, password: string): string {.slot.} = + var success: bool + let response = release(username, address, gas, gasPrice, password, success) + result = $(%* { "result": %response, "success": %success }) + + if success: + self.transactionWasSent(response) + self.pendingUsernames.excl(username) + self.remove(username) + proc setPubKeyGasEstimate(self: EnsManager, ensUsername: string, address: string): int {.slot.} = var success: bool let pubKey = self.status.settings.getSetting[:string](Setting.PublicKey, "0x0") diff --git a/src/status/ens.nim b/src/status/ens.nim index acee8bc84f..13d271b0d2 100644 --- a/src/status/ens.nim +++ b/src/status/ens.nim @@ -16,6 +16,7 @@ import transactions import algorithm import web3/[ethtypes, conversions], stew/byteutils, stint import libstatus/eth/contracts +import libstatus/eth/transactions as eth_transactions import chronicles, libp2p/[multihash, multicodec, cid] import ./settings as status_settings @@ -172,6 +173,54 @@ proc getPrice*(): Stuint[256] = raise newException(RpcException, "Error getting ens username price: 0x") result = fromHex(Stuint[256], response.result) +proc releaseEstimateGas*(username: string, address: string, success: var bool): int = + let + label = fromHex(FixedBytes[32], label(username)) + ensUsernamesContract = contracts.getContract("ens-usernames") + release = Release(label: label) + + var tx = transactions.buildTokenTransaction(parseAddress(address), ensUsernamesContract.address, "", "") + try: + let response = ensUsernamesContract.methods["release"].estimateGas(tx, release, success) + if success: + result = fromHex[int](response) + except RpcException as e: + raise + +proc release*(username: string, address: string, gas, gasPrice, password: string, success: var bool): string = + let + label = fromHex(FixedBytes[32], label(username)) + ensUsernamesContract = contracts.getContract("ens-usernames") + release = Release(label: label) + + var tx = transactions.buildTokenTransaction(parseAddress(address), ensUsernamesContract.address, "", "") + try: + result = ensUsernamesContract.methods["release"].send(tx, release, password, success) + if success: + trackPendingTransaction(result, address, $ensUsernamesContract.address, PendingTransactionType.ReleaseENS, username) + except RpcException as e: + raise + +proc getExpirationTime*(username: string, success: var bool): int = + let + label = fromHex(FixedBytes[32], label(username)) + expTime = ExpirationTime(label: label) + ensUsernamesContract = contracts.getContract("ens-usernames") + + var tx = transactions.buildTransaction(parseAddress("0x0000000000000000000000000000000000000000"), 0.u256) + tx.to = ensUsernamesContract.address.some + tx.data = ensUsernamesContract.methods["getExpirationTime"].encodeAbi(expTime) + var response = "" + try: + response = eth_transactions.call(tx).result + success = true + except RpcException as e: + success = false + error "Error obtaining expiration time", err=e.msg + + if success: + result = fromHex[int](response) + proc extractCoordinates*(pubkey: string):tuple[x: string, y:string] = result = ("0x" & pubkey[4..67], "0x" & pubkey[68..131]) diff --git a/src/status/libstatus/coder.nim b/src/status/libstatus/coder.nim index f795ac6866..c058ee0f7b 100644 --- a/src/status/libstatus/coder.nim +++ b/src/status/libstatus/coder.nim @@ -29,6 +29,12 @@ type x*: FixedBytes[32] y*: FixedBytes[32] + ExpirationTime* = object + label*: FixedBytes[32] + + Release* = object + label*: FixedBytes[32] + ApproveAndCall*[N: static[int]] = object to*: Address value*: Stuint[256] diff --git a/src/status/libstatus/eth/contracts.nim b/src/status/libstatus/eth/contracts.nim index 5a8b7f140a..ef1245719c 100644 --- a/src/status/libstatus/eth/contracts.nim +++ b/src/status/libstatus/eth/contracts.nim @@ -10,7 +10,7 @@ import export GetPackData, PackData, BuyToken, ApproveAndCall, Transfer, BalanceOf, Register, SetPubkey, TokenOfOwnerByIndex, TokenPackId, TokenUri, FixedBytes, DynamicBytes, toHex, fromHex, - decodeContractResponse, encodeAbi, estimateGas, send, call + decodeContractResponse, encodeAbi, estimateGas, send, call, ExpirationTime, Release logScope: @@ -204,7 +204,9 @@ proc allContracts(): seq[Contract] = Contract(name: "ens-usernames", network: Network.Mainnet, address: parseAddress("0xDB5ac1a559b02E12F29fC0eC0e37Be8E046DEF49"), methods: [ ("register", Method(signature: "register(bytes32,address,bytes32,bytes32)")), - ("getPrice", Method(signature: "getPrice()")) + ("getPrice", Method(signature: "getPrice()")), + ("getExpirationTime", Method(signature: "getExpirationTime(bytes32)")), + ("release", Method(signature: "release(bytes32)")) ].toTable ), Contract(name: "ens-resolver", network: Network.Mainnet, address: parseAddress("0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41"), @@ -234,10 +236,12 @@ proc allContracts(): seq[Contract] = ), newErc721Contract("sticker-pack", Network.Testnet, parseAddress("0xf852198d0385c4b871e0b91804ecd47c6ba97351"), "PACK", false, @[("tokenPackId", Method(signature: "tokenPackId(uint256)"))]), newErc721Contract("kudos", Network.Testnet, parseAddress("0xcd520707fc68d153283d518b29ada466f9091ea8"), "KDO", true), - Contract(name: "ens-usernames", network: Network.Testnet, address: parseAddress("0x11d9F481effd20D76cEE832559bd9Aca25405841"), + Contract(name: "ens-usernames", network: Network.Testnet, address: parseAddress("0xdaae165beb8c06e0b7613168138ebba774aff071"), methods: [ ("register", Method(signature: "register(bytes32,address,bytes32,bytes32)")), - ("getPrice", Method(signature: "getPrice()")) + ("getPrice", Method(signature: "getPrice()")), + ("getExpirationTime", Method(signature: "getExpirationTime(bytes32)")), + ("release", Method(signature: "release(bytes32)")) ].toTable ), Contract(name: "ens-resolver", network: Network.Testnet, address: parseAddress("0x42D63ae25990889E35F215bC95884039Ba354115"), diff --git a/src/status/libstatus/eth/transactions.nim b/src/status/libstatus/eth/transactions.nim index 4fe7092581..ff61c513eb 100644 --- a/src/status/libstatus/eth/transactions.nim +++ b/src/status/libstatus/eth/transactions.nim @@ -24,7 +24,7 @@ proc sendTransaction*(tx: EthSend, password: string): RpcResponse = trace "Transaction sent succesfully", hash=result.result proc call*(tx: EthSend): RpcResponse = - let responseStr = core.callPrivateRPC("eth_call", %*[%tx]) + let responseStr = core.callPrivateRPC("eth_call", %*[%tx, "latest"]) result = Json.decode(responseStr, RpcResponse) if not result.error.isNil: raise newException(RpcException, "Error calling method: " & result.error.message) \ No newline at end of file diff --git a/ui/app/AppLayouts/Profile/Sections/Ens/ENSDetails.qml b/ui/app/AppLayouts/Profile/Sections/Ens/ENSDetails.qml index 691a46d3b9..7887b12502 100644 --- a/ui/app/AppLayouts/Profile/Sections/Ens/ENSDetails.qml +++ b/ui/app/AppLayouts/Profile/Sections/Ens/ENSDetails.qml @@ -10,8 +10,10 @@ Item { property string username: "" property string walletAddress: "-" property string key: "-" + property var expiration: 0 signal backBtnClicked(); + signal usernameReleased(username: string); StyledText { id: sectionTitle @@ -49,12 +51,17 @@ Item { keyLbl.textToCopy = pubkey; walletAddressLbl.visible = true; keyLbl.visible = true; + releaseBtn.visible = isStatus + releaseBtn.enabled = (Date.now() / 1000) > expirationTime && expirationTime > 0 && profileModel.ens.preferredUsername != username + expiration = new Date(expirationTime * 1000).getTime() } onLoading: { loadingImg.active = isLoading if(!isLoading) return; walletAddressLbl.visible = false; keyLbl.visible = false; + releaseBtn.visible = false; + expiration = 0; } } @@ -84,6 +91,63 @@ Item { anchors.topMargin: 24 } + Component { + id: transactionDialogComponent + StatusETHTransactionModal { + onOpened: { + walletModel.gasView.getGasPricePredictions() + } + title: qsTr("Connect username with your pubkey") + onClosed: { + destroy() + } + estimateGasFunction: function(selectedAccount) { + if (username === "" || !selectedAccount) return 100000; + return profileModel.ens.releaseEstimate(Utils.removeStatusEns(username), selectedAccount.address) + } + onSendTransaction: function(selectedAddress, gasLimit, gasPrice, password) { + return profileModel.ens.release(username, + selectedAddress, + gasLimit, + gasPrice, + password) + } + onSuccess: function(){ + usernameReleased(username); + } + + width: 475 + height: 500 + } + } + + StatusButton { + id: releaseBtn + visible: false + enabled: false + anchors.top: keyLbl.bottom + anchors.topMargin: 24 + anchors.left: parent.left + anchors.leftMargin: 24 + text: qsTrId("Release username") + onClicked: { + openPopup(transactionDialogComponent) + } + } + + Text { + visible: releaseBtn.visible && !releaseBtn.enabled + anchors.top: releaseBtn.bottom + anchors.topMargin: 2 + anchors.left: parent.left + anchors.leftMargin: 24 + text: profileModel.ens.preferredUsername != username ? + qsTr("Username locked. You won’t be able to release it until %1").arg(Utils.formatShortDateStr(new Date(expiration).toDateString())): + qsTr("This is current preferred username. It can't be released") + color: Style.current.darkGrey + } + + StatusButton { anchors.bottom: parent.bottom anchors.bottomMargin: Style.current.padding diff --git a/ui/app/AppLayouts/Profile/Sections/Ens/ENSReleased.qml b/ui/app/AppLayouts/Profile/Sections/Ens/ENSReleased.qml new file mode 100644 index 0000000000..f328bdffa1 --- /dev/null +++ b/ui/app/AppLayouts/Profile/Sections/Ens/ENSReleased.qml @@ -0,0 +1,95 @@ +import QtQuick 2.14 +import QtQuick.Layouts 1.3 +import QtQuick.Controls 2.14 +import "../../../../../imports" +import "../../../../../shared" +import "../../../../../shared/status" + +Item { + property string ensUsername: "" + signal okBtnClicked() + + StyledText { + id: sectionTitle + //% "ENS usernames" + text: qsTrId("ens-usernames") + anchors.left: parent.left + anchors.leftMargin: Style.current.bigPadding + anchors.top: parent.top + anchors.topMargin: Style.current.bigPadding + font.weight: Font.Bold + font.pixelSize: 20 + } + + + Rectangle { + id: circle + anchors.top: sectionTitle.bottom + anchors.topMargin: Style.current.bigPadding + anchors.horizontalCenter: parent.horizontalCenter + width: 60 + height: 60 + radius: 120 + color: Style.current.blue + + StyledText { + text: "✓" + opacity: 0.7 + font.weight: Font.Bold + font.pixelSize: 18 + color: Style.current.white + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + } + } + + StyledText { + id: title + text: qsTr("Username removed") + anchors.top: circle.bottom + anchors.topMargin: Style.current.bigPadding + font.weight: Font.Bold + font.pixelSize: 24 + anchors.left: parent.left + anchors.right: parent.right + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + StyledText { + id: subtitle + text: qsTr("The username %1 will be removed and your deposit will be returned once the transaction is mined").arg(ensUsername) + anchors.top: title.bottom + anchors.topMargin: 24 + font.pixelSize: 14 + anchors.left: parent.left + anchors.right: parent.right + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + + StyledText { + id: progress + //% "You can follow the progress in the Transaction History section of your wallet." + text: qsTrId("ens-username-you-can-follow-progress") + anchors.top: subtitle.bottom + anchors.topMargin: 24 + font.pixelSize: 12 + anchors.left: parent.left + anchors.right: parent.right + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + color: Style.current.secondaryText + + } + + StatusButton { + id: startBtn + anchors.top: progress.bottom + anchors.topMargin: Style.current.padding + anchors.horizontalCenter: parent.horizontalCenter + //% "Ok, got it" + text: qsTrId("ens-got-it") + onClicked: okBtnClicked() + } +} diff --git a/ui/app/AppLayouts/Profile/Sections/Ens/Search.qml b/ui/app/AppLayouts/Profile/Sections/Ens/Search.qml index 5599193179..8a3b8b0aee 100644 --- a/ui/app/AppLayouts/Profile/Sections/Ens/Search.qml +++ b/ui/app/AppLayouts/Profile/Sections/Ens/Search.qml @@ -47,25 +47,33 @@ Item { Qt.callLater(validateENS, ensUsername, isStatus) } - Loader { - id: transactionDialog - function open() { - this.active = true - this.item.open() - } - function closed() { - this.active = false // kill an opened instance - } - sourceComponent: SetPubKeyModal { + Component { + id: transactionDialogComponent + StatusETHTransactionModal { onOpened: { walletModel.gasView.getGasPricePredictions() } + title: qsTr("Connect username with your pubkey") onClosed: { - transactionDialog.closed() + destroy() } - ensUsername: ensUsername.text || "" - width: 400 - height: 400 + estimateGasFunction: function(selectedAccount) { + if (ensUsername.text === "" || !selectedAccount) return 80000; + return profileModel.ens.setPubKeyGasEstimate(ensUsername.text + (isStatus ? ".stateofus.eth" : "" ), selectedAccount.address) + } + onSendTransaction: function(selectedAddress, gasLimit, gasPrice, password) { + return profileModel.ens.setPubKey(ensUsername.text + (isStatus ? ".stateofus.eth" : "" ), + selectedAddress, + gasLimit, + gasPrice, + password) + } + onSuccess: function(){ + usernameUpdated(ensUsername.text); + } + + width: 475 + height: 500 } } @@ -181,7 +189,7 @@ Item { } if(ensStatus === Constants.ens_connected_dkey || ensStatus === Constants.ens_owned){ - transactionDialog.open(); + openPopup(transactionDialogComponent) return; } } diff --git a/ui/app/AppLayouts/Profile/Sections/EnsContainer.qml b/ui/app/AppLayouts/Profile/Sections/EnsContainer.qml index f4ebbe8ed7..f04bc010a8 100644 --- a/ui/app/AppLayouts/Profile/Sections/EnsContainer.qml +++ b/ui/app/AppLayouts/Profile/Sections/EnsContainer.qml @@ -140,6 +140,10 @@ Item { targetState: welcomeState signal: goToWelcome } + DSM.SignalTransition { + targetState: ensReleasedState + signal: done + } } DSM.State { @@ -176,6 +180,15 @@ Item { } } + DSM.State { + id: ensReleasedState + onEntered:loader.sourceComponent = ensReleased + DSM.SignalTransition { + targetState: listState + signal: next + } + } + DSM.State { id: ensConnectedState onEntered:loader.sourceComponent = ensConnected @@ -240,6 +253,14 @@ Item { } } + Component { + id: ensReleased + ENSReleased { + ensUsername: selectedUsername + onOkBtnClicked: next(null) + } + } + Component { id: ensConnected ENSConnected { @@ -265,6 +286,10 @@ Item { ENSDetails { username: selectedUsername onBackBtnClicked: back(); + onUsernameReleased: { + selectedUsername = username; + done(username); + } } } diff --git a/ui/app/AppLayouts/Profile/Sections/Ens/SetPubKeyModal.qml b/ui/shared/status/StatusETHTransactionModal.qml similarity index 76% rename from ui/app/AppLayouts/Profile/Sections/Ens/SetPubKeyModal.qml rename to ui/shared/status/StatusETHTransactionModal.qml index 8fb78e69fe..45b17b5b31 100644 --- a/ui/app/AppLayouts/Profile/Sections/Ens/SetPubKeyModal.qml +++ b/ui/shared/status/StatusETHTransactionModal.qml @@ -2,17 +2,52 @@ import QtQuick 2.13 import QtQuick.Controls 2.13 import QtQuick.Layouts 1.13 import QtQuick.Dialogs 1.3 -import "../../../../../imports" -import "../../../../../shared" -import "../../../../../shared/status" +import "../../imports" +import "../../shared" +import "../../shared/status" ModalPopup { id: root readonly property var asset: {"name": "Ethereum", "symbol": "ETH"} - property string ensUsername: "" - //% "Connect username with your pubkey" - title: qsTrId("connect-username-with-your-pubkey") + title: qsTr("Contract interaction") + + property var estimateGasFunction: (function(userAddress) { return 0; }) + property var onSendTransaction: (function(userAddress, gasLimit, gasPrice, password){ return ""; }) + property var onSuccess: (function(){}) + + Component.onCompleted: { + walletModel.gasView.getGasPricePredictions() + } + + + function sendTransaction() { + try { + let responseStr = onSendTransaction(selectFromAccount.selectedAccount.address, + gasSelector.selectedGasLimit, + gasSelector.selectedGasPrice, + transactionSigner.enteredPassword); + + let response = JSON.parse(responseStr) + + if (!response.success) { + if (Utils.isInvalidPasswordMessage(response.result)){ + //% "Wrong password" + transactionSigner.validationError = qsTrId("wrong-password") + return + } + sendingError.text = response.result + return sendingError.open() + } + + onSuccess(); + root.close(); + } catch (e) { + console.error('Error sending the transaction', e) + sendingError.text = "Error sending the transaction: " + e.message; + return sendingError.open() + } + } property MessageDialog sendingError: MessageDialog { id: sendingError @@ -22,33 +57,6 @@ ModalPopup { standardButtons: StandardButton.Ok } - function sendTransaction() { - try { - let responseStr = profileModel.ens.setPubKey(root.ensUsername, - selectFromAccount.selectedAccount.address, - gasSelector.selectedGasLimit, - gasSelector.selectedGasPrice, - transactionSigner.enteredPassword) - let response = JSON.parse(responseStr) - - if (!response.success) { - if (Utils.isInvalidPasswordMessage(response.error.message)){ - //% "Wrong password" - transactionSigner.validationError = qsTrId("wrong-password") - return - } - sendingError.text = response.error.message - return sendingError.open() - } - - usernameUpdated(root.ensUsername); - } catch (e) { - console.error('Error sending the transaction', e) - sendingError.text = "Error sending the transaction: " + e.message; - return sendingError.open() - } - } - TransactionStackView { id: stack height: parent.height @@ -61,8 +69,7 @@ ModalPopup { } TransactionFormGroup { id: group1 - //% "Connect username with your pubkey" - headerText: qsTrId("connect-username-with-your-pubkey") + headerText: root.title //% "Continue" footerText: qsTrId("continue") @@ -104,11 +111,9 @@ ModalPopup { getFiatValue: walletModel.balanceView.getFiatValue defaultCurrency: walletModel.balanceView.defaultCurrency property var estimateGas: Backpressure.debounce(gasSelector, 600, function() { - if (!(root.ensUsername !== "" && selectFromAccount.selectedAccount)) { - selectedGasLimit = 80000; - return; - } - selectedGasLimit = profileModel.ens.setPubKeyGasEstimate(root.ensUsername, selectFromAccount.selectedAccount.address) + let estimatedGas = root.estimateGasFunction(selectFromAccount.selectedAccount); + gasSelector.selectedGasLimit = estimatedGas + return estimatedGas; }) } GasValidator { @@ -123,8 +128,7 @@ ModalPopup { } TransactionFormGroup { id: group3 - //% "Connect username with your pubkey" - headerText: qsTrId("connect-username-with-your-pubkey") + headerText: root.title //% "Sign with password" footerText: qsTrId("sign-with-password") @@ -148,8 +152,7 @@ ModalPopup { } TransactionFormGroup { id: group4 - //% "Connect username with your pubkey" - headerText: qsTrId("connect-username-with-your-pubkey") + headerText: root.title //% "Sign with password" footerText: qsTrId("sign-with-password") @@ -164,6 +167,23 @@ ModalPopup { footer: Item { width: parent.width height: btnNext.height + + StatusRoundButton { + id: btnBack + anchors.left: parent.left + icon.name: "arrow-right" + icon.width: 20 + icon.height: 16 + rotation: 180 + visible: stack.currentGroup.showBackBtn + enabled: stack.currentGroup.isValid || stack.isLastGroup + onClicked: { + if (typeof stack.currentGroup.onBackClicked === "function") { + return stack.currentGroup.onBackClicked() + } + stack.back() + } + } StatusButton { id: btnNext diff --git a/ui/shared/status/StatusSNTTransactionModal.qml b/ui/shared/status/StatusSNTTransactionModal.qml index f7bf730bf1..ee270eed24 100644 --- a/ui/shared/status/StatusSNTTransactionModal.qml +++ b/ui/shared/status/StatusSNTTransactionModal.qml @@ -115,7 +115,9 @@ ModalPopup { defaultCurrency: walletModel.balanceView.defaultCurrency width: stack.width property var estimateGas: Backpressure.debounce(gasSelector, 600, function() { - return root.estimateGasFunction(selectFromAccount.selectedAccount, uuid); + let estimatedGas = root.estimateGasFunction(selectFromAccount.selectedAccount, uuid); + gasSelector.selectedGasLimit = estimatedGas + return estimatedGas; }) } GasValidator {