From 248ba1c1c8136c9e1bf6dd79e069a1336711832a Mon Sep 17 00:00:00 2001 From: Stefan Date: Wed, 17 Jul 2024 13:46:43 +0300 Subject: [PATCH] feat(dapps): Add support for max fees in WalletConnect requests Compute max fees for transaction related requests and display the results to user. Also: - Add helper to convert from hex (backend) to decimal (frontend) values. - Add helper to convert from float gwei to hex wei - Update tests to accommodate for the new dependencies. Sourcing of account balances is not included therefore the transaction is allowed to go through even if the account balance is insufficient. An error will be generated by the backend in this case. Updates: #15192 --- .../wallet_connect/controller.nim | 28 ++- .../shared_modules/wallet_connect/helpers.nim | 21 ++ .../service/wallet_connect/service.nim | 3 + storybook/pages/DAppsWorkflowPage.qml | 8 + .../qmlTests/tests/tst_DAppsWorkflow.qml | 1 + test/nim/wallet_connect_tests.nim | 11 + .../Wallet/panels/DAppsWorkflow.qml | 36 ++-- .../services/dapps/DAppsRequestHandler.qml | 204 +++++++++++++----- .../services/dapps/WalletConnectService.qml | 1 + .../Wallet/services/dapps/helpers.js | 2 +- .../dapps/types/SessionRequestResolved.qml | 6 +- ui/imports/shared/stores/DAppsStore.qml | 25 ++- 12 files changed, 265 insertions(+), 81 deletions(-) create mode 100644 src/app/modules/shared_modules/wallet_connect/helpers.nim diff --git a/src/app/modules/shared_modules/wallet_connect/controller.nim b/src/app/modules/shared_modules/wallet_connect/controller.nim index 7dd28f1d5e..d81e509c49 100644 --- a/src/app/modules/shared_modules/wallet_connect/controller.nim +++ b/src/app/modules/shared_modules/wallet_connect/controller.nim @@ -4,9 +4,12 @@ import chronicles import app_service/service/wallet_connect/service as wallet_connect_service import app_service/service/wallet_account/service as wallet_account_service +import helpers + logScope: topics = "wallet-connect-controller" + QtObject: type Controller* = ref object of QObject @@ -44,16 +47,16 @@ QtObject: self.dappsListReceived(res) return true - proc userAuthenticationResult*(self: Controller, topic: string, id: string, error: bool, password: string, pin: string) {.signal.} + proc userAuthenticationResult*(self: Controller, topic: string, id: string, error: bool, password: string, pin: string, payload: string) {.signal.} # Beware, it will fail if an authentication is already in progress - proc authenticateUser*(self: Controller, topic: string, id: string, address: string): bool {.slot.} = + proc authenticateUser*(self: Controller, topic: string, id: string, address: string, payload: string): bool {.slot.} = let acc = self.walletAccountService.getAccountByAddress(address) if acc.keyUid == "": return false return self.service.authenticateUser(acc.keyUid, proc(password: string, pin: string, success: bool) = - self.userAuthenticationResult(topic, id, success, password, pin) + self.userAuthenticationResult(topic, id, success, password, pin, payload) ) proc signMessageUnsafe*(self: Controller, address: string, password: string, message: string): string {.slot.} = @@ -73,3 +76,22 @@ QtObject: proc getEstimatedTime(self: Controller, chainId: int, maxFeePerGasHex: string): int {.slot.} = return self.service.getEstimatedTime(chainId, maxFeePerGasHex).int + + proc getSuggestedFeesJson(self: Controller, chainId: int): string {.slot.} = + let dto = self.service.getSuggestedFees(chainId) + return dto.toJson() + + proc hexToDecBigString*(self: Controller, hex: string): string {.slot.} = + try: + return hexToDec(hex) + except Exception as e: + error "Failed to convert hex big int: ", hex=hex, ex=e.msg + return "" + + # Convert from float gwei to hex wei + proc convertFeesInfoToHex*(self: Controller, feesInfoJson: string): string {.slot.} = + try: + return convertFeesInfoToHex(feesInfoJson) + except Exception as e: + error "Failed to convert fees info to hex: ", feesInfoJson=feesInfoJson, ex=e.msg + return "" diff --git a/src/app/modules/shared_modules/wallet_connect/helpers.nim b/src/app/modules/shared_modules/wallet_connect/helpers.nim new file mode 100644 index 0000000000..6927b26a4d --- /dev/null +++ b/src/app/modules/shared_modules/wallet_connect/helpers.nim @@ -0,0 +1,21 @@ +import stint, json, strutils + +proc hexToDec*(hex: string): string = + return stint.parse(hex, UInt256, 16).toString() + +proc convertFeesInfoToHex*(feesInfoJson: string): string = + let parsedJson = parseJson(feesInfoJson) + + let maxFeeFloat = parsedJson["maxFeePerGas"].getFloat() + let maxFeeWei = int64(maxFeeFloat * 1e9) + + let maxPriorityFeeFloat = parsedJson["maxPriorityFeePerGas"].getFloat() + let maxPriorityFeeWei = int64(maxPriorityFeeFloat * 1e9) + + # Assemble the JSON and return it + var resultJson = %* { + "maxFeePerGas": "0x" & toHex(maxFeeWei).strip(chars = {'0'}, trailing = false), + "maxPriorityFeePerGas": "0x" & toHex(maxPriorityFeeWei).strip(chars = {'0'}, trailing = false) + } + return $resultJson + diff --git a/src/app_service/service/wallet_connect/service.nim b/src/app_service/service/wallet_connect/service.nim index d2d47dae96..879f92311e 100644 --- a/src/app_service/service/wallet_connect/service.nim +++ b/src/app_service/service/wallet_connect/service.nim @@ -225,3 +225,6 @@ QtObject: let maxFeePerGasInt = parseHexInt(maxFeePerGasHex) maxFeePerGas = maxFeePerGasInt.float return self.transactions.getEstimatedTime(chainId, $(maxFeePerGas)) + +proc getSuggestedFees*(self: Service, chainId: int): SuggestedFeesDto = + return self.transactions.suggestedFees(chainId) \ No newline at end of file diff --git a/storybook/pages/DAppsWorkflowPage.qml b/storybook/pages/DAppsWorkflowPage.qml index 79705e9ec9..2e9f9d73a9 100644 --- a/storybook/pages/DAppsWorkflowPage.qml +++ b/storybook/pages/DAppsWorkflowPage.qml @@ -377,6 +377,13 @@ Item { function getEstimatedTime(chainId, maxFeePerGas) { return Constants.TransactionEstimatedTime.LessThanThreeMins } + + function hexToDec(hex) { + if (hex.length > "0xfffffffffffff".length) { + console.warn(`Beware of possible loss of precision converting ${hex}`) + } + return parseInt(hex, 16).toString() + } } walletRootStore: QObject { @@ -390,6 +397,7 @@ Item { function getNetworkShortNames(chainIds) { return "eth:oeth:arb" } + readonly property CurrenciesStore currencyStore: CurrenciesStore {} } onDisplayToastMessage: (message, isErr) => { diff --git a/storybook/qmlTests/tests/tst_DAppsWorkflow.qml b/storybook/qmlTests/tests/tst_DAppsWorkflow.qml index 491469240a..ca3b3eae6f 100644 --- a/storybook/qmlTests/tests/tst_DAppsWorkflow.qml +++ b/storybook/qmlTests/tests/tst_DAppsWorkflow.qml @@ -155,6 +155,7 @@ Item { id: dappsRequestHandlerComponent DAppsRequestHandler { + currenciesStore: CurrenciesStore {} } } diff --git a/test/nim/wallet_connect_tests.nim b/test/nim/wallet_connect_tests.nim index ea47cda27f..8e7dfbe04d 100644 --- a/test/nim/wallet_connect_tests.nim +++ b/test/nim/wallet_connect_tests.nim @@ -2,8 +2,19 @@ import unittest import app/modules/main/wallet_section/poc_wallet_connect/helpers +import app/modules/shared_modules/wallet_connect/helpers + suite "wallet connect": + test "hexToDec": + check(hexToDec("0x3") == "3") + check(hexToDec("f") == "15") + + test "convertFeesInfoToHex": + const feesInfoJson = "{\"maxFees\":\"24528.282681\",\"maxFeePerGas\":1.168013461,\"maxPriorityFeePerGas\":0.036572351,\"gasPrice\":\"1.168013461\"}" + + check(convertFeesInfoToHex(feesInfoJson) == """{"maxFeePerGas":"0x459E7895","maxPriorityFeePerGas":"0x22E0CBF"}""") + test "parse deep link url": const testUrl = "https://status.app/wc?uri=wc%3Aa4f32854428af0f5b6635fb7a3cb2cfe174eaad63b9d10d52ef1c686f8eab862%402%3Frelay-protocol%3Dirn%26symKey%3D4ccbae2b4c81c26fbf4a6acee9de2771705d467de9a1d24c80240e8be59de6be" diff --git a/ui/app/AppLayouts/Wallet/panels/DAppsWorkflow.qml b/ui/app/AppLayouts/Wallet/panels/DAppsWorkflow.qml index f2de3de5b1..0ec1e2c4ec 100644 --- a/ui/app/AppLayouts/Wallet/panels/DAppsWorkflow.qml +++ b/ui/app/AppLayouts/Wallet/panels/DAppsWorkflow.qml @@ -3,6 +3,7 @@ import QtQuick.Controls 2.15 import QtQuick.Layouts 1.15 import StatusQ 0.1 +import StatusQ.Core.Utils 0.1 as SQUtils import SortFilterProxyModel 0.2 import AppLayouts.Wallet 1.0 @@ -159,6 +160,8 @@ DappsComboBox { loginType: request.account.migragedToKeycard ? Constants.LoginType.Keycard : root.loginType visible: true + property var feesInfo: null + dappUrl: request.dappUrl dappIcon: request.dappIcon dappName: request.dappName @@ -180,7 +183,7 @@ DappsComboBox { enoughFundsForTransaction: request.enoughFunds enoughFundsForFees: request.enoughFunds - signingTransaction: request.method === SessionRequest.methods.signTransaction.name || request.method === SessionRequest.methods.sendTransaction.name + signingTransaction: !!request.method && (request.method === SessionRequest.methods.signTransaction.name || request.method === SessionRequest.methods.sendTransaction.name) requestPayload: { switch(request.method) { case SessionRequest.methods.personalSign.name: @@ -219,8 +222,9 @@ DappsComboBox { console.error("Error signing: request is null") return } + requestHandled = true - root.wcService.requestHandler.authenticate(request) + root.wcService.requestHandler.authenticate(request, JSON.stringify(feesInfo)) } onRejected: { @@ -240,22 +244,18 @@ DappsComboBox { Connections { target: root.wcService.requestHandler - function onMaxFeesUpdated(maxFees, maxFeesWei, haveEnoughFunds, symbol) { - fiatFees = maxFees - currentCurrency = symbol - - var ethStr = "?" - try { - ethStr = globalUtils.wei2Eth(maxFeesWei, 9) - } catch (e) { - // ignore error in case of tests and storybook where we don't have access to globalUtils + function onMaxFeesUpdated(fiatMaxFees, ethMaxFees, haveEnoughFunds, haveEnoughFees, symbol, feesInfo) { + dappRequestModal.hasFees = !!ethMaxFees + dappRequestModal.feesLoading = !dappRequestModal.hasFees + if (!hasFees) { + return } - - cryptoFees = ethStr - enoughFundsForTransaction = haveEnoughFunds - enoughFundsForFees = haveEnoughFunds - feesLoading = false - hasFees = !!maxFees + dappRequestModal.fiatFees = fiatMaxFees.toString() + dappRequestModal.cryptoFees = ethMaxFees.toString() + dappRequestModal.currentCurrency = symbol + dappRequestModal.enoughFundsForTransaction = haveEnoughFunds + dappRequestModal.enoughFundsForFees = haveEnoughFees + dappRequestModal.feesInfo = feesInfo } function onEstimatedTimeUpdated(estimatedTimeEnum) { @@ -273,6 +273,8 @@ DappsComboBox { sessionRequestLoader.active = false } else { // TODO #14762 handle the error case + let userRejected = false + root.wcService.requestHandler.rejectSessionRequest(request, userRejected) } } } diff --git a/ui/app/AppLayouts/Wallet/services/dapps/DAppsRequestHandler.qml b/ui/app/AppLayouts/Wallet/services/dapps/DAppsRequestHandler.qml index e645c18cbd..a404f905e0 100644 --- a/ui/app/AppLayouts/Wallet/services/dapps/DAppsRequestHandler.qml +++ b/ui/app/AppLayouts/Wallet/services/dapps/DAppsRequestHandler.qml @@ -3,20 +3,21 @@ import QtQuick 2.15 import AppLayouts.Wallet.services.dapps 1.0 import AppLayouts.Wallet.services.dapps.types 1.0 -import StatusQ.Core.Utils 0.1 +import StatusQ.Core.Utils 0.1 as SQUtils import shared.stores 1.0 import utils 1.0 import "types" -QObject { +SQUtils.QObject { id: root required property WalletConnectSDKBase sdk required property DAppsStore store required property var accountsModel required property var networksModel + required property CurrenciesStore currenciesStore property alias requestsModel: requests @@ -26,14 +27,14 @@ QObject { } /// Beware, it will fail if called multiple times before getting an answer - function authenticate(request) { - return store.authenticateUser(request.topic, request.id, request.account.address) + function authenticate(request, payload) { + return store.authenticateUser(request.topic, request.id, request.account.address, payload) } signal sessionRequest(SessionRequestResolved request) signal displayToastMessage(string message, bool error) signal sessionRequestResult(/*model entry of SessionRequestResolved*/ var request, bool isSuccess) - signal maxFeesUpdated(real maxFees, int maxFeesWei, bool haveEnoughFunds, string symbol) + signal maxFeesUpdated(real fiatMaxFees, var /* Big */ ethMaxFees, bool haveEnoughFunds, bool haveEnoughFees, string symbol, var feesInfo) // Reports Constants.TransactionEstimatedTime values signal estimatedTimeUpdated(int estimatedTimeEnum) @@ -51,7 +52,7 @@ QObject { } function onSessionRequestUserAnswerResult(topic, id, accept, error) { - var request = requests.findRequest(topic, id) + let request = requests.findRequest(topic, id) if (request === null) { console.error("Error finding event for topic", topic, "id", id) return @@ -85,17 +86,17 @@ QObject { Connections { target: root.store - function onUserAuthenticated(topic, id, password, pin) { + function onUserAuthenticated(topic, id, password, pin, payload) { var request = requests.findRequest(topic, id) if (request === null) { console.error("Error finding event for topic", topic, "id", id) return } - d.executeSessionRequest(request, password, pin) + d.executeSessionRequest(request, password, pin, payload) } function onUserAuthenticationFailed(topic, id) { - var request = requests.findRequest(topic, id) + let request = requests.findRequest(topic, id) let methodStr = SessionRequest.methodToUserString(request.method) if (request === null || !methodStr) { return @@ -108,7 +109,7 @@ QObject { } } - QObject { + SQUtils.QObject { id: d function resolveAsync(event) { @@ -128,6 +129,7 @@ QObject { console.error("Error in event data lookup", JSON.stringify(event)) return null } + let enoughFunds = !d.isTransactionMethod(method) let obj = sessionRequestComponent.createObject(null, { event, topic: event.topic, @@ -138,7 +140,7 @@ QObject { data, maxFeesText: "?", maxFeesEthText: "?", - enoughFunds: false, + enoughFunds: enoughFunds, }) if (obj === null) { console.error("Error creating SessionRequestResolved for event") @@ -159,20 +161,16 @@ QObject { obj.resolveDappInfoFromSession(session) root.sessionRequest(obj) - let estimatedTimeEnum = getEstimatedTimeInterval(data, method, obj.network.chainId) - root.estimatedTimeUpdated(estimatedTimeEnum) - - // TODO #15192: update maxFees - if (!event.params.request.params[0].gasLimit || !event.params.request.params[0].gasPrice) { - root.maxFeesUpdated(0, 0, true, "") + if (!d.isTransactionMethod(method)) { return } - let gasLimit = parseFloat(parseInt(event.params.request.params[0].gasLimit, 16)); - let gasPrice = parseFloat(parseInt(event.params.request.params[0].gasPrice, 16)); - let maxFees = gasLimit * gasPrice - root.maxFeesUpdated(maxFees/1000000000, maxFees, true, "Gwei") + let estimatedTimeEnum = getEstimatedTimeInterval(data, method, obj.network.chainId) + root.estimatedTimeUpdated(estimatedTimeEnum) + let st = getEstimatedFeesStatus(data, method, obj.network.chainId) + + root.maxFeesUpdated(st.fiatMaxFees, st.maxFeesEth, st.haveEnoughFunds, st.haveEnoughFees, st.symbol, st.feesInfo) }) return obj @@ -180,7 +178,7 @@ QObject { /// Returns null if the account is not found function lookupAccountFromEvent(event, method) { - var address = "" + let address = "" if (method === SessionRequest.methods.personalSign.name) { if (event.params.request.params.length < 2) { return null @@ -198,14 +196,13 @@ QObject { return null } address = event.params.request.params[0] - } else if (method === SessionRequest.methods.signTransaction.name - || method === SessionRequest.methods.sendTransaction.name) { + } else if (d.isTransactionMethod(method)) { if (event.params.request.params.length == 0) { return null } address = event.params.request.params[0].from } - return ModelUtils.getFirstModelEntryIf(root.accountsModel, (account) => { + return SQUtils.ModelUtils.getFirstModelEntryIf(root.accountsModel, (account) => { return account.address.toLowerCase() === address.toLowerCase(); }) } @@ -216,7 +213,7 @@ QObject { return null } let chainId = Helpers.chainIdFromEip155(event.params.chainId) - return ModelUtils.getByKey(root.networksModel, "chainId", chainId) + return SQUtils.ModelUtils.getByKey(root.networksModel, "chainId", chainId) } function extractMethodData(event, method) { @@ -226,7 +223,7 @@ QObject { if (event.params.request.params.length < 1) { return null } - var message = "" + let message = "" let messageIndex = (method === SessionRequest.methods.personalSign.name ? 0 : 1) let messageParam = event.params.request.params[messageIndex] // There is no standard on how data is encoded. Therefore we support hex or utf8 @@ -275,14 +272,14 @@ QObject { }) } - function executeSessionRequest(request, password, pin) { + function executeSessionRequest(request, password, pin, payload) { if (!SessionRequest.getSupportedMethods().includes(request.method)) { console.error("Unsupported method to execute: ", request.method) return } if (password !== "") { - var actionResult = "" + let actionResult = "" if (request.method === SessionRequest.methods.sign.name) { actionResult = store.signMessageUnsafe(request.topic, request.id, request.account.address, password, @@ -299,15 +296,36 @@ QObject { request.account.address, password, SessionRequest.methods.signTypedData.getMessageFromData(request.data), request.network.chainId, legacy) - } else if (request.method === SessionRequest.methods.signTransaction.name) { - let txObj = SessionRequest.methods.signTransaction.getTxObjFromData(request.data) - actionResult = store.signTransaction(request.topic, request.id, - request.account.address, request.network.chainId, password, txObj) - } else if (request.method === SessionRequest.methods.sendTransaction.name) { - let txObj = SessionRequest.methods.sendTransaction.getTxObjFromData(request.data) - actionResult = store.sendTransaction(request.topic, request.id, - request.account.address, request.network.chainId, password, txObj) + } else if (d.isTransactionMethod(request.method)) { + let txObj = d.getTxObject(request.method, request.data) + if (!!payload) { + let feesInfoJson = payload + let hexFeesJson = root.store.convertFeesInfoToHex(feesInfoJson) + if (!!hexFeesJson) { + let feesInfo = JSON.parse(hexFeesJson) + if (feesInfo.maxFeePerGas) { + txObj.maxFeePerGas = feesInfo.maxFeePerGas + } + if (feesInfo.maxPriorityFeePerGas) { + txObj.maxPriorityFeePerGas = feesInfo.maxPriorityFeePerGas + } + } + delete txObj.gasLimit + delete txObj.gasPrice + } + // Remove nonce from txObj to be auto-filled by the wallet + delete txObj.nonce + + if (request.method === SessionRequest.methods.signTransaction.name) { + actionResult = store.signTransaction(request.topic, request.id, + request.account.address, request.network.chainId, password, txObj) + } else if (request.method === SessionRequest.methods.sendTransaction.name) { + let txObj = SessionRequest.methods.sendTransaction.getTxObjFromData(request.data) + actionResult = store.sendTransaction(request.topic, request.id, + request.account.address, request.network.chainId, password, txObj) + } } + let isSuccessful = (actionResult != "") if (isSuccessful) { // acceptSessionRequest will trigger an sdk.sessionRequestUserAnswerResult signal @@ -324,28 +342,110 @@ QObject { // Returns Constants.TransactionEstimatedTime function getEstimatedTimeInterval(data, method, chainId) { - if (method != SessionRequest.methods.signTransaction.name - && method != SessionRequest.methods.sendTransaction.name) - { - return "" + let tx = {} + let maxFeePerGas = "" + if (d.isTransactionMethod(method)) { + tx = d.getTxObject(method, data) + // Empty string instructs getEstimatedTime to fetch the blockchain value + if (!!tx.maxFeePerGas) { + maxFeePerGas = tx.maxFeePerGas + } else if (!!tx.gasPrice) { + maxFeePerGas = tx.gasPrice + } } - var tx = {} + return root.store.getEstimatedTime(chainId, maxFeePerGas) + } + + // Returns { + // maxFees -> Big number in Gwei + // maxFeePerGas + // maxPriorityFeePerGas + // gasPrice + // } + function getEstimatedMaxFees(data, method, chainId) { + let tx = {} + if (d.isTransactionMethod(method)) { + tx = d.getTxObject(method, data) + } + + let Math = SQUtils.AmountsArithmetic + let gasLimit = Math.fromString("21000") + let gasPrice, maxFeePerGas, maxPriorityFeePerGas + // Beware, the tx values are standard blockchain hex big number values; the fees values are nim's float64 values, hence the complex conversions + if (!!tx.maxFeePerGas && !!tx.maxPriorityFeePerGas) { + let maxFeePerGasDec = root.store.hexToDec(tx.maxFeePerGas) + gasPrice = Math.fromString(maxFeePerGasDec) + // Source fees info from the incoming transaction for when we process it + maxFeePerGas = maxFeePerGasDec + let maxPriorityFeePerGasDec = root.store.hexToDec(tx.maxPriorityFeePerGas) + maxPriorityFeePerGas = maxPriorityFeePerGasDec + } else { + let fees = root.store.getSuggestedFees(chainId) + maxPriorityFeePerGas = fees.maxPriorityFeePerGas + if (fees.eip1559Enabled) { + if (!!fees.maxFeePerGasM) { + gasPrice = Math.fromString(fees.maxFeePerGasM) + maxFeePerGas = fees.maxFeePerGasM + } else if(!!tx.maxFeePerGas) { + let maxFeePerGasDec = root.store.hexToDec(tx.maxFeePerGas) + gasPrice = Math.fromString(maxFeePerGasDec) + maxFeePerGas = maxFeePerGasDec + } else { + console.error("Error fetching maxFeePerGas from fees or tx objects") + return + } + } else { + if (!!fees.gasPrice) { + gasPrice = Math.fromString(fees.gasPrice) + } else { + console.error("Error fetching suggested fees") + return + } + } + gasPrice = Math.sum(gasPrice, Math.fromString(fees.l1GasFee)) + } + + let maxFees = Math.times(gasLimit, gasPrice) + return {maxFees, maxFeePerGas, maxPriorityFeePerGas, gasPrice} + } + + function getEstimatedFeesStatus(data, method, chainId) { + let Math = SQUtils.AmountsArithmetic + + let feesInfo = getEstimatedMaxFees(data, method, chainId) + + let maxFeesEth = Math.div(feesInfo.maxFees, Math.fromString("1000000000")) + + // TODO #15192: extract account.balance + //let accountFundsEth = account.balance + //let haveEnoughFees = Math.cmp(accountFundsEth, maxFeesEth) >= 0 + let haveEnoughFees = true + + let maxFeesEthStr = maxFeesEth.toString() + let fiatMaxFees = root.currenciesStore.getFiatValue(maxFeesEthStr, Constants.ethToken) + let symbol = root.currenciesStore.currentCurrency + + // We don't process the transaction so we don't have this information yet + let haveEnoughFunds = true + return {fiatMaxFees, maxFeesEth, haveEnoughFunds, haveEnoughFees, symbol, feesInfo} + } + + function isTransactionMethod(method) { + return method === SessionRequest.methods.signTransaction.name + || method === SessionRequest.methods.sendTransaction.name + } + + function getTxObject(method, data) { + let tx if (method === SessionRequest.methods.signTransaction.name) { tx = SessionRequest.methods.signTransaction.getTxObjFromData(data) } else if (method === SessionRequest.methods.sendTransaction.name) { tx = SessionRequest.methods.sendTransaction.getTxObjFromData(data) + } else { + console.error("Not a transaction method") } - - // Empty string instructs getEstimatedTime to fetch the blockchain value - var maxFeePerGas = "" - if (!!tx.maxFeePerGas) { - maxFeePerGas = tx.maxFeePerGas - } else if (!!tx.gasPrice) { - maxFeePerGas = tx.gasPrice - } - - return root.store.getEstimatedTime(chainId, maxFeePerGas) + return tx } } diff --git a/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectService.qml b/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectService.qml index 851faceb36..5c1da37440 100644 --- a/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectService.qml +++ b/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectService.qml @@ -238,6 +238,7 @@ QObject { store: root.store accountsModel: root.validAccounts networksModel: root.flatNetworks + currenciesStore: root.walletRootStore.currencyStore onSessionRequest: (request) => { timeoutTimer.stop() diff --git a/ui/app/AppLayouts/Wallet/services/dapps/helpers.js b/ui/app/AppLayouts/Wallet/services/dapps/helpers.js index 7925d235d4..a7c2db0590 100644 --- a/ui/app/AppLayouts/Wallet/services/dapps/helpers.js +++ b/ui/app/AppLayouts/Wallet/services/dapps/helpers.js @@ -99,4 +99,4 @@ function extractInfoFromPairUri(uri) { } } return { topic, expiry } -} \ No newline at end of file +} diff --git a/ui/app/AppLayouts/Wallet/services/dapps/types/SessionRequestResolved.qml b/ui/app/AppLayouts/Wallet/services/dapps/types/SessionRequestResolved.qml index d7ebecf1cc..d6fb10a04c 100644 --- a/ui/app/AppLayouts/Wallet/services/dapps/types/SessionRequestResolved.qml +++ b/ui/app/AppLayouts/Wallet/services/dapps/types/SessionRequestResolved.qml @@ -28,9 +28,9 @@ QObject { readonly property alias dappUrl: d.dappUrl readonly property alias dappIcon: d.dappIcon - readonly property string maxFeesText: "" - readonly property string maxFeesEthText: "" - readonly property bool enoughFunds: false + property string maxFeesText: "" + property string maxFeesEthText: "" + property bool enoughFunds: false function resolveDappInfoFromSession(session) { let meta = session.peer.metadata diff --git a/ui/imports/shared/stores/DAppsStore.qml b/ui/imports/shared/stores/DAppsStore.qml index d2a3bb977a..4c87413205 100644 --- a/ui/imports/shared/stores/DAppsStore.qml +++ b/ui/imports/shared/stores/DAppsStore.qml @@ -8,7 +8,7 @@ QObject { required property var controller /// \c dappsJson serialized from status-go.wallet.GetDapps signal dappsListReceived(string dappsJson) - signal userAuthenticated(string topic, string id, string password, string pin) + signal userAuthenticated(string topic, string id, string password, string pin, string payload) signal userAuthenticationFailed(string topic, string id) function addWalletConnectSession(sessionJson) { @@ -23,8 +23,8 @@ QObject { return controller.updateSessionsMarkedAsActive(activeTopicsJson) } - function authenticateUser(topic, id, address) { - let ok = controller.authenticateUser(topic, id, address) + function authenticateUser(topic, id, address, payload) { + let ok = controller.authenticateUser(topic, id, address, payload) if(!ok) { root.userAuthenticationFailed() } @@ -70,6 +70,12 @@ QObject { return controller.getEstimatedTime(chainId, maxFeePerGasHex) } + // Returns nim's SuggestedFeesDto; see src/app_service/service/transaction/dto.nim + // Returns all value initialized to 0 if error + function getSuggestedFees(chainId) { + return JSON.parse(controller.getSuggestedFeesJson(chainId)) + } + // Returns the hex encoded signature of the transaction or empty string if error function signTransaction(topic, id, address, chainId, password, txObj) { let tx = prepareTxForStatusGo(txObj) @@ -87,6 +93,15 @@ QObject { return controller.getDapps() } + function hexToDec(hex) { + return controller.hexToDecBigString(hex) + } + + // Return just the modified fields { "maxFeePerGas": "0x<...>", "maxPriorityFeePerGas": "0x<...>" } + function convertFeesInfoToHex(feesInfoJson) { + return controller.convertFeesInfoToHex(feesInfoJson) + } + // Handle async response from controller Connections { target: controller @@ -95,9 +110,9 @@ QObject { root.dappsListReceived(dappsJson) } - function onUserAuthenticationResult(topic, id, success, password, pin) { + function onUserAuthenticationResult(topic, id, success, password, pin, payload) { if (success) { - root.userAuthenticated(topic, id, password, pin) + root.userAuthenticated(topic, id, password, pin, payload) } else { root.userAuthenticationFailed(topic, id) }