feat(dapps): show estimated time for dApps requests
Implemented using the fees from the transaction data sent by the dApp. Also fixed the nim status go wrapper to send proper formatted string as expected on the other side. Updates: #15192
This commit is contained in:
parent
be1c6ba2ad
commit
4deea3461f
|
@ -168,7 +168,7 @@ proc newModule*(
|
||||||
result.collectibleDetailsController = collectible_detailsc.newController(int32(backend_collectibles.CollectiblesRequestID.WalletAccount), networkService, events)
|
result.collectibleDetailsController = collectible_detailsc.newController(int32(backend_collectibles.CollectiblesRequestID.WalletAccount), networkService, events)
|
||||||
result.filter = initFilter(result.controller)
|
result.filter = initFilter(result.controller)
|
||||||
|
|
||||||
result.walletConnectService = wc_service.newService(result.events, result.threadpool, settingsService)
|
result.walletConnectService = wc_service.newService(result.events, result.threadpool, settingsService, transactionService)
|
||||||
result.walletConnectController = wc_controller.newController(result.walletConnectService, walletAccountService)
|
result.walletConnectController = wc_controller.newController(result.walletConnectService, walletAccountService)
|
||||||
|
|
||||||
result.view = newView(result, result.activityController, result.tmpActivityControllers, result.activityDetailsController, result.collectibleDetailsController, result.walletConnectController)
|
result.view = newView(result, result.activityController, result.tmpActivityControllers, result.activityDetailsController, result.collectibleDetailsController, result.walletConnectController)
|
||||||
|
|
|
@ -70,3 +70,6 @@ QtObject:
|
||||||
|
|
||||||
proc sendTransaction*(self: Controller, address: string, chainId: int, password: string, txJson: string): string {.slot.} =
|
proc sendTransaction*(self: Controller, address: string, chainId: int, password: string, txJson: string): string {.slot.} =
|
||||||
return self.service.sendTransaction(address, chainId, password, txJson)
|
return self.service.sendTransaction(address, chainId, password, txJson)
|
||||||
|
|
||||||
|
proc getEstimatedTimeMinutesInterval(self: Controller, chainId: int, maxFeePerGas: string): int {.slot.} =
|
||||||
|
return self.service.getEstimatedTimeMinutesInterval(chainId, maxFeePerGas).int
|
|
@ -632,7 +632,7 @@ QtObject:
|
||||||
|
|
||||||
proc getEstimatedTime*(self: Service, chainId: int, maxFeePerGas: string): EstimatedTime =
|
proc getEstimatedTime*(self: Service, chainId: int, maxFeePerGas: string): EstimatedTime =
|
||||||
try:
|
try:
|
||||||
let response = backend.getTransactionEstimatedTime(chainId, maxFeePerGas.parseFloat).result.getInt
|
let response = backend.getTransactionEstimatedTime(chainId, maxFeePerGas).result.getInt
|
||||||
return EstimatedTime(response)
|
return EstimatedTime(response)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error "Error estimating transaction time", message = e.msg
|
error "Error estimating transaction time", message = e.msg
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import NimQml, chronicles, times, json
|
import NimQml, chronicles, times, json
|
||||||
|
import strutils
|
||||||
|
|
||||||
import backend/wallet_connect as status_go
|
import backend/wallet_connect as status_go
|
||||||
import backend/wallet
|
import backend/wallet
|
||||||
|
@ -6,6 +7,7 @@ import backend/wallet
|
||||||
import app_service/service/settings/service as settings_service
|
import app_service/service/settings/service as settings_service
|
||||||
import app_service/common/wallet_constants
|
import app_service/common/wallet_constants
|
||||||
from app_service/service/transaction/dto import PendingTransactionTypeDto
|
from app_service/service/transaction/dto import PendingTransactionTypeDto
|
||||||
|
import app_service/service/transaction/service as tr
|
||||||
|
|
||||||
import app/global/global_singleton
|
import app/global/global_singleton
|
||||||
|
|
||||||
|
@ -30,6 +32,7 @@ QtObject:
|
||||||
events: EventEmitter
|
events: EventEmitter
|
||||||
threadpool: ThreadPool
|
threadpool: ThreadPool
|
||||||
settingsService: settings_service.Service
|
settingsService: settings_service.Service
|
||||||
|
transactions: tr.Service
|
||||||
|
|
||||||
authenticationCallback: AuthenticationResponseFn
|
authenticationCallback: AuthenticationResponseFn
|
||||||
|
|
||||||
|
@ -40,6 +43,7 @@ QtObject:
|
||||||
events: EventEmitter,
|
events: EventEmitter,
|
||||||
threadpool: ThreadPool,
|
threadpool: ThreadPool,
|
||||||
settingsService: settings_service.Service,
|
settingsService: settings_service.Service,
|
||||||
|
transactions: tr.Service,
|
||||||
): Service =
|
): Service =
|
||||||
new(result, delete)
|
new(result, delete)
|
||||||
result.QObject.setup
|
result.QObject.setup
|
||||||
|
@ -47,6 +51,7 @@ QtObject:
|
||||||
result.events = events
|
result.events = events
|
||||||
result.threadpool = threadpool
|
result.threadpool = threadpool
|
||||||
result.settingsService = settings_service
|
result.settingsService = settings_service
|
||||||
|
result.transactions = transactions
|
||||||
|
|
||||||
proc init*(self: Service) =
|
proc init*(self: Service) =
|
||||||
self.events.on(SIGNAL_SHARED_KEYCARD_MODULE_USER_AUTHENTICATED) do(e: Args):
|
self.events.on(SIGNAL_SHARED_KEYCARD_MODULE_USER_AUTHENTICATED) do(e: Args):
|
||||||
|
@ -202,3 +207,7 @@ QtObject:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
return txResponse.getStr
|
return txResponse.getStr
|
||||||
|
|
||||||
|
proc getEstimatedTimeMinutesInterval*(self: Service, chainId: int, maxFeePerGas: string): EstimatedTime =
|
||||||
|
let maxFeePerGasInt = parseHexInt(maxFeePerGas)
|
||||||
|
return self.transactions.getEstimatedTime(chainId, $(maxFeePerGasInt.float))
|
||||||
|
|
|
@ -156,7 +156,7 @@ rpc(startWallet, "wallet"):
|
||||||
|
|
||||||
rpc(getTransactionEstimatedTime, "wallet"):
|
rpc(getTransactionEstimatedTime, "wallet"):
|
||||||
chainId: int
|
chainId: int
|
||||||
maxFeePerGas: float
|
maxFeePerGas: string
|
||||||
|
|
||||||
rpc(fetchPrices, "wallet"):
|
rpc(fetchPrices, "wallet"):
|
||||||
symbols: seq[string]
|
symbols: seq[string]
|
||||||
|
|
|
@ -20,6 +20,7 @@ import Storybook 1.0
|
||||||
|
|
||||||
import AppLayouts.Wallet.controls 1.0
|
import AppLayouts.Wallet.controls 1.0
|
||||||
import AppLayouts.Wallet.services.dapps 1.0
|
import AppLayouts.Wallet.services.dapps 1.0
|
||||||
|
import AppLayouts.Wallet.services.dapps.types 1.0
|
||||||
|
|
||||||
import SortFilterProxyModel 0.2
|
import SortFilterProxyModel 0.2
|
||||||
|
|
||||||
|
@ -331,7 +332,6 @@ Item {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function getDapps() {
|
function getDapps() {
|
||||||
let dappsJson = JSON.stringify(d.persistedDapps)
|
let dappsJson = JSON.stringify(d.persistedDapps)
|
||||||
this.dappsListReceived(dappsJson)
|
this.dappsListReceived(dappsJson)
|
||||||
|
@ -373,6 +373,10 @@ Item {
|
||||||
console.info(`calling mocked DAppsStore.sendTransaction(${topic}, ${id}, ${address}, ${chainId}, ${password}, ${tx})`)
|
console.info(`calling mocked DAppsStore.sendTransaction(${topic}, ${id}, ${address}, ${chainId}, ${password}, ${tx})`)
|
||||||
return "0xf8672a8402fb7acf82520894e2d622c817878da5143bbe068"
|
return "0xf8672a8402fb7acf82520894e2d622c817878da5143bbe068"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getEstimatedTime(chainId, maxFeePerGas) {
|
||||||
|
return Constants.TransactionEstimatedTime.LessThanThreeMins
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
walletRootStore: QObject {
|
walletRootStore: QObject {
|
||||||
|
|
|
@ -5,12 +5,13 @@ import QtQuick.Layouts 1.15
|
||||||
import StatusQ 0.1
|
import StatusQ 0.1
|
||||||
import SortFilterProxyModel 0.2
|
import SortFilterProxyModel 0.2
|
||||||
|
|
||||||
|
import AppLayouts.Wallet 1.0
|
||||||
import AppLayouts.Wallet.controls 1.0
|
import AppLayouts.Wallet.controls 1.0
|
||||||
|
|
||||||
import shared.popups.walletconnect 1.0
|
|
||||||
import AppLayouts.Wallet.services.dapps 1.0
|
import AppLayouts.Wallet.services.dapps 1.0
|
||||||
import AppLayouts.Wallet.services.dapps.types 1.0
|
import AppLayouts.Wallet.services.dapps.types 1.0
|
||||||
|
|
||||||
|
import shared.popups.walletconnect 1.0
|
||||||
|
|
||||||
import utils 1.0
|
import utils 1.0
|
||||||
|
|
||||||
DappsComboBox {
|
DappsComboBox {
|
||||||
|
@ -119,6 +120,7 @@ DappsComboBox {
|
||||||
property SessionRequestResolved request: null
|
property SessionRequestResolved request: null
|
||||||
|
|
||||||
sourceComponent: DAppSignRequestModal {
|
sourceComponent: DAppSignRequestModal {
|
||||||
|
id: dappRequestModal
|
||||||
objectName: "dappsRequestModal"
|
objectName: "dappsRequestModal"
|
||||||
loginType: request.account.migragedToKeycard ? Constants.LoginType.Keycard : root.loginType
|
loginType: request.account.migragedToKeycard ? Constants.LoginType.Keycard : root.loginType
|
||||||
visible: true
|
visible: true
|
||||||
|
@ -138,7 +140,7 @@ DappsComboBox {
|
||||||
currentCurrency: ""
|
currentCurrency: ""
|
||||||
fiatFees: request.maxFeesText
|
fiatFees: request.maxFeesText
|
||||||
cryptoFees: request.maxFeesEthText
|
cryptoFees: request.maxFeesEthText
|
||||||
estimatedTime: request.estimatedTimeText
|
estimatedTime: ""
|
||||||
feesLoading: !request.maxFeesText || !request.maxFeesEthText
|
feesLoading: !request.maxFeesText || !request.maxFeesEthText
|
||||||
hasFees: signingTransaction
|
hasFees: signingTransaction
|
||||||
enoughFundsForTransaction: request.enoughFunds
|
enoughFundsForTransaction: request.enoughFunds
|
||||||
|
@ -199,14 +201,16 @@ DappsComboBox {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// ignore error in case of tests and storybook where we don't have access to globalUtils
|
// ignore error in case of tests and storybook where we don't have access to globalUtils
|
||||||
}
|
}
|
||||||
|
|
||||||
cryptoFees = ethStr
|
cryptoFees = ethStr
|
||||||
enoughFundsForTransaction = haveEnoughFunds
|
enoughFundsForTransaction = haveEnoughFunds
|
||||||
enoughFundsForFees = haveEnoughFunds
|
enoughFundsForFees = haveEnoughFunds
|
||||||
feesLoading = false
|
feesLoading = false
|
||||||
hasFees = !!maxFees
|
hasFees = !!maxFees
|
||||||
}
|
}
|
||||||
function onEstimatedTimeUpdated(minMinutes, maxMinutes) {
|
|
||||||
estimatedTime = qsTr("%1-%2mins").arg(minMinutes).arg(maxMinutes)
|
function onEstimatedTimeUpdated(estimatedTimeEnum) {
|
||||||
|
dappRequestModal.estimatedTime = WalletUtils.getLabelForEstimatedTxTime(estimatedTimeEnum)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,8 @@ QObject {
|
||||||
signal displayToastMessage(string message, bool error)
|
signal displayToastMessage(string message, bool error)
|
||||||
signal sessionRequestResult(/*model entry of SessionRequestResolved*/ var request, bool isSuccess)
|
signal sessionRequestResult(/*model entry of SessionRequestResolved*/ var request, bool isSuccess)
|
||||||
signal maxFeesUpdated(real maxFees, int maxFeesWei, bool haveEnoughFunds, string symbol)
|
signal maxFeesUpdated(real maxFees, int maxFeesWei, bool haveEnoughFunds, string symbol)
|
||||||
signal estimatedTimeUpdated(int minMinutes, int maxMinutes)
|
// Reports Constants.TransactionEstimatedTime values
|
||||||
|
signal estimatedTimeUpdated(int estimatedTimeEnum)
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: sdk
|
target: sdk
|
||||||
|
@ -138,7 +139,6 @@ QObject {
|
||||||
maxFeesText: "?",
|
maxFeesText: "?",
|
||||||
maxFeesEthText: "?",
|
maxFeesEthText: "?",
|
||||||
enoughFunds: false,
|
enoughFunds: false,
|
||||||
estimatedTimeText: "?"
|
|
||||||
})
|
})
|
||||||
if (obj === null) {
|
if (obj === null) {
|
||||||
console.error("Error creating SessionRequestResolved for event")
|
console.error("Error creating SessionRequestResolved for event")
|
||||||
|
@ -158,20 +158,21 @@ QObject {
|
||||||
}
|
}
|
||||||
obj.resolveDappInfoFromSession(session)
|
obj.resolveDappInfoFromSession(session)
|
||||||
root.sessionRequest(obj)
|
root.sessionRequest(obj)
|
||||||
|
|
||||||
|
let estimatedTimeEnum = getEstimatedTimeInterval(data, method, obj.network.chainId)
|
||||||
|
root.estimatedTimeUpdated(estimatedTimeEnum)
|
||||||
|
|
||||||
// TODO #15192: update maxFees
|
// TODO #15192: update maxFees
|
||||||
if (!event.params.request.params[0].gasLimit || !event.params.request.params[0].gasPrice) {
|
if (!event.params.request.params[0].gasLimit || !event.params.request.params[0].gasPrice) {
|
||||||
root.maxFeesUpdated(0, 0, true, "")
|
root.maxFeesUpdated(0, 0, true, "")
|
||||||
root.estimatedTimeUpdated(0, 0)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let gasLimit = parseFloat(parseInt(event.params.request.params[0].gasLimit, 16));
|
let gasLimit = parseFloat(parseInt(event.params.request.params[0].gasLimit, 16));
|
||||||
let gasPrice = parseFloat(parseInt(event.params.request.params[0].gasPrice, 16));
|
let gasPrice = parseFloat(parseInt(event.params.request.params[0].gasPrice, 16));
|
||||||
let maxFees = gasLimit * gasPrice
|
let maxFees = gasLimit * gasPrice
|
||||||
root.maxFeesUpdated(maxFees/1000000000, maxFees, true, "Gwei")
|
root.maxFeesUpdated(maxFees/1000000000, maxFees, true, "Gwei")
|
||||||
// TODO #15192: update estimatedTime
|
|
||||||
root.estimatedTimeUpdated(1, 12)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return obj
|
return obj
|
||||||
|
@ -320,6 +321,34 @@ QObject {
|
||||||
console.error("No password or pin provided to sign message")
|
console.error("No password or pin provided to sign message")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns Constants.TransactionEstimatedTime
|
||||||
|
function getEstimatedTimeInterval(data, method, chainId) {
|
||||||
|
if (method != SessionRequest.methods.signTransaction.name
|
||||||
|
&& method != SessionRequest.methods.sendTransaction.name)
|
||||||
|
{
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
var maxFeePerGas = ""
|
||||||
|
if (!!tx.maxFeePerGas) {
|
||||||
|
maxFeePerGas = tx.maxFeePerGas
|
||||||
|
} else if (!!tx.gasPrice) {
|
||||||
|
maxFeePerGas = tx.gasPrice
|
||||||
|
}
|
||||||
|
if (!maxFeePerGas) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.store.getEstimatedTime(chainId, maxFeePerGas)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The queue is used to ensure that the events are processed in the order they are received but they could be
|
/// The queue is used to ensure that the events are processed in the order they are received but they could be
|
||||||
|
|
|
@ -5,6 +5,7 @@ import QtQml 2.15
|
||||||
import utils 1.0
|
import utils 1.0
|
||||||
|
|
||||||
QtObject {
|
QtObject {
|
||||||
|
|
||||||
/// Supported methods
|
/// Supported methods
|
||||||
/// userString is used in the context `dapp.url #{userString} <accepted/rejected>`
|
/// userString is used in the context `dapp.url #{userString} <accepted/rejected>`
|
||||||
/// requestDisplay is used in the context `dApp wants you to ${requestDisplay} with <Account Name Here>`
|
/// requestDisplay is used in the context `dApp wants you to ${requestDisplay} with <Account Name Here>`
|
||||||
|
|
|
@ -31,7 +31,6 @@ QObject {
|
||||||
readonly property string maxFeesText: ""
|
readonly property string maxFeesText: ""
|
||||||
readonly property string maxFeesEthText: ""
|
readonly property string maxFeesEthText: ""
|
||||||
readonly property bool enoughFunds: false
|
readonly property bool enoughFunds: false
|
||||||
readonly property string estimatedTimeText: ""
|
|
||||||
|
|
||||||
function resolveDappInfoFromSession(session) {
|
function resolveDappInfoFromSession(session) {
|
||||||
let meta = session.peer.metadata
|
let meta = session.peer.metadata
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
import QtQuick 2.15
|
|
||||||
import QtQuick.Layouts 1.15
|
|
||||||
|
|
||||||
import StatusQ.Core 0.1
|
|
||||||
import StatusQ.Core.Theme 0.1
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property alias estimatedTimeText: contentText.text
|
|
||||||
|
|
||||||
StatusBaseText {
|
|
||||||
text: qsTr("Est. time:")
|
|
||||||
font.pixelSize: 12
|
|
||||||
color: Theme.palette.directColor1
|
|
||||||
}
|
|
||||||
StatusBaseText {
|
|
||||||
id: contentText
|
|
||||||
|
|
||||||
font.pixelSize: 16
|
|
||||||
font.weight: Font.DemiBold
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +1,3 @@
|
||||||
MaxFeesDisplay 1.0 MaxFeesDisplay.qml
|
MaxFeesDisplay 1.0 MaxFeesDisplay.qml
|
||||||
EstimatedTimeDisplay 1.0 EstimatedTimeDisplay.qml
|
|
||||||
IntentionPanel 1.0 IntentionPanel.qml
|
IntentionPanel 1.0 IntentionPanel.qml
|
||||||
ContentPanel 1.0 ContentPanel.qml
|
ContentPanel 1.0 ContentPanel.qml
|
||||||
|
|
|
@ -63,6 +63,12 @@ QObject {
|
||||||
if (txObj.value) { tx.value = stripLeadingZeros(txObj.value) }
|
if (txObj.value) { tx.value = stripLeadingZeros(txObj.value) }
|
||||||
return tx
|
return tx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns ui/imports/utils -> Constants.TransactionEstimatedTime values
|
||||||
|
function getEstimatedTime(chainId, maxFeePerGas) {
|
||||||
|
return controller.getEstimatedTime(chainId, maxFeePerGas)
|
||||||
|
}
|
||||||
|
|
||||||
// Returns the hex encoded signature of the transaction or empty string if error
|
// Returns the hex encoded signature of the transaction or empty string if error
|
||||||
function signTransaction(topic, id, address, chainId, password, txObj) {
|
function signTransaction(topic, id, address, chainId, password, txObj) {
|
||||||
let tx = prepareTxForStatusGo(txObj)
|
let tx = prepareTxForStatusGo(txObj)
|
||||||
|
|
|
@ -1333,6 +1333,7 @@ QtObject {
|
||||||
readonly property string paraswapUrl: "app.paraswap.io"
|
readonly property string paraswapUrl: "app.paraswap.io"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mirrors src/app_service/service/transaction/service.nim -> EstimatedTime
|
||||||
enum TransactionEstimatedTime {
|
enum TransactionEstimatedTime {
|
||||||
Unknown = 0,
|
Unknown = 0,
|
||||||
LessThanOneMin,
|
LessThanOneMin,
|
||||||
|
|
Loading…
Reference in New Issue