fix(@desktop/wallet): estimated time for transaction is always Unknown
Fixes #5938
This commit is contained in:
parent
2251f870a0
commit
60ed62231e
|
@ -105,4 +105,7 @@ proc getChainIdForChat*(self: Controller): int =
|
||||||
return self.networkService.getNetworkForChat().chainId
|
return self.networkService.getNetworkForChat().chainId
|
||||||
|
|
||||||
proc getChainIdForBrowser*(self: Controller): int =
|
proc getChainIdForBrowser*(self: Controller): int =
|
||||||
return self.networkService.getNetworkForBrowser().chainId
|
return self.networkService.getNetworkForBrowser().chainId
|
||||||
|
|
||||||
|
proc getEstimatedTime*(self: Controller, priorityFeePerGas: string, maxFeePerGas: string): EstimatedTime =
|
||||||
|
return self.transactionService.getEstimatedTime(priorityFeePerGas, maxFeePerGas)
|
|
@ -64,6 +64,9 @@ method getChainIdForChat*(self: AccessInterface): int =
|
||||||
method getChainIdForBrowser*(self: AccessInterface): int =
|
method getChainIdForBrowser*(self: AccessInterface): int =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method getEstimatedTime*(self: AccessInterface, priorityFeePerGas: string, maxFeePerGas: string): int {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
# View Delegate Interface
|
# View Delegate Interface
|
||||||
# Delegate for the view must be declared here due to use of QtObject and multi
|
# Delegate for the view must be declared here due to use of QtObject and multi
|
||||||
# inheritance, which is not well supported in Nim.
|
# inheritance, which is not well supported in Nim.
|
||||||
|
|
|
@ -109,4 +109,7 @@ method getChainIdForChat*(self: Module): int =
|
||||||
return self.controller.getChainIdForChat()
|
return self.controller.getChainIdForChat()
|
||||||
|
|
||||||
method getChainIdForBrowser*(self: Module): int =
|
method getChainIdForBrowser*(self: Module): int =
|
||||||
return self.controller.getChainIdForBrowser()
|
return self.controller.getChainIdForBrowser()
|
||||||
|
|
||||||
|
method getEstimatedTime*(self: Module, priorityFeePerGas: string, maxFeePerGas: string): int =
|
||||||
|
return self.controller.getEstimatedTime(priorityFeePerGas, maxFeePerGas).int
|
|
@ -139,4 +139,7 @@ QtObject:
|
||||||
return self.delegate.getChainIdForChat()
|
return self.delegate.getChainIdForChat()
|
||||||
|
|
||||||
proc getChainIdForBrowser*(self: View): int {.slot.} =
|
proc getChainIdForBrowser*(self: View): int {.slot.} =
|
||||||
return self.delegate.getChainIdForBrowser()
|
return self.delegate.getChainIdForBrowser()
|
||||||
|
|
||||||
|
proc getEstimatedTime*(self: View, priorityFeePerGas: string, maxFeePerGas: string): int {.slot.} =
|
||||||
|
return self.delegate.getEstimatedTime(priorityFeePerGas, maxFeePerGas)
|
|
@ -33,6 +33,13 @@ include ../../common/json_utils
|
||||||
const SIGNAL_TRANSACTIONS_LOADED* = "transactionsLoaded"
|
const SIGNAL_TRANSACTIONS_LOADED* = "transactionsLoaded"
|
||||||
const SIGNAL_TRANSACTION_SENT* = "transactionSent"
|
const SIGNAL_TRANSACTION_SENT* = "transactionSent"
|
||||||
|
|
||||||
|
type
|
||||||
|
EstimatedTime* {.pure.} = enum
|
||||||
|
Unknown = -1
|
||||||
|
LessThanOneMin
|
||||||
|
LessThanThreeMins
|
||||||
|
LessThanFiveMins
|
||||||
|
|
||||||
type
|
type
|
||||||
TransactionMinedArgs* = ref object of Args
|
TransactionMinedArgs* = ref object of Args
|
||||||
data*: string
|
data*: string
|
||||||
|
@ -394,3 +401,69 @@ QtObject:
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error "Error fetching crypto services", message = e.msg
|
error "Error fetching crypto services", message = e.msg
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
|
proc addToAllTransactionsAndSetNewMinMax(self: Service, myTip: float, numOfTransactionWithTipLessThanMine: var int,
|
||||||
|
transactions: JsonNode) =
|
||||||
|
if transactions.kind != JArray:
|
||||||
|
return
|
||||||
|
for t in transactions:
|
||||||
|
let gasPriceUnparsed = $fromHex(Stuint[256], t{"gasPrice"}.getStr)
|
||||||
|
let gasPrice = parseFloat(wei2gwei(gasPriceUnparsed))
|
||||||
|
if gasPrice < myTip:
|
||||||
|
numOfTransactionWithTipLessThanMine.inc
|
||||||
|
|
||||||
|
proc getEstimatedTime*(self: Service, priorityFeePerGas: string, maxFeePerGas: string): EstimatedTime =
|
||||||
|
let priorityFeePerGasF = priorityFeePerGas.parseFloat
|
||||||
|
let maxFeePerGasF = maxFeePerGas.parseFloat
|
||||||
|
var transactionsProcessed = 0
|
||||||
|
var numOfTransactionWithTipLessThanMine = 0
|
||||||
|
var latestBlockNumber: Option[Uint256]
|
||||||
|
var expectedBaseFeeForNextBlock: float
|
||||||
|
try:
|
||||||
|
let response = eth.getBlockByNumber("latest", true)
|
||||||
|
if response.error.isNil:
|
||||||
|
let transactionsJson = response.result{"transactions"}
|
||||||
|
self.addToAllTransactionsAndSetNewMinMax(priorityFeePerGasF, numOfTransactionWithTipLessThanMine, transactionsJson)
|
||||||
|
transactionsProcessed = transactionsJson.len
|
||||||
|
latestBlockNumber = some(stint.fromHex(Uint256, response.result{"number"}.getStr))
|
||||||
|
let latestBlockBaseFeePerGasUnparsed = $fromHex(Stuint[256], response.result{"baseFeePerGas"}.getStr)
|
||||||
|
let latestBlockBaseFeePerGas = parseFloat(wei2gwei(latestBlockBaseFeePerGasUnparsed))
|
||||||
|
let latestBlockGasUsedUnparsed = $fromHex(Stuint[256], response.result{"gasUsed"}.getStr)
|
||||||
|
let latestBlockGasUsed = parseFloat(wei2gwei(latestBlockGasUsedUnparsed))
|
||||||
|
let latestBlockGasLimitUnparsed = $fromHex(Stuint[256], response.result{"gasLimit"}.getStr)
|
||||||
|
let latestBlockGasLimit = parseFloat(wei2gwei(latestBlockGasLimitUnparsed))
|
||||||
|
|
||||||
|
let ratio = latestBlockGasUsed / latestBlockGasLimit * 0.01
|
||||||
|
let maxFeeChange = latestBlockBaseFeePerGas * 0.125
|
||||||
|
if(ratio > 50):
|
||||||
|
expectedBaseFeeForNextBlock = latestBlockBaseFeePerGas + maxFeeChange * (ratio - 50) * 0.01
|
||||||
|
else:
|
||||||
|
expectedBaseFeeForNextBlock = latestBlockBaseFeePerGas - maxFeeChange * (50 - ratio) * 0.01
|
||||||
|
except Exception as e:
|
||||||
|
error "error fetching latest block", msg=e.msg
|
||||||
|
|
||||||
|
if latestBlockNumber.isNone or
|
||||||
|
priorityFeePerGasF + expectedBaseFeeForNextBlock > maxFeePerGasF:
|
||||||
|
return EstimatedTime.Unknown
|
||||||
|
|
||||||
|
var blockNumber = latestBlockNumber.get
|
||||||
|
while (transactionsProcessed < 100 and latestBlockNumber.get < blockNumber + 10):
|
||||||
|
blockNumber = blockNumber - 1
|
||||||
|
try:
|
||||||
|
let hexPrevNum = "0x" & stint.toHex(blockNumber)
|
||||||
|
let response = getBlockByNumber(hexPrevNum, true)
|
||||||
|
let transactionsJson = response.result{"transactions"}
|
||||||
|
self.addToAllTransactionsAndSetNewMinMax(priorityFeePerGasF, numOfTransactionWithTipLessThanMine, transactionsJson)
|
||||||
|
transactionsProcessed += transactionsJson.len
|
||||||
|
except Exception as e:
|
||||||
|
error "error fetching block number", blockNumber=blockNumber, msg=e.msg
|
||||||
|
|
||||||
|
let p = numOfTransactionWithTipLessThanMine / transactionsProcessed
|
||||||
|
if p > 0.5:
|
||||||
|
return EstimatedTime.LessThanOneMin
|
||||||
|
elif p > 0.35:
|
||||||
|
return EstimatedTime.LessThanThreeMins
|
||||||
|
elif p > 0.15:
|
||||||
|
return EstimatedTime.LessThanFiveMins
|
||||||
|
else:
|
||||||
|
return EstimatedTime.Unknown
|
|
@ -6,8 +6,8 @@ export response_type
|
||||||
proc getAccounts*(): RpcResponse[JsonNode] {.raises: [Exception].} =
|
proc getAccounts*(): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||||
return core.callPrivateRPC("eth_accounts")
|
return core.callPrivateRPC("eth_accounts")
|
||||||
|
|
||||||
proc getBlockByNumber*(blockNumber: string): RpcResponse[JsonNode] {.raises: [Exception].} =
|
proc getBlockByNumber*(blockNumber: string, fullTransactionObject = false): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||||
let payload = %* [blockNumber, false]
|
let payload = %* [blockNumber, fullTransactionObject]
|
||||||
return core.callPrivateRPC("eth_getBlockByNumber", payload)
|
return core.callPrivateRPC("eth_getBlockByNumber", payload)
|
||||||
|
|
||||||
proc getNativeChainBalance*(chainId: int, address: string): RpcResponse[JsonNode] {.raises: [Exception].} =
|
proc getNativeChainBalance*(chainId: int, address: string): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||||
|
|
|
@ -99,6 +99,10 @@ QtObject {
|
||||||
return JSON.parse(walletSectionTransactions.suggestedFees(chainId))
|
return JSON.parse(walletSectionTransactions.suggestedFees(chainId))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getEstimatedTime(priorityFeePerGas, maxFeePerGas) {
|
||||||
|
return walletSectionTransactions.getEstimatedTime(priorityFeePerGas, maxFeePerGas)
|
||||||
|
}
|
||||||
|
|
||||||
function getChainIdForChat() {
|
function getChainIdForChat() {
|
||||||
return walletSectionTransactions.getChainIdForChat()
|
return walletSectionTransactions.getChainIdForChat()
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,11 +21,13 @@ Item {
|
||||||
property var getGasGweiValue: function () {}
|
property var getGasGweiValue: function () {}
|
||||||
property var getGasEthValue: function () {}
|
property var getGasEthValue: function () {}
|
||||||
property var getFiatValue: function () {}
|
property var getFiatValue: function () {}
|
||||||
|
property var getEstimatedTime: function () {}
|
||||||
property string defaultCurrency: "USD"
|
property string defaultCurrency: "USD"
|
||||||
property alias selectedGasPrice: inputGasPrice.text
|
property alias selectedGasPrice: inputGasPrice.text
|
||||||
property alias selectedGasLimit: inputGasLimit.text
|
property alias selectedGasLimit: inputGasLimit.text
|
||||||
property string defaultGasLimit: "0"
|
property string defaultGasLimit: "0"
|
||||||
property string maxFiatFees: selectedGasFiatValue + root.defaultCurrency.toUpperCase()
|
property string maxFiatFees: selectedGasFiatValue + root.defaultCurrency.toUpperCase()
|
||||||
|
property int estimatedTxTimeFlag: Constants.transactionEstimatedTime.unknown
|
||||||
|
|
||||||
|
|
||||||
property alias selectedTipLimit: inputPerGasTipLimit.text
|
property alias selectedTipLimit: inputPerGasTipLimit.text
|
||||||
|
@ -62,11 +64,15 @@ Item {
|
||||||
return
|
return
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Qt.callLater(function () {
|
||||||
let ethValue = root.getGasEthValue(inputGasPrice.text, inputGasLimit.text)
|
let ethValue = root.getGasEthValue(inputGasPrice.text, inputGasLimit.text)
|
||||||
|
|
||||||
let fiatValue = root.getFiatValue(ethValue, "ETH", root.defaultCurrency)
|
let fiatValue = root.getFiatValue(ethValue, "ETH", root.defaultCurrency)
|
||||||
selectedGasEthValue = ethValue
|
selectedGasEthValue = ethValue
|
||||||
selectedGasFiatValue = fiatValue
|
selectedGasFiatValue = fiatValue
|
||||||
|
root.estimatedTxTimeFlag = root.getEstimatedTime(inputPerGasTipLimit.text, inputGasPrice.text)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function appendError(accum, error, nonBlocking = false) {
|
function appendError(accum, error, nonBlocking = false) {
|
||||||
|
@ -111,6 +117,11 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
updateGasEthValue()
|
||||||
|
checkLimits()
|
||||||
|
}
|
||||||
|
|
||||||
function validate() {
|
function validate() {
|
||||||
// causes error on application load without a null check
|
// causes error on application load without a null check
|
||||||
if (!inputGasLimit || !inputGasPrice || !inputPerGasTipLimit) {
|
if (!inputGasLimit || !inputGasPrice || !inputPerGasTipLimit) {
|
||||||
|
|
|
@ -176,6 +176,7 @@ StatusModal {
|
||||||
anchors.top: networkSelector.bottom
|
anchors.top: networkSelector.bottom
|
||||||
getGasEthValue: popup.store.getGasEthValue
|
getGasEthValue: popup.store.getGasEthValue
|
||||||
getFiatValue: popup.store.getFiatValue
|
getFiatValue: popup.store.getFiatValue
|
||||||
|
getEstimatedTime: popup.store.getEstimatedTime
|
||||||
defaultCurrency: popup.store.currentCurrency
|
defaultCurrency: popup.store.currentCurrency
|
||||||
|
|
||||||
width: stack.width
|
width: stack.width
|
||||||
|
@ -239,6 +240,7 @@ StatusModal {
|
||||||
|
|
||||||
advancedFooterComponent: SendModalFooter {
|
advancedFooterComponent: SendModalFooter {
|
||||||
maxFiatFees: gasSelector.maxFiatFees
|
maxFiatFees: gasSelector.maxFiatFees
|
||||||
|
estimatedTxTimeFlag: gasSelector.estimatedTxTimeFlag
|
||||||
currentGroupPending: popup.contentItem.currentGroup.isPending
|
currentGroupPending: popup.contentItem.currentGroup.isPending
|
||||||
currentGroupValid: popup.contentItem.currentGroup.isValid
|
currentGroupValid: popup.contentItem.currentGroup.isValid
|
||||||
isLastGroup: popup.contentItem.isLastGroup
|
isLastGroup: popup.contentItem.isLastGroup
|
||||||
|
|
|
@ -12,15 +12,18 @@ import StatusQ.Core.Theme 0.1
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: footer
|
id: footer
|
||||||
|
|
||||||
//% "Unknown"
|
|
||||||
property string estimatedTime: qsTr("Unknown")
|
|
||||||
property string maxFiatFees: ""
|
property string maxFiatFees: ""
|
||||||
|
property int estimatedTxTimeFlag: Constants.transactionEstimatedTime.unknown
|
||||||
property bool currentGroupPending: true
|
property bool currentGroupPending: true
|
||||||
property bool currentGroupValid: false
|
property bool currentGroupValid: false
|
||||||
property bool isLastGroup: false
|
property bool isLastGroup: false
|
||||||
|
|
||||||
signal nextButtonClicked()
|
signal nextButtonClicked()
|
||||||
|
|
||||||
|
onEstimatedTxTimeFlagChanged: {
|
||||||
|
estimatedTime.text = Utils.getLabelForEstimatedTxTime(estimatedTxTimeFlag)
|
||||||
|
}
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 82
|
height: 82
|
||||||
radius: 8
|
radius: 8
|
||||||
|
@ -55,9 +58,9 @@ Rectangle {
|
||||||
}
|
}
|
||||||
// To-do not implemented yet
|
// To-do not implemented yet
|
||||||
StatusBaseText {
|
StatusBaseText {
|
||||||
|
id: estimatedTime
|
||||||
font.pixelSize: 15
|
font.pixelSize: 15
|
||||||
color: Theme.palette.directColor1
|
color: Theme.palette.directColor1
|
||||||
text: estimatedTime
|
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,6 +82,7 @@ Rectangle {
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
}
|
}
|
||||||
StatusBaseText {
|
StatusBaseText {
|
||||||
|
id: fiatFees
|
||||||
font.pixelSize: 15
|
font.pixelSize: 15
|
||||||
color: Theme.palette.directColor1
|
color: Theme.palette.directColor1
|
||||||
text: maxFiatFees
|
text: maxFiatFees
|
||||||
|
|
|
@ -189,6 +189,13 @@ QtObject {
|
||||||
readonly property int success: 1
|
readonly property int success: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readonly property QtObject transactionEstimatedTime: QtObject {
|
||||||
|
readonly property int unknown: -1
|
||||||
|
readonly property int lessThanOneMin: 0
|
||||||
|
readonly property int lessThanThreeMins: 1
|
||||||
|
readonly property int lessThanFiveMins: 2
|
||||||
|
}
|
||||||
|
|
||||||
readonly property int communityImported: 0
|
readonly property int communityImported: 0
|
||||||
readonly property int communityImportingInProgress: 1
|
readonly property int communityImportingInProgress: 1
|
||||||
readonly property int communityImportingError: 2
|
readonly property int communityImportingError: 2
|
||||||
|
|
|
@ -618,6 +618,19 @@ QtObject {
|
||||||
|
|
||||||
/* Validation section end */
|
/* Validation section end */
|
||||||
|
|
||||||
|
function getLabelForEstimatedTxTime(estimatedFlag) {
|
||||||
|
if (estimatedFlag === Constants.transactionEstimatedTime.lessThanOneMin) {
|
||||||
|
return qsTr("< 1 min")
|
||||||
|
}
|
||||||
|
if (estimatedFlag === Constants.transactionEstimatedTime.lessThanThreeMins) {
|
||||||
|
return qsTr("< 3 mins")
|
||||||
|
}
|
||||||
|
if (estimatedFlag === Constants.transactionEstimatedTime.lessThanFiveMins) {
|
||||||
|
return qsTr("< 5 mins")
|
||||||
|
}
|
||||||
|
|
||||||
|
return qsTr("> 5 mins")
|
||||||
|
}
|
||||||
|
|
||||||
function getContactDetailsAsJson(publicKey) {
|
function getContactDetailsAsJson(publicKey) {
|
||||||
let jsonObj = mainModule.getContactDetailsAsJson(publicKey)
|
let jsonObj = mainModule.getContactDetailsAsJson(publicKey)
|
||||||
|
|
Loading…
Reference in New Issue