fix: fill missing fees in tx object received from dApp

Fixes #16528
This commit is contained in:
Sale Djenic 2024-10-30 22:37:42 +01:00 committed by saledjenic
parent 623333ab8c
commit e3128587d8
8 changed files with 274 additions and 32 deletions

View File

@ -26,7 +26,6 @@ proc convertFeesInfoToHex*(feesInfoJson: string): string =
parsedJson = parseJson(feesInfoJson)
maxFeePerGasFloat = getFloatFromJson(parsedJson, "maxFeePerGas")
a = maxFeePerGasFloat * 1e9
maxFeePerGasWei = uint64(maxFeePerGasFloat * 1e9)
maxPriorityFeePerGasFloat = getFloatFromJson(parsedJson, "maxPriorityFeePerGas")

View File

@ -74,6 +74,8 @@ DappsComboBox {
signal signRequestAccepted(string connectionId, string requestId)
signal signRequestRejected(string connectionId, string requestId)
signal subscribeForFeeUpdates(string connectionId, string requestId)
/// Response to pairingValidationRequested
function pairingValidated(validationState) {
if (pairWCLoader.item) {
@ -327,11 +329,27 @@ DappsComboBox {
signingTransaction: !!request.method && (request.method === SessionRequest.methods.signTransaction.name
|| request.method === SessionRequest.methods.sendTransaction.name)
requestPayload: request.preparedData
requestPayload: {
try {
const data = JSON.parse(request.preparedData)
delete data.maxFeePerGas
delete data.maxPriorityFeePerGas
delete data.gasPrice
return JSON.stringify(data, null, 2)
} catch(_) {
return request.preparedData
}
}
expirationSeconds: request.expirationTimestamp ? request.expirationTimestamp - requestTimestamp.getTime() / 1000
: 0
hasExpiryDate: !!request.expirationTimestamp
onOpened: {
root.subscribeForFeeUpdates(request.topic, request.id)
}
onClosed: {
Qt.callLater(rejectRequest)
}

View File

@ -165,6 +165,7 @@ Item {
onConnectionDeclined: (pairingId) => wcService.rejectPairSession(pairingId)
onSignRequestAccepted: (connectionId, requestId) => wcService.sign(connectionId, requestId)
onSignRequestRejected: (connectionId, requestId) => wcService.rejectSign(connectionId, requestId, false /*hasError*/)
onSubscribeForFeeUpdates: (connectionId, requestId) => wcService.subscribeForFeeUpdates(connectionId, requestId)
Connections {
target: dappsWorkflow.wcService

View File

@ -24,11 +24,17 @@ SQUtils.QObject {
property alias requestsModel: requests
function rejectSessionRequest(topic, id, hasError) {
d.unsubscribeForFeeUpdates(topic, id)
sdk.rejectSessionRequest(topic, id, hasError)
}
function subscribeForFeeUpdates(topic, id) {
d.subscribeForFeeUpdates(topic, id)
}
/// Beware, it will fail if called multiple times before getting an answer
function authenticate(topic, id, address, payload) {
d.unsubscribeForFeeUpdates(topic, id)
return store.authenticateUser(topic, id, address, payload)
}
@ -158,6 +164,108 @@ SQUtils.QObject {
SQUtils.QObject {
id: d
property int selectedFeesMode: Constants.FeesMode.Medium
function getFeesForFeesMode(feesObj) {
if (!(feesObj.hasOwnProperty("maxFeePerGasL") &&
feesObj.hasOwnProperty("maxFeePerGasM") &&
feesObj.hasOwnProperty("maxFeePerGasH"))) {
throw new Error("inappropriate fees object provided")
}
switch (d.selectedFeesMode) {
case Constants.FeesMode.Low:
return feesObj.maxFeePerGasL
case Constants.FeesMode.Medium:
return feesObj.maxFeePerGasM
case Constants.FeesMode.High:
return feesObj.maxFeePerGasH
default:
throw new Error("unknown selected mode")
}
}
property var feesSubscriptions: []
function findSubscriptionIndex(topic, id) {
for (let i = 0; i < d.feesSubscriptions.length; i++) {
const subscription = d.feesSubscriptions[i]
if (subscription.topic == topic && subscription.id == id) {
return i
}
}
return -1
}
function findChainIndex(chainId) {
for (let i = 0; i < feesSubscription.chainIds.length; i++) {
if (feesSubscription.chainIds[i] == chainId) {
return i
}
}
return -1
}
function subscribeForFeeUpdates(topic, id) {
const request = requests.findRequest(topic, id)
if (request === null) {
console.error("Error finding event for subscribing for fees for topic", topic, "id", id)
return
}
const index = d.findSubscriptionIndex(topic, id)
if (index >= 0) {
return
}
d.feesSubscriptions.push({
topic: topic,
id: id,
chainId: request.chainId
})
for (let i = 0; i < feesSubscription.chainIds.length; i++) {
if (feesSubscription.chainIds == request.chainId) {
return
}
}
feesSubscription.chainIds.push(request.chainId)
feesSubscription.restart()
}
function unsubscribeForFeeUpdates(topic, id) {
const index = d.findSubscriptionIndex(topic, id)
if (index == -1) {
return
}
const chainId = d.feesSubscriptions[index].chainId
d.feesSubscriptions.splice(index, 1)
const chainIndex = d.findChainIndex(chainId)
if (index == -1) {
return
}
let found = false
for (let i = 0; i < d.feesSubscriptions.length; i++) {
if (d.feesSubscriptions[i].chainId == chainId) {
found = true
break
}
}
if (found) {
return
}
feesSubscription.chainIds.splice(chainIndex, 1)
if (feesSubscription.chainIds.length == 0) {
feesSubscription.stop()
}
}
readonly property QtObject resolveAsyncResult: QtObject {
readonly property int error: 0
readonly property int ok: 1
@ -193,7 +301,7 @@ SQUtils.QObject {
return { obj: null, code: resolveAsyncResult.error }
}
const interpreted = d.prepareData(method, data)
const interpreted = d.prepareData(method, data, chainId)
const enoughFunds = !d.isTransactionMethod(method)
const requestExpiry = event.params.request.expiryTimestamp
@ -232,11 +340,21 @@ SQUtils.QObject {
obj.resolveDappInfoFromSession(session)
root.sessionRequest(obj.id)
if (!d.isTransactionMethod(method)) {
d.updateFeesParamsToPassedObj(obj)
})
return {
obj: obj,
code: resolveAsyncResult.ok
}
}
function updateFeesParamsToPassedObj(obj) {
if (!d.isTransactionMethod(obj.method)) {
return
}
obj.estimatedTimeCategory = getEstimatedTimeInterval(data, method, obj.chainId)
obj.estimatedTimeCategory = getEstimatedTimeInterval(obj.data, obj.method, obj.chainId)
const mainNet = lookupMainnetNetwork()
let mainChainId = obj.chainId
@ -246,19 +364,15 @@ SQUtils.QObject {
console.error("Error finding mainnet network")
}
let st = getEstimatedFeesStatus(data, method, obj.chainId, mainChainId)
const interpreted = d.prepareData(obj.method, obj.data, obj.chainId)
let st = getEstimatedFeesStatus(obj.data, obj.method, obj.chainId, mainChainId)
let fundsStatus = checkFundsStatus(st.feesInfo.maxFees, st.feesInfo.l1GasFee, obj.accountAddress, obj.chainId, mainNet.chainId, interpreted.value)
obj.fiatMaxFees = st.fiatMaxFees
obj.ethMaxFees = st.maxFeesEth
obj.haveEnoughFunds = fundsStatus.haveEnoughFunds
obj.haveEnoughFees = fundsStatus.haveEnoughForFees
obj.feesInfo = st.feesInfo
})
return {
obj: obj,
code: resolveAsyncResult.ok
}
}
/// returns {
@ -628,7 +742,7 @@ SQUtils.QObject {
// preparedData,
// value // null or ETH Big number
// }
function prepareData(method, data) {
function prepareData(method, data, chainId) {
let payload = null
switch(method) {
case SessionRequest.methods.personalSign.name: {
@ -662,22 +776,44 @@ SQUtils.QObject {
if (d.isTransactionMethod(method)) {
let txObj = d.getTxObject(method, data)
let tx = Object.assign({}, txObj)
let fees = root.store.getSuggestedFees(chainId)
if (tx.value) {
value = hexToEth(tx.value)
tx.value = value.toString()
}
if (tx.hasOwnProperty("maxFeePerGas")) {
if (tx.maxFeePerGas) {
tx.maxFeePerGas = hexToGwei(tx.maxFeePerGas).toString()
} else if (fees.eip1559Enabled) {
try {
tx.maxFeePerGas = d.getFeesForFeesMode(fees)
} catch (e) {
console.warn(e)
}
}
}
if (tx.hasOwnProperty("maxPriorityFeePerGas")) {
if (tx.maxPriorityFeePerGas) {
tx.maxPriorityFeePerGas = hexToGwei(tx.maxPriorityFeePerGas).toString()
} else if (fees.eip1559Enabled) {
tx.maxPriorityFeePerGas = fees.maxPriorityFeePerGas
}
}
if (tx.hasOwnProperty("gasPrice")) {
if (tx.gasPrice) {
tx.gasPrice = hexToGwei(tx.gasPrice)
} else if (!fees.eip1559Enabled) {
tx.gasPrice = fees.gasPrice
}
}
if (tx.gasLimit) {
tx.gasLimit = parseInt(root.store.hexToDec(tx.gasLimit))
}
if (tx.nonce) {
tx.nonce = parseInt(root.store.hexToDec(tx.nonce))
}
@ -723,4 +859,30 @@ SQUtils.QObject {
sourceId: Constants.DAppConnectors.WalletConnect
}
}
Timer {
id: feesSubscription
property var chainIds: []
interval: 5000
repeat: true
running: Qt.application.state === Qt.ApplicationActive
onTriggered: {
for (let i = 0; i < chainIds.length; i++) {
for (let j = 0; j < d.feesSubscriptions.length; j++) {
let subscription = d.feesSubscriptions[j]
if (subscription.chainId == chainIds[i]) {
let request = requests.findRequest(subscription.topic, subscription.id)
if (request === null) {
console.error("Error updating fees for topic", subscription.topic, "id", subscription.id)
continue
}
d.updateFeesParamsToPassedObj(request)
}
}
}
}
}
}

View File

@ -63,6 +63,10 @@ QObject {
requestHandler.rejectSessionRequest(topic, id, hasError)
}
function subscribeForFeeUpdates(topic, id) {
requestHandler.subscribeForFeeUpdates(topic, id)
}
/// Validates the pairing URI
function validatePairingUri(uri) {
d.validatePairingUri(uri)

View File

@ -11,6 +11,7 @@ import AppLayouts.Wallet.popups 1.0
import AppLayouts.Wallet.panels 1.0
import shared.popups.walletconnect.panels 1.0
import shared.panels 1.0
import utils 1.0
@ -93,6 +94,7 @@ SignTransactionModalBase {
font.pixelSize: Theme.additionalTextSize
}
StatusTextWithLoadingState {
id: maxFees
Layout.fillWidth: true
objectName: "footerFiatFeesText"
text: formatBigNumber(root.fiatFees, root.currentCurrency)
@ -103,6 +105,18 @@ SignTransactionModalBase {
when: !root.hasFees
value: qsTr("No fees")
}
onTextChanged: {
if (text === "") {
return
}
maxFeesAnimation.restart()
}
AnimatedText {
id: maxFeesAnimation
target: maxFees
}
}
}
ColumnLayout {
@ -114,9 +128,22 @@ SignTransactionModalBase {
font.pixelSize: Theme.additionalTextSize
}
StatusTextWithLoadingState {
id: estimatedTime
objectName: "footerEstimatedTime"
text: root.estimatedTime
loading: root.feesLoading
onTextChanged: {
if (text === "") {
return
}
estimatedTimeAnimation.restart()
}
AnimatedText {
id: estimatedTimeAnimation
target: estimatedTime
}
}
}
}
@ -169,6 +196,7 @@ SignTransactionModalBase {
ColumnLayout {
spacing: 2
StatusTextWithLoadingState {
id: fiatFees
objectName: "fiatFeesText"
Layout.alignment: Qt.AlignRight
text: formatBigNumber(root.fiatFees, root.currentCurrency)
@ -176,8 +204,21 @@ SignTransactionModalBase {
font.pixelSize: Theme.additionalTextSize
loading: root.feesLoading
customColor: root.enoughFundsForFees ? Theme.palette.directColor1 : Theme.palette.dangerColor1
onTextChanged: {
if (text === "") {
return
}
fiatFeesAnimation.restart()
}
AnimatedText {
id: fiatFeesAnimation
target: fiatFees
}
}
StatusTextWithLoadingState {
id: cryptoFees
objectName: "cryptoFeesText"
Layout.alignment: Qt.AlignRight
text: formatBigNumber(root.cryptoFees, Constants.ethToken)
@ -185,6 +226,18 @@ SignTransactionModalBase {
font.pixelSize: Theme.additionalTextSize
customColor: root.enoughFundsForFees ? Theme.palette.baseColor1 : Theme.palette.dangerColor1
loading: root.feesLoading
onTextChanged: {
if (text === "") {
return
}
cryptoFeesAnimation.restart()
}
AnimatedText {
id: cryptoFeesAnimation
target: cryptoFees
}
}
}
]

View File

@ -33,7 +33,6 @@ Rectangle {
anchors.fill: parent
anchors.margins: 20
text: root.payloadToDisplay
font.pixelSize: Theme.additionalTextSize
lineHeightMode: Text.FixedHeight
lineHeight: 18

View File

@ -1098,6 +1098,12 @@ QtObject {
}
}
enum FeesMode {
Low,
Medium,
High
}
enum LoginType {
Password,
Biometrics,