This commit is contained in:
Richard Ramos 2021-07-05 08:34:56 -04:00 committed by Iuri Matias
parent daa0865885
commit 285f54dab6
25 changed files with 830 additions and 141 deletions

View File

@ -118,15 +118,17 @@ QtObject:
let estimateResult = Json.decode(estimateJson, tuple[estimate: int, uuid: string]) let estimateResult = Json.decode(estimateJson, tuple[estimate: int, uuid: string])
self.gasEstimateReturned(estimateResult.estimate, estimateResult.uuid) self.gasEstimateReturned(estimateResult.estimate, estimateResult.uuid)
proc buy*(self: StickersView, packId: int, address: string, price: string, gas: string, gasPrice: string, password: string): string {.slot.} = proc buy*(self: StickersView, packId: int, address: string, price: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string, maxFeePerGas: string, password: string): string {.slot.} =
let eip1559Enabled = self.status.wallet.isEIP1559Enabled()
try: try:
validateTransactionInput(address, address, "", price, gas, gasPrice, "", "ok") validateTransactionInput(address, address, "", price, gas, gasPrice, "", eip1559Enabled, maxPriorityFeePerGas, maxFeePerGas, "ok")
except Exception as e: except Exception as e:
error "Error buying sticker pack", msg = e.msg error "Error buying sticker pack", msg = e.msg
return "" return ""
var success: bool var success: bool
let response = self.status.stickers.buyPack(packId, address, price, gas, gasPrice, password, success) let response = self.status.stickers.buyPack(packId, address, price, gas, gasPrice, eip1559Enabled, maxPriorityFeePerGas, maxFeePerGas, password, success)
# TODO: # TODO:
# check if response["error"] is not null and handle the error # check if response["error"] is not null and handle the error

View File

@ -243,10 +243,11 @@ QtObject:
if not success: if not success:
result = 380000 result = 380000
proc registerENS*(self: EnsManager, username: string, address: string, gas: string, gasPrice: string, password: string): string {.slot.} = proc registerENS*(self: EnsManager, username: string, address: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string, maxFeePerGas: string, password: string): string {.slot.} =
let eip1559Enabled = self.status.wallet.isEIP1559Enabled()
var success: bool var success: bool
let pubKey = self.status.settings.getSetting[:string](Setting.PublicKey, "0x0") let pubKey = self.status.settings.getSetting[:string](Setting.PublicKey, "0x0")
let response = registerUsername(username, pubKey, address, gas, gasPrice, password, success) let response = registerUsername(username, pubKey, address, gas, gasPrice, eip1559Enabled, maxPriorityFeePerGas, maxFeePerGas, password, success)
result = $(%* { "result": %response, "success": %success }) result = $(%* { "result": %response, "success": %success })
if success: if success:
@ -282,10 +283,11 @@ QtObject:
if not success: if not success:
result = 80000 result = 80000
proc setPubKey(self: EnsManager, username: string, address: string, gas: string, gasPrice: string, password: string): string {.slot.} = proc setPubKey*(self: EnsManager, username: string, address: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string, maxFeePerGas: string, password: string): string {.slot.} =
let eip1559Enabled = self.status.wallet.isEIP1559Enabled()
var success: bool var success: bool
let pubKey = self.status.settings.getSetting[:string](Setting.PublicKey, "0x0") let pubKey = self.status.settings.getSetting[:string](Setting.PublicKey, "0x0")
let response = setPubKey(username, pubKey, address, gas, gasPrice, password, success) let response = setPubKey(username, pubKey, address, gas, gasPrice, eip1559Enabled, maxPriorityFeePerGas, maxFeePerGas, password, success)
result = $(%* { "result": %response, "success": %success }) result = $(%* { "result": %response, "success": %success })
if success: if success:
self.transactionWasSent(response) self.transactionWasSent(response)

View File

@ -60,6 +60,8 @@ proc init*(self: WalletController) =
for acc in data.accounts: for acc in data.accounts:
self.status.wallet.updateAccount(acc) self.status.wallet.updateAccount(acc)
self.status.wallet.checkPendingTransactions(acc, data.blockNumber) self.status.wallet.checkPendingTransactions(acc, data.blockNumber)
discard self.status.wallet.isEIP1559Enabled(data.blockNumber)
self.status.wallet.setLatestBaseFee(data.baseFeePerGas)
self.view.updateView() self.view.updateView()
# TODO: show notification # TODO: show notification
@ -85,5 +87,9 @@ proc init*(self: WalletController) =
let tx = TransactionMinedArgs(e) let tx = TransactionMinedArgs(e)
self.view.transactionCompleted(tx.success, tx.transactionHash, tx.revertReason) self.view.transactionCompleted(tx.success, tx.transactionHash, tx.revertReason)
proc checkPendingTransactions*(self: WalletController) = proc onLogin*(self: WalletController) =
self.status.wallet.checkPendingTransactions() # TODO: consider doing this in a threadpool task let blockInfo = getLatestBlock()
self.status.wallet.checkPendingTransactions(blockInfo[0]) # TODO: consider doing this in a threadpool task
discard self.status.wallet.isEIP1559Enabled(blockInfo[0])
self.status.wallet.setLatestBaseFee(blockInfo[1])

View File

@ -99,6 +99,8 @@ QtObject:
read = getDappBrowserView read = getDappBrowserView
proc updateView*(self: WalletView) = proc updateView*(self: WalletView) =
self.transactionsView.triggerEIP1559Check()
self.balanceView.setTotalFiatBalance(self.status.wallet.getTotalFiatBalance()) self.balanceView.setTotalFiatBalance(self.status.wallet.getTotalFiatBalance())
self.balanceView.totalFiatBalanceChanged() self.balanceView.totalFiatBalanceChanged()

View File

@ -1,9 +1,10 @@
import atomics, strformat, strutils, sequtils, json, std/wrapnils, parseUtils, chronicles, web3/[ethtypes, conversions], stint import atomics, strformat, strutils, sequtils, json, std/wrapnils, parseUtils, chronicles, web3/[ethtypes, conversions], stint
import NimQml, json, sequtils, chronicles, strutils, strformat, json import NimQml, json, sequtils, chronicles, strutils, strformat, json, math
import import
status/[status, wallet, utils], status/[status, wallet, utils],
status/types/[gas_prediction] status/types/[gas_prediction],
status/libstatus/wallet as status_wallet
import ../../../../app_service/[main] import ../../../../app_service/[main]
import ../../../../app_service/tasks/[qt, threadpool] import ../../../../app_service/tasks/[qt, threadpool]
@ -17,7 +18,10 @@ type
const getGasPredictionsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} = const getGasPredictionsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let let
arg = decode[GasPredictionsTaskArg](argEncoded) arg = decode[GasPredictionsTaskArg](argEncoded)
output = %getGasPricePredictions() response = status_wallet.getGasPrice().parseJson
var output = "0"
if response.hasKey("result"):
output = $fromHex(Stuint[256], response["result"].getStr)
arg.finish(output) arg.finish(output)
proc getGasPredictions[T](self: T, slot: string) = proc getGasPredictions[T](self: T, slot: string) =
@ -35,10 +39,7 @@ QtObject:
type GasView* = ref object of QObject type GasView* = ref object of QObject
status: Status status: Status
appService: AppService appService: AppService
safeLowGasPrice: string gasPrice: string
standardGasPrice: string
fastGasPrice: string
fastestGasPrice: string
defaultGasLimit: string defaultGasLimit: string
proc setup(self: GasView) = self.QObject.setup proc setup(self: GasView) = self.QObject.setup
@ -48,10 +49,7 @@ QtObject:
new(result, delete) new(result, delete)
result.status = status result.status = status
result.appService = appService result.appService = appService
result.safeLowGasPrice = "0" result.gasPrice = "0"
result.standardGasPrice = "0"
result.fastGasPrice = "0"
result.fastestGasPrice = "0"
result.defaultGasLimit = "21000" result.defaultGasLimit = "21000"
result.setup result.setup
@ -89,39 +87,69 @@ QtObject:
else: else:
result = $(%* { "result": "-1", "success": %success, "error": { "message": %response } }) result = $(%* { "result": "-1", "success": %success, "error": { "message": %response } })
proc gasPricePredictionsChanged*(self: GasView) {.signal.} proc gasPriceChanged*(self: GasView) {.signal.}
proc getGasPricePredictions*(self: GasView) {.slot.} = proc getGasPrice*(self: GasView) {.slot.} =
self.getGasPredictions("getGasPricePredictionsResult") if not self.status.wallet.isEIP1559Enabled():
self.getGasPredictions("getGasPriceResult")
proc getGasPricePredictionsResult(self: GasView, gasPricePredictionsJson: string) {.slot.} = proc getGasPriceResult(self: GasView, gasPrice: string) {.slot.} =
let prediction = Json.decode(gasPricePredictionsJson, GasPricePrediction) let p = parseFloat(wei2gwei(gasPrice))
self.safeLowGasPrice = fmt"{prediction.safeLow:.3f}" self.gasPrice = fmt"{p:.3f}"
self.standardGasPrice = fmt"{prediction.standard:.3f}" self.gasPriceChanged()
self.fastGasPrice = fmt"{prediction.fast:.3f}"
self.fastestGasPrice = fmt"{prediction.fastest:.3f}"
self.gasPricePredictionsChanged()
proc safeLowGasPrice*(self: GasView): string {.slot.} = result = ?.self.safeLowGasPrice proc gasPrice*(self: GasView): string {.slot.} = result = ?.self.gasPrice
QtProperty[string] safeLowGasPrice: QtProperty[string] gasPrice:
read = safeLowGasPrice read = gasPrice
notify = gasPricePredictionsChanged notify = gasPriceChanged
proc standardGasPrice*(self: GasView): string {.slot.} = result = ?.self.standardGasPrice
QtProperty[string] standardGasPrice:
read = standardGasPrice
notify = gasPricePredictionsChanged
proc fastGasPrice*(self: GasView): string {.slot.} = result = ?.self.fastGasPrice
QtProperty[string] fastGasPrice:
read = fastGasPrice
notify = gasPricePredictionsChanged
proc fastestGasPrice*(self: GasView): string {.slot.} = result = ?.self.fastestGasPrice
QtProperty[string] fastestGasPrice:
read = fastestGasPrice
notify = gasPricePredictionsChanged
proc defaultGasLimit*(self: GasView): string {.slot.} = result = ?.self.defaultGasLimit proc defaultGasLimit*(self: GasView): string {.slot.} = result = ?.self.defaultGasLimit
QtProperty[string] defaultGasLimit: QtProperty[string] defaultGasLimit:
read = defaultGasLimit read = defaultGasLimit
proc maxPriorityFeePerGas*(self: GasView): string {.slot.} =
result = self.status.wallet.maxPriorityFeePerGas()
debug "Max priority fee per gas", value=result
proc suggestedFees*(self: GasView): string {.slot.} =
#[
0. priority tip always same, the value returned by eth_maxPriorityFeePerGas
1. slow fee 10th percentile base fee (last 100 blocks) + eth_maxPriorityFeePerGas
2. normal fee.
if 20th_percentile <= current_base_fee <= 80th_percentile then fee = current_base_fee + eth_maxPriorityFeePerGas.
if current_base_fee < 20th_percentile then fee = 20th_percentile + eth_maxPriorityFeePerGas
if current_base_fee > 80th_percentile then fee = 80th_percentile + eth_maxPriorityFeePerGas
The idea is to avoid setting too low base fee when price is in a dip and also to avoid overpaying on peak. Specific percentiles can be revisit later, it doesn't need to be symmetric because we are mostly interested in not getting stuck and overpaying might not be a huge issue here.
3. fast fee: current_base_fee + eth_maxPriorityFeePerGas
]#
let maxPriorityFeePerGas = self.status.wallet.maxPriorityFeePerGas().u256
let feeHistory = self.status.wallet.feeHistory(101)
let baseFee = self.status.wallet.getLatestBaseFee().u256
let gasPrice = self.status.wallet.getGasPrice().u256
let perc10 = feeHistory[ceil(10/100 * feeHistory.len.float).int - 1]
let perc20 = feeHistory[ceil(20/100 * feeHistory.len.float).int - 1]
let perc80 = feeHistory[ceil(80/100 * feeHistory.len.float).int - 1]
let maxFeePerGasM = if baseFee >= perc20 and baseFee <= perc80:
baseFee + maxPriorityFeePerGas
elif baseFee < perc20:
perc20 + maxPriorityFeePerGas
else:
perc80 + maxPriorityFeePerGas
result = $(%* {
"gasPrice": $gasPrice,
"baseFee": parseFloat(wei2gwei($baseFee)),
"maxPriorityFeePerGas": parseFloat(wei2gwei($maxPriorityFeePerGas)),
"maxFeePerGasL": parseFloat(wei2gwei($(perc10 + maxPriorityFeePerGas))),
"maxFeePerGasM": parseFloat(wei2gwei($(maxFeePerGasM))),
"maxFeePerGasH": parseFloat(wei2gwei($(baseFee + maxPriorityFeePerGas)))
})
QtProperty[string] maxPriorityFeePerGas:
read = maxPriorityFeePerGas
QtProperty[string] suggestedFees:
read = suggestedFees

View File

@ -24,6 +24,9 @@ type
value: string value: string
gas: string gas: string
gasPrice: string gasPrice: string
isEIP1559Enabled: bool
maxPriorityFeePerGas: string
maxFeePerGas: string
password: string password: string
uuid: string uuid: string
WatchTransactionTaskArg = ref object of QObjectTaskArg WatchTransactionTaskArg = ref object of QObjectTaskArg
@ -35,19 +38,20 @@ const sendTransactionTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
success: bool success: bool
response: string response: string
if arg.assetAddress != ZERO_ADDRESS and not arg.assetAddress.isEmptyOrWhitespace: if arg.assetAddress != ZERO_ADDRESS and not arg.assetAddress.isEmptyOrWhitespace:
response = wallet.sendTokenTransaction(arg.from_addr, arg.to, arg.assetAddress, arg.value, arg.gas, arg.gasPrice, arg.password, success) response = wallet.sendTokenTransaction(arg.from_addr, arg.to, arg.assetAddress, arg.value, arg.gas, arg.gasPrice, arg.isEIP1559Enabled, arg.maxPriorityFeePerGas, arg.maxFeePerGas, arg.password, success)
else: else:
response = wallet.sendTransaction(arg.from_addr, arg.to, arg.value, arg.gas, arg.gasPrice, arg.password, success) response = wallet.sendTransaction(arg.from_addr, arg.to, arg.value, arg.gas, arg.gasPrice, arg.isEIP1559Enabled, arg.maxPriorityFeePerGas, arg.maxFeePerGas, arg.password, success)
let output = %* { "result": %response, "success": %success, "uuid": %arg.uuid } let output = %* { "result": %response, "success": %success, "uuid": %arg.uuid }
arg.finish(output) arg.finish(output)
proc sendTransaction[T](self: T, slot: string, from_addr: string, to: string, assetAddress: string, value: string, gas: string, gasPrice: string, password: string, uuid: string) = proc sendTransaction[T](self: T, slot: string, from_addr: string, to: string, assetAddress: string, value: string, gas: string, gasPrice: string, isEIP1559Enabled: bool, maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, uuid: string) =
let arg = SendTransactionTaskArg( let arg = SendTransactionTaskArg(
tptr: cast[ByteAddress](sendTransactionTask), tptr: cast[ByteAddress](sendTransactionTask),
vptr: cast[ByteAddress](self.vptr), vptr: cast[ByteAddress](self.vptr),
slot: slot, from_addr: from_addr, to: to, slot: slot, from_addr: from_addr, to: to,
assetAddress: assetAddress, value: value, gas: gas, assetAddress: assetAddress, value: value, gas: gas,
gasPrice: gasPrice, password: password, uuid: uuid gasPrice: gasPrice, password: password, uuid: uuid,
isEIP1559Enabled: isEIP1559Enabled, maxPriorityFeePerGas: maxPriorityFeePerGas, maxFeePerGas: maxFeePerGas
) )
self.appService.threadpool.start(arg) self.appService.threadpool.start(arg)
@ -110,23 +114,24 @@ QtObject:
if txHash != "": if txHash != "":
self.watchTransaction("transactionWatchResultReceived", txHash) self.watchTransaction("transactionWatchResultReceived", txHash)
proc sendTransaction*(self: TransactionsView, from_addr: string, to: string, assetAddress: string, value: string, gas: string, gasPrice: string, password: string, uuid: string) {.slot.} = proc sendTransaction*(self: TransactionsView, from_addr: string, to: string, assetAddress: string, value: string, gas: string, gasPrice: string,eip1559Enabled: bool, maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, uuid: string) {.slot.} =
self.sendTransaction("transactionSent", from_addr, to, assetAddress, value, gas, gasPrice, password, uuid) self.sendTransaction("transactionSent", from_addr, to, assetAddress, value, gas, gasPrice, eip1559Enabled, maxPriorityFeePerGas, maxFeePerGas, password, uuid)
proc transferEth*(self: TransactionsView, from_addr: string, to_addr: string, value: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, uuid: string): bool {.slot.} =
proc transferEth*(self: TransactionsView, from_addr: string, to_addr: string, value: string, gas: string, gasPrice: string, password: string, uuid: string): bool {.slot.} =
try: try:
validateTransactionInput(from_addr, to_addr, "", value, gas, gasPrice, "", uuid) let eip1559Enabled = self.status.wallet.isEIP1559Enabled()
self.sendTransaction("transactionSent", from_addr, to_addr, ZERO_ADDRESS, value, gas, gasPrice, password, uuid) validateTransactionInput(from_addr, to_addr, "", value, gas, gasPrice, "", eip1559Enabled, maxPriorityFeePerGas, maxFeePerGas, uuid)
self.sendTransaction("transactionSent", from_addr, to_addr, ZERO_ADDRESS, value, gas, gasPrice, eip1559Enabled, maxPriorityFeePerGas, maxFeePerGas, password, uuid)
except Exception as e: except Exception as e:
error "Error sending eth transfer transaction", msg = e.msg error "Error sending eth transfer transaction", msg = e.msg
return false return false
return true return true
proc transferTokens*(self: TransactionsView, from_addr: string, to_addr: string, assetAddress: string, value: string, gas: string, gasPrice: string, password: string, uuid: string): bool {.slot.} = proc transferTokens*(self: TransactionsView, from_addr: string, to_addr: string, assetAddress: string, value: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, uuid: string): bool {.slot.} =
try: try:
validateTransactionInput(from_addr, to_addr, assetAddress, value, gas, gasPrice, "", uuid) let eip1559Enabled = self.status.wallet.isEIP1559Enabled()
self.sendTransaction("transactionSent", from_addr, to_addr, assetAddress, value, gas, gasPrice, password, uuid) validateTransactionInput(from_addr, to_addr, assetAddress, value, gas, gasPrice, "", eip1559Enabled, maxPriorityFeePerGas, maxFeePerGas, uuid)
self.sendTransaction("transactionSent", from_addr, to_addr, assetAddress, value, gas, gasPrice, eip1559Enabled, maxPriorityFeePerGas, maxFeePerGas, password, uuid)
except Exception as e: except Exception as e:
error "Error sending token transfer transaction", msg = e.msg error "Error sending token transfer transaction", msg = e.msg
return false return false
@ -146,3 +151,26 @@ QtObject:
discard #TODO: Ask Simon if should we show an error popup indicating the trx wasn't mined in 10m or something discard #TODO: Ask Simon if should we show an error popup indicating the trx wasn't mined in 10m or something
proc transactionCompleted*(self: TransactionsView, success: bool, txHash: string, revertReason: string = "") {.signal.} proc transactionCompleted*(self: TransactionsView, success: bool, txHash: string, revertReason: string = "") {.signal.}
proc triggerEIP1559Check*(self: TransactionsView) {.signal.}
proc isEIP1559Enabled(self: TransactionsView): bool {.slot.} =
return self.status.wallet.isEIP1559Enabled()
proc getLatestBaseFee(self: TransactionsView): string {.slot.} =
var baseFeeWei:string = self.status.wallet.getLatestBaseFee()
var baseFeeGwei:string = wei2Gwei(baseFeeWei)
var unit:string = "wei"
var amount = baseFeeWei
if parseFloat(baseFeeGwei) > 1:
unit = "gwei"
amount = baseFeeGwei
return $(%*{"gwei": baseFeeGwei, "amount": amount, "unit": unit})
QtProperty[bool] isEIP1559Enabled:
read = isEIP1559Enabled
notify = triggerEIP1559Check
QtProperty[string] latestBaseFee:
read = getLatestBaseFee
notify = triggerEIP1559Check

View File

@ -198,7 +198,8 @@ proc mainProc() =
utilsController.init() utilsController.init()
browserController.init() browserController.init()
node.init() node.init()
wallet.checkPendingTransactions()
wallet.onLogin()
# this should be the last defer in the scope # this should be the last defer in the scope
defer: defer:

View File

@ -77,7 +77,7 @@ Rectangle {
} }
// TODO we'll need a new dialog at one point because this one is not using the same call, but it's good for now // TODO we'll need a new dialog at one point because this one is not using the same call, but it's good for now
property Component sendTransactionModalComponent: SignTransactionModal {} property Component sendTransactionModalComponent: SignTransactionModal {}
property Component signMessageModalComponent: SignMessageModal {} property Component signMessageModalComponent: SignMessageModal {}
@ -190,9 +190,11 @@ property Component sendTransactionModalComponent: SignTransactionModal {}
}); });
// TODO change sendTransaction function to the postMessage one // TODO change sendTransaction function to the postMessage one
sendDialog.sendTransaction = function (selectedGasLimit, selectedGasPrice, enteredPassword) { sendDialog.sendTransaction = function (selectedGasLimit, selectedGasPrice, selectedTipLimit, selectedOverallLimit, enteredPassword) {
request.payload.selectedGasLimit = selectedGasLimit request.payload.selectedGasLimit = selectedGasLimit
request.payload.selectedGasPrice = selectedGasPrice request.payload.selectedGasPrice = selectedGasPrice
request.payload.selectedTipLimit = selectedTipLimit
request.payload.selectedOverallLimit = selectedOverallLimit
request.payload.password = enteredPassword request.payload.password = enteredPassword
request.payload.params[0].value = value request.payload.params[0].value = value
@ -229,7 +231,7 @@ property Component sendTransactionModalComponent: SignTransactionModal {}
} }
sendDialog.open(); sendDialog.open();
walletModel.gasView.getGasPricePredictions() walletModel.gasView.getGasPrice()
} else if (request.type === Constants.web3SendAsyncReadOnly && ["eth_sign", "personal_sign", "eth_signTypedData", "eth_signTypedData_v3"].indexOf(request.payload.method) > -1) { } else if (request.type === Constants.web3SendAsyncReadOnly && ["eth_sign", "personal_sign", "eth_signTypedData", "eth_signTypedData_v3"].indexOf(request.payload.method) > -1) {
const signDialog = signMessageModalComponent.createObject(browserWindow, { const signDialog = signMessageModalComponent.createObject(browserWindow, {
request, request,
@ -516,6 +518,7 @@ property Component sendTransactionModalComponent: SignTransactionModal {}
anchors.top: parent.top anchors.top: parent.top
anchors.topMargin: browserHeader.height anchors.topMargin: browserHeader.height
focus: true focus: true
url: "https://dap.ps"
webChannel: channel webChannel: channel
onLinkHovered: function(hoveredUrl) { onLinkHovered: function(hoveredUrl) {
if (hoveredUrl === "") if (hoveredUrl === "")

View File

@ -498,7 +498,7 @@ Item {
SendModal { SendModal {
id: sendTransactionWithEns id: sendTransactionWithEns
onOpened: { onOpened: {
walletModel.gasView.getGasPricePredictions() walletModel.gasView.getGasPrice()
} }
onClosed: { onClosed: {
txModalLoader.closed() txModalLoader.closed()
@ -622,8 +622,8 @@ Item {
Connections { Connections {
target: chatsModel.stickers target: chatsModel.stickers
onTransactionWasSent: { onTransactionWasSent: {
//% "Transaction pending" //% "Transaction pending..."
toastMessage.title = qsTrId("transaction-pending") toastMessage.title = qsTr("Transaction pending...")
toastMessage.source = "../../../img/loading.svg" toastMessage.source = "../../../img/loading.svg"
toastMessage.iconColor = Style.current.primary toastMessage.iconColor = Style.current.primary
toastMessage.iconRotates = true toastMessage.iconRotates = true

View File

@ -5,6 +5,7 @@ import QtQuick.Dialogs 1.3
import "../../../../../imports" import "../../../../../imports"
import "../../../../../shared" import "../../../../../shared"
import "../../../../../shared/status" import "../../../../../shared/status"
import "../../../Wallet/"
ModalPopup { ModalPopup {
property var selectedAccount property var selectedAccount
@ -18,7 +19,7 @@ ModalPopup {
property alias transactionSigner: transactionSigner property alias transactionSigner: transactionSigner
property var sendTransaction: function(selectedGasLimit, selectedGasPrice, enteredPassword) { property var sendTransaction: function(selectedGasLimit, selectedGasPrice, selectedTipLimit, selectedOveralLimit, enteredPassword) {
let success = false let success = false
if(root.selectedAsset.address == Constants.zeroAddress){ if(root.selectedAsset.address == Constants.zeroAddress){
success = walletModel.transactionsView.transferEth( success = walletModel.transactionsView.transferEth(
@ -26,7 +27,9 @@ ModalPopup {
selectRecipient.selectedRecipient.address, selectRecipient.selectedRecipient.address,
root.selectedAmount, root.selectedAmount,
selectedGasLimit, selectedGasLimit,
selectedGasPrice, gasSelector.eip1599Enabled ? "" : gasSelector.selectedGasPrice,
gasSelector.selectedTipLimit,
gasSelector.selectedOverallLimit,
enteredPassword, enteredPassword,
stack.uuid) stack.uuid)
} else { } else {
@ -36,7 +39,9 @@ ModalPopup {
root.selectedAsset.address, root.selectedAsset.address,
root.selectedAmount, root.selectedAmount,
selectedGasLimit, selectedGasLimit,
selectedGasPrice, gasSelector.eip1599Enabled ? "" : gasSelector.selectedGasPrice,
gasSelector.selectedTipLimit,
gasSelector.selectedOverallLimit,
enteredPassword, enteredPassword,
stack.uuid) stack.uuid)
} }
@ -52,7 +57,7 @@ ModalPopup {
//% "Send" //% "Send"
title: qsTrId("command-button-send") title: qsTrId("command-button-send")
height: 504 height: 540
property MessageDialog sendingError: MessageDialog { property MessageDialog sendingError: MessageDialog {
id: sendingError id: sendingError
@ -60,7 +65,6 @@ ModalPopup {
title: qsTrId("error-sending-the-transaction") title: qsTrId("error-sending-the-transaction")
icon: StandardIcon.Critical icon: StandardIcon.Critical
standardButtons: StandardButton.Ok standardButtons: StandardButton.Ok
onAccepted: root.close()
} }
onClosed: { onClosed: {
@ -122,8 +126,7 @@ ModalPopup {
id: groupSelectGas id: groupSelectGas
//% "Network fee" //% "Network fee"
headerText: qsTrId("network-fee") headerText: qsTrId("network-fee")
//% "Preview" footerText: qsTr("Continue")
footerText: qsTrId("preview")
showNextBtn: false showNextBtn: false
onBackClicked: function() { onBackClicked: function() {
stack.pop() stack.pop()
@ -131,8 +134,7 @@ ModalPopup {
GasSelector { GasSelector {
id: gasSelector id: gasSelector
anchors.topMargin: Style.current.bigPadding anchors.topMargin: Style.current.bigPadding
slowestGasPrice: parseFloat(walletModel.gasView.safeLowGasPrice) gasPrice: parseFloat(walletModel.gasView.gasPrice)
fastestGasPrice: parseFloat(walletModel.gasView.fastestGasPrice)
getGasEthValue: walletModel.gasView.getGasEthValue getGasEthValue: walletModel.gasView.getGasEthValue
getFiatValue: walletModel.balanceView.getFiatValue getFiatValue: walletModel.balanceView.getFiatValue
defaultCurrency: walletModel.balanceView.defaultCurrency defaultCurrency: walletModel.balanceView.defaultCurrency
@ -144,6 +146,7 @@ ModalPopup {
root.selectedAsset && root.selectedAsset.address && root.selectedAsset && root.selectedAsset.address &&
root.selectedAmount)) { root.selectedAmount)) {
selectedGasLimit = 250000 selectedGasLimit = 250000
defaultGasLimit = selectedGasLimit
return return
} }
@ -164,6 +167,7 @@ ModalPopup {
return return
} }
selectedGasLimit = gasEstimate.result selectedGasLimit = gasEstimate.result
defaultGasLimit = selectedGasLimit
}) })
} }
GasValidator { GasValidator {
@ -267,6 +271,14 @@ ModalPopup {
stack.back() stack.back()
} }
} }
Component {
id: transactionSettingsConfirmationPopupComponent
TransactionSettingsConfirmationPopup {
}
}
StatusButton { StatusButton {
id: btnNext id: btnNext
anchors.right: parent.right anchors.right: parent.right
@ -279,9 +291,33 @@ ModalPopup {
if (validity.isValid && !validity.isPending) { if (validity.isValid && !validity.isPending) {
if (stack.isLastGroup) { if (stack.isLastGroup) {
return root.sendTransaction(gasSelector.selectedGasLimit, return root.sendTransaction(gasSelector.selectedGasLimit,
gasSelector.selectedGasPrice, gasSelector.eip1599Enabled ? "" : gasSelector.selectedGasPrice,
gasSelector.selectedTipLimit,
gasSelector.selectedOverallLimit,
transactionSigner.enteredPassword) transactionSigner.enteredPassword)
} }
if(gasSelector.eip1599Enabled && stack.currentGroup === groupSelectGas && gasSelector.advancedMode){
if(gasSelector.showPriceLimitWarning || gasSelector.showTipLimitWarning){
openPopup(transactionSettingsConfirmationPopupComponent, {
currentBaseFee: gasSelector.latestBaseFeeGwei,
currentMinimumTip: gasSelector.perGasTipLimitFloor,
currentAverageTip: gasSelector.perGasTipLimitAverage,
tipLimit: gasSelector.selectedTipLimit,
suggestedTipLimit: gasSelector.perGasTipLimitFloor, // TODO:
priceLimit: gasSelector.selectedOverallLimit,
suggestedPriceLimit: gasSelector.latestBaseFeeGwei + gasSelector.perGasTipLimitFloor,
showPriceLimitWarning: gasSelector.showPriceLimitWarning,
showTipLimitWarning: gasSelector.showTipLimitWarning,
onConfirm: function(){
stack.next();
}
})
return
}
}
if (typeof stack.currentGroup.onNextClicked === "function") { if (typeof stack.currentGroup.onNextClicked === "function") {
return stack.currentGroup.onNextClicked() return stack.currentGroup.onNextClicked()
} }

View File

@ -89,7 +89,7 @@ Item {
id: signTxComponent id: signTxComponent
SignTransactionModal { SignTransactionModal {
onOpened: { onOpened: {
walletModel.gasView.getGasPricePredictions() walletModel.gasView.getGasPrice()
} }
onClosed: { onClosed: {
destroy(); destroy();

View File

@ -46,7 +46,7 @@ Item {
id: signTxComponent id: signTxComponent
SignTransactionModal { SignTransactionModal {
onOpened: { onOpened: {
walletModel.gasView.getGasPricePredictions() walletModel.gasView.getGasPrice()
} }
onClosed: { onClosed: {
destroy(); destroy();

View File

@ -51,7 +51,7 @@ Item {
id: transactionDialogComponent id: transactionDialogComponent
StatusETHTransactionModal { StatusETHTransactionModal {
onOpened: { onOpened: {
walletModel.gasView.getGasPricePredictions() walletModel.gasView.getGasPrice()
} }
title: qsTr("Connect username with your pubkey") title: qsTr("Connect username with your pubkey")
onClosed: { onClosed: {

View File

@ -39,10 +39,12 @@ Item {
if (username === "" || !selectedAccount) return 380000; if (username === "" || !selectedAccount) return 380000;
return profileModel.ens.registerENSGasEstimate(username, selectedAccount.address) return profileModel.ens.registerENSGasEstimate(username, selectedAccount.address)
} }
onSendTransaction: function(selectedAddress, gasLimit, gasPrice, password) { onSendTransaction: function(selectedAddress, gasLimit, gasPrice, tipLimit, overallLimit, password) {
return profileModel.ens.registerENS(username, return profileModel.ens.registerENS(username,
selectedAddress, selectedAddress,
gasLimit, gasLimit,
tipLimit,
overallLimit,
gasPrice, gasPrice,
password) password)
} }

View File

@ -34,7 +34,9 @@ ModalPopup {
selectRecipient.selectedRecipient.address, selectRecipient.selectedRecipient.address,
txtAmount.selectedAmount, txtAmount.selectedAmount,
gasSelector.selectedGasLimit, gasSelector.selectedGasLimit,
gasSelector.selectedGasPrice, gasSelector.eip1599Enabled ? "" : gasSelector.selectedGasPrice,
gasSelector.selectedTipLimit,
gasSelector.selectedOverallLimit,
transactionSigner.enteredPassword, transactionSigner.enteredPassword,
stack.uuid) stack.uuid)
} else { } else {
@ -44,7 +46,9 @@ ModalPopup {
txtAmount.selectedAsset.address, txtAmount.selectedAsset.address,
txtAmount.selectedAmount, txtAmount.selectedAmount,
gasSelector.selectedGasLimit, gasSelector.selectedGasLimit,
gasSelector.selectedGasPrice, gasSelector.eip1599Enabled ? "" : gasSelector.selectedGasPrice,
gasSelector.selectedTipLimit,
gasSelector.selectedOverallLimit,
transactionSigner.enteredPassword, transactionSigner.enteredPassword,
stack.uuid) stack.uuid)
} }
@ -53,7 +57,6 @@ ModalPopup {
//% "Invalid transaction parameters" //% "Invalid transaction parameters"
sendingError.text = qsTrId("invalid-transaction-parameters") sendingError.text = qsTrId("invalid-transaction-parameters")
sendingError.open() sendingError.open()
root.close()
} }
} }
@ -111,7 +114,7 @@ ModalPopup {
//% "Send" //% "Send"
headerText: qsTrId("command-button-send") headerText: qsTrId("command-button-send")
//% "Preview" //% "Preview"
footerText: qsTrId("preview") footerText: qsTr("Continue")
AssetAndAmountInput { AssetAndAmountInput {
id: txtAmount id: txtAmount
@ -127,11 +130,11 @@ ModalPopup {
id: gasSelector id: gasSelector
anchors.top: txtAmount.bottom anchors.top: txtAmount.bottom
anchors.topMargin: Style.current.bigPadding * 2 anchors.topMargin: Style.current.bigPadding * 2
slowestGasPrice: parseFloat(walletModel.gasView.safeLowGasPrice) gasPrice: parseFloat(walletModel.gasView.gasPrice)
fastestGasPrice: parseFloat(walletModel.gasView.fastestGasPrice)
getGasEthValue: walletModel.gasView.getGasEthValue getGasEthValue: walletModel.gasView.getGasEthValue
getFiatValue: walletModel.balanceView.getFiatValue getFiatValue: walletModel.balanceView.getFiatValue
defaultCurrency: walletModel.balanceView.defaultCurrency defaultCurrency: walletModel.balanceView.defaultCurrency
width: stack.width width: stack.width
property var estimateGas: Backpressure.debounce(gasSelector, 600, function() { property var estimateGas: Backpressure.debounce(gasSelector, 600, function() {
if (!(selectFromAccount.selectedAccount && selectFromAccount.selectedAccount.address && if (!(selectFromAccount.selectedAccount && selectFromAccount.selectedAccount.address &&
@ -151,7 +154,9 @@ ModalPopup {
console.warn(qsTrId("error-estimating-gas---1").arg(gasEstimate.error.message)) console.warn(qsTrId("error-estimating-gas---1").arg(gasEstimate.error.message))
return return
} }
selectedGasLimit = gasEstimate.result selectedGasLimit = gasEstimate.result
defaultGasLimit = selectedGasLimit
}) })
} }
GasValidator { GasValidator {
@ -222,6 +227,14 @@ ModalPopup {
stack.back() stack.back()
} }
} }
Component {
id: transactionSettingsConfirmationPopupComponent
TransactionSettingsConfirmationPopup {
}
}
StatusButton { StatusButton {
id: btnNext id: btnNext
anchors.right: parent.right anchors.right: parent.right
@ -235,6 +248,27 @@ ModalPopup {
if (stack.isLastGroup) { if (stack.isLastGroup) {
return root.sendTransaction() return root.sendTransaction()
} }
if(gasSelector.eip1599Enabled && stack.currentGroup === group2 && gasSelector.advancedMode){
if(gasSelector.showPriceLimitWarning || gasSelector.showTipLimitWarning){
openPopup(transactionSettingsConfirmationPopupComponent, {
currentBaseFee: gasSelector.latestBaseFeeGwei,
currentMinimumTip: gasSelector.perGasTipLimitFloor,
currentAverageTip: gasSelector.perGasTipLimitAverage,
tipLimit: gasSelector.selectedTipLimit,
suggestedTipLimit: gasSelector.perGasTipLimitFloor,
priceLimit: gasSelector.selectedOverallLimit,
suggestedPriceLimit: gasSelector.latestBaseFeeGwei + gasSelector.perGasTipLimitFloor,
showPriceLimitWarning: gasSelector.showPriceLimitWarning,
showTipLimitWarning: gasSelector.showTipLimitWarning,
onConfirm: function(){
stack.next();
}
})
return
}
}
stack.next() stack.next()
} }
} }

View File

@ -0,0 +1,258 @@
import QtQuick 2.13
import QtQuick.Controls 2.13
import QtQuick.Layouts 1.13
import "../../../imports"
import "../../../shared/status"
import "../../../shared"
ModalPopup {
id: popup
height: 300 + (showPriceLimitWarning ? 65 : 0) + (showTipLimitWarning ? 65 : 0)
width: 400
title: qsTr("Are you sure?")
property var onConfirm: function(){}
property double currentBaseFee: 0
property double currentMinimumTip: 0
property double currentAverageTip: 0
property double tipLimit: 0
property double suggestedTipLimit: 0
property double priceLimit: 0
property double suggestedPriceLimit: 0
property bool showPriceLimitWarning: false
property bool showTipLimitWarning: false
Column {
id: content
width: 450
height: parent.height
spacing: 10
StyledText {
anchors.left: parent.left
anchors.leftMargin: Style.current.smallPadding
text: qsTr("Your priority fee is below our suggested parameters.")
font.pixelSize: 13
wrapMode: Text.WordWrap
color: Style.current.secondaryText
}
Item {
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: Style.current.smallPadding
height: 20
StyledText {
text: qsTr("Current base fee")
font.pixelSize: 13
width: 190
anchors.left: parent.left
}
StyledText {
text: qsTr("%1 Gwei").arg(currentBaseFee)
font.pixelSize: 13
width: 190
anchors.right: parent.right
}
}
Item {
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: Style.current.smallPadding
height: 20
StyledText {
text: qsTr("Current minimum tip")
font.pixelSize: 13
width: 190
anchors.left: parent.left
}
StyledText {
text: qsTr("%1 Gwei").arg(currentMinimumTip)
font.pixelSize: 13
width: 190
anchors.right: parent.right
}
}
Item {
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: Style.current.smallPadding
height: 20
StyledText {
text: qsTr("Current average tip")
font.pixelSize: 13
width: 190
anchors.left: parent.left
}
StyledText {
text: qsTr("%1 Gwei").arg(currentAverageTip)
font.pixelSize: 13
width: 190
anchors.right: parent.right
}
}
Rectangle {
id: tipLimitRect
width: 368
visible: showTipLimitWarning
height: visible ? 70 : 0
radius: 8
color: Style.current.backgroundHoverLight
Column {
anchors.top: parent.top
anchors.topMargin: Style.current.smallPadding
anchors.left: parent.left
anchors.leftMargin: Style.current.smallPadding
height: 100
width: 450 - Style.current.smallPadding
spacing: 10
Item {
anchors.left: parent.left
anchors.right: parent.right
height: 20
StyledText {
text: qsTr("Your tip limit")
font.pixelSize: 13
width: 190
anchors.left: parent.left
color: Style.current.red
}
StyledText {
text: qsTr("%1 Gwei").arg(tipLimit)
font.pixelSize: 13
width: 190
anchors.right: parent.right
color: Style.current.red
}
}
Item {
anchors.left: parent.left
anchors.right: parent.right
height: 20
StyledText {
text: qsTr("Suggested minimum tip")
font.pixelSize: 13
width: 190
anchors.left: parent.left
}
StyledText {
text: qsTr("%1 Gwei").arg(suggestedTipLimit)
font.pixelSize: 13
width: 190
anchors.right: parent.right
}
}
}
}
Rectangle {
id: minPriceLimitRect
width: 368
visible: showPriceLimitWarning
height: visible ? 70 : 0
radius: 8
color: Style.current.backgroundHoverLight
Column {
anchors.top: parent.top
anchors.topMargin: Style.current.smallPadding
anchors.left: parent.left
anchors.leftMargin: Style.current.smallPadding
height: 100
width: 450 - Style.current.smallPadding
spacing: 10
Item {
anchors.left: parent.left
anchors.right: parent.right
height: 20
StyledText {
text: qsTr("Your price limit")
font.pixelSize: 13
width: 190
anchors.left: parent.left
color: Style.current.red
}
StyledText {
text: qsTr("%1 Gwei").arg(priceLimit)
font.pixelSize: 13
width: 190
anchors.right: parent.right
color: Style.current.red
}
}
Item {
anchors.left: parent.left
anchors.right: parent.right
height: 20
StyledText {
text: qsTr("Suggested minimum price limit")
font.pixelSize: 13
width: 190
anchors.left: parent.left
}
StyledText {
text: qsTr("%1 Gwei").arg(suggestedPriceLimit)
font.pixelSize: 13
width: 190
anchors.right: parent.right
}
}
}
}
}
footer: Item {
id: footerContainer
width: parent.width
StatusButton {
id: cancelButton
anchors.right: confirmButton.left
anchors.rightMargin: Style.current.smallPadding
text: qsTr("Change Limit")
anchors.bottom: parent.bottom
onClicked: popup.destroy()
}
StatusButton {
id: confirmButton
type: "warn"
anchors.right: parent.right
anchors.rightMargin: Style.current.smallPadding
text: qsTr("Continue anyway")
anchors.bottom: parent.bottom
onClicked: {
popup.onConfirm();
popup.destroy();
}
}
}
}

View File

@ -743,16 +743,15 @@ Item {
} }
} }
ToastMessage { ToastMessage {
id: toastMessage id: toastMessage
} }
// Add SendModal here as it is used by the Wallet as well as the Browser // Add SendModal here as it is used by the Wallet as well as the Browser
Loader { Loader {
id: sendModal id: sendModal
active: false active: false
function open() { function open() {
this.active = true this.active = true
this.item.open() this.item.open()
@ -763,7 +762,7 @@ Item {
} }
sourceComponent: SendModal { sourceComponent: SendModal {
onOpened: { onOpened: {
walletModel.gasView.getGasPricePredictions() walletModel.gasView.getGasPrice()
} }
onClosed: { onClosed: {
sendModal.closed() sendModal.closed()

View File

@ -11,14 +11,30 @@ Item {
height: Style.current.smallPadding + prioritytext.height + height: Style.current.smallPadding + prioritytext.height +
(advancedMode ? advancedModeItemGroup.height : selectorButtons.height) (advancedMode ? advancedModeItemGroup.height : selectorButtons.height)
property double slowestGasPrice: 0 property double gasPrice: 0
property double fastestGasPrice: 100
property double stepSize: ((root.fastestGasPrice - root.slowestGasPrice) / 10).toFixed(1)
property bool eip1599Enabled: walletModel.transactionsView.isEIP1559Enabled
property var suggestedFees: JSON.parse(walletModel.gasView.suggestedFees)
property var latestBaseFee: JSON.parse(walletModel.transactionsView.latestBaseFee)
property double latestBaseFeeGwei: {
if (!eip1599Enabled) return 0;
return parseFloat(latestBaseFee.gwei)
}
property var getGasGweiValue: function () {}
property var getGasEthValue: function () {} property var getGasEthValue: function () {}
property var getFiatValue: function () {} property var getFiatValue: 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 alias selectedTipLimit: inputPerGasTipLimit.text
property alias selectedOverallLimit: inputGasPrice.text
property double selectedGasEthValue property double selectedGasEthValue
property double selectedGasFiatValue property double selectedGasFiatValue
//% "Must be greater than 0" //% "Must be greater than 0"
@ -30,13 +46,26 @@ Item {
property bool isValid: true property bool isValid: true
readonly property string uuid: Utils.uuid() readonly property string uuid: Utils.uuid()
property bool advancedMode: false property bool advancedMode: true // TODO: change to false once EIP1559 suggestions are revised
// TODO: change these values false once EIP1559 suggestions are revised
property double perGasTipLimitFloor: 1 // Matches status-react minimum-priority-fee
property double perGasTipLimitAverage: formatDec(suggestedFees.maxPriorityFeePerGas, 2) // 1.5 // Matches status-react average-priority-fee
property bool showPriceLimitWarning : false
property bool showTipLimitWarning : false
function formatDec(num, dec){
return Math.round((num + Number.EPSILON) * Math.pow(10, dec)) / Math.pow(10, dec)
}
function updateGasEthValue() { function updateGasEthValue() {
// causes error on application load without this null check // causes error on application load without this null check
if (!inputGasPrice || !inputGasLimit) { if (!inputGasPrice || !inputGasLimit) {
return return
} }
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)
@ -44,39 +73,114 @@ Item {
selectedGasFiatValue = fiatValue selectedGasFiatValue = fiatValue
} }
Component.onCompleted: updateGasEthValue() function appendError(accum, error, nonBlocking = false) {
return accum + ` <span class="${nonBlocking ? "non-blocking" : ""}">${error}.</span>`
}
function checkLimits(){
if(!eip1599Enabled) return;
let inputTipLimit = parseFloat(inputPerGasTipLimit.text || "0.00")
let inputOverallLimit = parseFloat(inputGasPrice.text || "0.00")
let gasLimit = parseInt(inputGasLimit.text, 10)
errorsText.text = "";
showPriceLimitWarning = false
showTipLimitWarning = false
let errorMsg = "";
if(gasLimit < 21000) {
errorMsg = appendError(errorMsg, qsTr("Min 21000 units"))
} else if (gasLimit < parseInt(defaultGasLimit)){
errorMsg = appendError(errorMsg, qsTr("Not enough gas").arg(perGasTipLimitAverage), true)
}
// Per-gas tip limit rules
if(inputTipLimit < perGasTipLimitFloor){
errorMsg = appendError(errorMsg, qsTr("Miners will currently not process transactions with a tip below %1 Gwei, the average is %2 Gwei").arg(perGasTipLimitFloor).arg(perGasTipLimitAverage))
showTipLimitWarning = true
} else if (inputTipLimit < perGasTipLimitAverage) {
errorMsg = appendError(errorMsg, qsTr("The average miner tip is %1 Gwei").arg(perGasTipLimitAverage), true)
}
// Per-gas overall limit rules
if(inputOverallLimit < latestBaseFeeGwei){
errorMsg = appendError(errorMsg, qsTr("The limit is below the current base fee of %1 %2").arg(latestBaseFeeGwei).arg("Gwei"))
showPriceLimitWarning = true
}
/* TODO: change these values false once EIP1559 suggestions are revised
else if((inputOverallLimit - inputTipLimit) < latestBaseFeeGwei){
errorMsg = appendError(errorMsg, qsTr("The limit should be at least %1 Gwei above the base fee").arg(perGasTipLimitFloor))
} else if((inputOverallLimit - perGasTipLimitAverage) < latestBaseFeeGwei) {
errorMsg = appendError(errorMsg, qsTr("The maximum miner tip after the current base fee will be %1 Gwei, the minimum miner tip is currently %2 Gwei").arg(inputOverallLimit).arg(perGasTipLimitFloor), true)
showTipLimitWarning = true
}*/
errorsText.text = `<style type="text/css">span { color: "#ff0000" } span.non-blocking { color: "#FE8F59" }</style>${errorMsg}`
}
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) { if (!inputGasLimit || !inputGasPrice || !inputPerGasTipLimit) {
return return
} }
inputGasLimit.validationError = "" inputGasLimit.validationError = ""
inputGasPrice.validationError = "" inputGasPrice.validationError = ""
inputPerGasTipLimit.validationError = ""
const noInputLimit = inputGasLimit.text === "" const noInputLimit = inputGasLimit.text === ""
const noInputPrice = inputGasPrice.text === "" const noInputPrice = inputGasPrice.text === ""
const noPerGasTip = inputPerGasTipLimit.text === ""
if (noInputLimit) { if (noInputLimit) {
inputGasLimit.validationError = root.noInputErrorMessage inputGasLimit.validationError = root.noInputErrorMessage
} }
if (noInputPrice) { if (noInputPrice) {
inputGasPrice.validationError = root.noInputErrorMessage inputGasPrice.validationError = root.noInputErrorMessage
} }
if (noPerGasTip) {
inputPerGasTipLimit.validationError = root.noInputErrorMessage
}
if (isNaN(inputGasLimit.text)) { if (isNaN(inputGasLimit.text)) {
inputGasLimit.validationError = invalidInputErrorMessage inputGasLimit.validationError = invalidInputErrorMessage
} }
if (isNaN(inputGasPrice.text)) { if (isNaN(inputGasPrice.text)) {
inputGasPrice.validationError = invalidInputErrorMessage inputGasPrice.validationError = invalidInputErrorMessage
} }
if (isNaN(inputPerGasTipLimit.text)) {
inputPerGasTipLimit.validationError = invalidInputErrorMessage
}
let inputLimit = parseFloat(inputGasLimit.text || "0.00") let inputLimit = parseFloat(inputGasLimit.text || "0.00")
let inputPrice = parseFloat(inputGasPrice.text || "0.00") let inputPrice = parseFloat(inputGasPrice.text || "0.00")
if (inputLimit <= 0) { let inputTipLimit = parseFloat(inputPerGasTipLimit.text || "0.00")
if (inputLimit <= 0.00) {
inputGasLimit.validationError = root.greaterThan0ErrorMessage inputGasLimit.validationError = root.greaterThan0ErrorMessage
} }
if (inputPrice <= 0) {
if (inputPrice <= 0.00) {
inputGasPrice.validationError = root.greaterThan0ErrorMessage inputGasPrice.validationError = root.greaterThan0ErrorMessage
} }
const isValid = inputGasLimit.validationError === "" && inputGasPrice.validationError === "" if (inputTipLimit <= 0.00) {
return isValid inputPerGasTipLimit.validationError = root.greaterThan0ErrorMessage
}
const isInputValid = inputGasLimit.validationError === "" && inputGasPrice.validationError === "" && inputPerGasTipLimit.validationError === ""
return isInputValid
} }
@ -91,7 +195,20 @@ Item {
color: Style.current.textColor color: Style.current.textColor
} }
StyledText {
id: baseFeeText
visible: eip1599Enabled && advancedMode
anchors.top: parent.top
anchors.left: prioritytext.right
anchors.leftMargin: Style.current.smallPadding
text: qsTr("Current base fee: %1 %2").arg(latestBaseFeeGwei).arg("Gwei")
font.weight: Font.Medium
font.pixelSize: 13
color: Style.current.secondaryText
}
StatusButton { StatusButton {
visible: false // Change to TRUE once EIP1559 suggestions are revised
id: buttonAdvanced id: buttonAdvanced
anchors.verticalCenter: prioritytext.verticalCenter anchors.verticalCenter: prioritytext.verticalCenter
anchors.right: parent.right anchors.right: parent.right
@ -119,14 +236,25 @@ Item {
GasSelectorButton { GasSelectorButton {
buttonGroup: gasGroup buttonGroup: gasGroup
//% "Low" text: qsTr("Low")
text: qsTrId("low") price: {
price: slowestGasPrice if (!eip1599Enabled) return gasPrice;
return formatDec(suggestedFees.maxFeePerGasL, 6)
}
gasLimit: inputGasLimit ? inputGasLimit.text : "" gasLimit: inputGasLimit ? inputGasLimit.text : ""
getGasEthValue: root.getGasEthValue getGasEthValue: root.getGasEthValue
getFiatValue: root.getFiatValue getFiatValue: root.getFiatValue
defaultCurrency: root.defaultCurrency defaultCurrency: root.defaultCurrency
onChecked: inputGasPrice.text = price onChecked: {
if (eip1599Enabled){
inputPerGasTipLimit.text = formatDec(suggestedFees.maxPriorityFeePerGas, 2);
inputGasPrice.text = formatDec(suggestedFees.maxFeePerGasL, 2);
} else {
inputGasPrice.text = price
}
root.updateGasEthValue()
root.checkLimits()
}
} }
GasSelectorButton { GasSelectorButton {
id: optimalGasButton id: optimalGasButton
@ -135,28 +263,52 @@ Item {
//% "Optimal" //% "Optimal"
text: qsTrId("optimal") text: qsTrId("optimal")
price: { price: {
const price = (fastestGasPrice + slowestGasPrice) / 2 if (!eip1599Enabled) {
// Setting the gas price field here because the binding didn't work const price = gasPrice
inputGasPrice.text = price // Setting the gas price field here because the binding didn't work
return price inputGasPrice.text = price
return price
}
return formatDec(suggestedFees.maxFeePerGasM, 6)
} }
gasLimit: inputGasLimit ? inputGasLimit.text : "" gasLimit: inputGasLimit ? inputGasLimit.text : ""
getGasEthValue: root.getGasEthValue getGasEthValue: root.getGasEthValue
getFiatValue: root.getFiatValue getFiatValue: root.getFiatValue
defaultCurrency: root.defaultCurrency defaultCurrency: root.defaultCurrency
onChecked: inputGasPrice.text = price onChecked: {
if (eip1599Enabled){
inputPerGasTipLimit.text = formatDec(suggestedFees.maxPriorityFeePerGas, 2);
inputGasPrice.text = formatDec(suggestedFees.maxFeePerGasM, 2);
} else {
inputGasPrice.text = price
}
root.updateGasEthValue()
root.checkLimits()
}
} }
GasSelectorButton { GasSelectorButton {
buttonGroup: gasGroup buttonGroup: gasGroup
//% "High" text: qsTr("High")
text: qsTrId("high") price: {
price: fastestGasPrice if (!eip1599Enabled) return gasPrice;
return formatDec(suggestedFees.maxFeePerGasH,6);
}
gasLimit: inputGasLimit ? inputGasLimit.text : "" gasLimit: inputGasLimit ? inputGasLimit.text : ""
getGasEthValue: root.getGasEthValue getGasEthValue: root.getGasEthValue
getFiatValue: root.getFiatValue getFiatValue: root.getFiatValue
defaultCurrency: root.defaultCurrency defaultCurrency: root.defaultCurrency
onChecked: inputGasPrice.text = price onChecked: {
if (eip1599Enabled){
inputPerGasTipLimit.text = formatDec(suggestedFees.maxPriorityFeePerGas, 2);
inputGasPrice.text = formatDec(suggestedFees.maxFeePerGasH, 2);
} else {
inputGasPrice.text = price
}
root.updateGasEthValue()
root.checkLimits()
}
} }
} }
@ -173,10 +325,11 @@ Item {
//% "Gas amount limit" //% "Gas amount limit"
label: qsTrId("gas-amount-limit") label: qsTrId("gas-amount-limit")
text: "21000" text: "21000"
inputLabel.color: Style.current.secondaryText
customHeight: 56 customHeight: 56
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: inputGasPrice.left anchors.right: eip1599Enabled ? inputPerGasTipLimit.left : inputGasPrice.left
anchors.rightMargin: Style.current.padding anchors.rightMargin: Style.current.padding
placeholderText: "21000" placeholderText: "21000"
validator: IntValidator{ validator: IntValidator{
@ -187,23 +340,59 @@ Item {
onTextChanged: { onTextChanged: {
if (root.validate()) { if (root.validate()) {
root.updateGasEthValue() root.updateGasEthValue()
root.checkLimits()
} }
} }
} }
Input {
id: inputPerGasTipLimit
label: qsTr("Per-gas tip limit")
inputLabel.color: Style.current.secondaryText
anchors.top: parent.top
anchors.right: inputGasPrice.left
anchors.rightMargin: Style.current.padding
anchors.left: undefined
visible: eip1599Enabled
width: 125
customHeight: 56
text: formatDec(suggestedFees.maxPriorityFeePerGas, 2);
placeholderText: "20"
onTextChanged: {
if (root.validate()) {
root.updateGasEthValue()
root.checkLimits()
}
}
}
StyledText {
color: Style.current.secondaryText
//% "Gwei"
text: qsTrId("gwei")
visible: eip1599Enabled
anchors.top: parent.top
anchors.topMargin: 42
anchors.right: inputPerGasTipLimit.right
anchors.rightMargin: Style.current.padding
font.pixelSize: 15
}
Input { Input {
id: inputGasPrice id: inputGasPrice
//% "Per-gas overall limit" //% "Per-gas overall limit"
label: qsTrId("per-gas-overall-limit") label: qsTrId("per-gas-overall-limit")
inputLabel.color: Style.current.secondaryText
anchors.top: parent.top anchors.top: parent.top
anchors.left: undefined anchors.left: undefined
anchors.right: parent.right anchors.right: parent.right
width: 130 width: 125
customHeight: 56 customHeight: 56
placeholderText: "20" placeholderText: "20"
onTextChanged: { onTextChanged: {
if (root.validate()) { if (root.validate()) {
root.updateGasEthValue() root.updateGasEthValue()
root.checkLimits()
} }
} }
} }
@ -220,17 +409,37 @@ Item {
} }
StyledText { StyledText {
id: maxPriorityFeeText id: errorsText
//% "Maximum priority fee: %1 ETH" text: ""
text: qsTrId("maximum-priority-fee---1-eth").arg(selectedGasEthValue) width: parent.width - Style.current.padding
visible: text != ""
height: visible ? undefined : 0
anchors.top: inputGasLimit.bottom anchors.top: inputGasLimit.bottom
anchors.topMargin: 19 anchors.topMargin: Style.current.smallPadding + 5
font.pixelSize: 13 font.pixelSize: 13
textFormat: Text.RichText
color: Style.current.secondaryText
wrapMode: Text.WordWrap
}
StyledText {
id: maxPriorityFeeText
anchors.left: parent.left
//% "Maximum priority fee: %1 ETH"
text: {
let v = selectedGasEthValue > 0.00009 ? selectedGasEthValue :
(selectedGasEthValue < 0.000001 ? "0.000000..." : selectedGasEthValue.toFixed(6))
return qsTrId("maximum-priority-fee---1-eth").arg(v)
}
anchors.top: errorsText.bottom
anchors.topMargin: Style.current.smallPadding + 5
font.pixelSize: 13
color: Style.current.textColor
} }
StyledText { StyledText {
id: maxPriorityFeeFiatText id: maxPriorityFeeFiatText
text: `${selectedGasFiatValue} ${root.defaultCurrency}` text: `${selectedGasFiatValue} ${root.defaultCurrency.toUpperCase()}`
anchors.verticalCenter: maxPriorityFeeText.verticalCenter anchors.verticalCenter: maxPriorityFeeText.verticalCenter
anchors.left: maxPriorityFeeText.right anchors.left: maxPriorityFeeText.right
anchors.leftMargin: 6 anchors.leftMargin: 6

View File

@ -17,11 +17,17 @@ Rectangle {
property bool checkedByDefault: false property bool checkedByDefault: false
property var getGasEthValue: function () {} property var getGasEthValue: function () {}
property var getFiatValue: function () {} property var getFiatValue: function () {}
function formatDec(num, dec){
return Math.round((num + Number.EPSILON) * Math.pow(10, dec)) / Math.pow(10, dec)
}
property double ethValue: { property double ethValue: {
if (!gasLimit) { if (!gasLimit) {
return 0 return 0
} }
return getGasEthValue(price, gasLimit) return formatDec(parseFloat(getGasEthValue(price, gasLimit)), 6)
} }
property double fiatValue: getFiatValue(ethValue, "ETH", defaultCurrency) property double fiatValue: getFiatValue(ethValue, "ETH", defaultCurrency)
signal checked() signal checked()

View File

@ -9,6 +9,8 @@ import "."
Item { Item {
property alias textField: inputValue property alias textField: inputValue
property alias inputLabel: inputLabel
property string placeholderText: "My placeholder" property string placeholderText: "My placeholder"
property string placeholderTextColor: Style.current.secondaryText property string placeholderTextColor: Style.current.secondaryText
property alias text: inputValue.text property alias text: inputValue.text

View File

@ -5,6 +5,7 @@ import QtQuick.Dialogs 1.3
import "../../imports" import "../../imports"
import "../../shared" import "../../shared"
import "../../shared/status" import "../../shared/status"
import "../../app/AppLayouts/Wallet/"
ModalPopup { ModalPopup {
id: root id: root
@ -20,14 +21,17 @@ ModalPopup {
walletModel.gasView.getGasPricePredictions() walletModel.gasView.getGasPricePredictions()
} }
height: 540
function sendTransaction() { function sendTransaction() {
try { try {
let responseStr = onSendTransaction(selectFromAccount.selectedAccount.address, let responseStr = profileModel.ens.setPubKey(root.ensUsername,
gasSelector.selectedGasLimit, selectFromAccount.selectedAccount.address,
gasSelector.selectedGasPrice, gasSelector.selectedGasLimit,
transactionSigner.enteredPassword); gasSelector.eip1599Enabled ? "" : gasSelector.selectedGasPrice,
gasSelector.selectedTipLimit,
gasSelector.selectedOverallLimit,
transactionSigner.enteredPassword)
let response = JSON.parse(responseStr) let response = JSON.parse(responseStr)
if (!response.success) { if (!response.success) {
@ -105,11 +109,11 @@ ModalPopup {
visible: true visible: true
anchors.top: selectFromAccount.bottom anchors.top: selectFromAccount.bottom
anchors.topMargin: Style.current.bigPadding * 2 anchors.topMargin: Style.current.bigPadding * 2
slowestGasPrice: parseFloat(walletModel.gasView.safeLowGasPrice) gasPrice: parseFloat(walletModel.gasView.gasPrice)
fastestGasPrice: parseFloat(walletModel.gasView.fastestGasPrice)
getGasEthValue: walletModel.gasView.getGasEthValue getGasEthValue: walletModel.gasView.getGasEthValue
getFiatValue: walletModel.balanceView.getFiatValue getFiatValue: walletModel.balanceView.getFiatValue
defaultCurrency: walletModel.balanceView.defaultCurrency defaultCurrency: walletModel.balanceView.defaultCurrency
property var estimateGas: Backpressure.debounce(gasSelector, 600, function() { property var estimateGas: Backpressure.debounce(gasSelector, 600, function() {
let estimatedGas = root.estimateGasFunction(selectFromAccount.selectedAccount); let estimatedGas = root.estimateGasFunction(selectFromAccount.selectedAccount);
gasSelector.selectedGasLimit = estimatedGas gasSelector.selectedGasLimit = estimatedGas
@ -168,7 +172,7 @@ ModalPopup {
width: parent.width width: parent.width
height: btnNext.height height: btnNext.height
StatusRoundButton { StatusRoundButton {
id: btnBack id: btnBack
anchors.left: parent.left anchors.left: parent.left
icon.name: "arrow-right" icon.name: "arrow-right"
@ -185,6 +189,13 @@ ModalPopup {
} }
} }
Component {
id: transactionSettingsConfirmationPopupComponent
TransactionSettingsConfirmationPopup {
}
}
StatusButton { StatusButton {
id: btnNext id: btnNext
anchors.right: parent.right anchors.right: parent.right
@ -197,6 +208,27 @@ ModalPopup {
if (stack.isLastGroup) { if (stack.isLastGroup) {
return root.sendTransaction() return root.sendTransaction()
} }
if(gasSelector.eip1599Enabled && stack.currentGroup === group2 && gasSelector.advancedMode){
if(gasSelector.showPriceLimitWarning || gasSelector.showTipLimitWarning){
openPopup(transactionSettingsConfirmationPopupComponent, {
currentBaseFee: gasSelector.latestBaseFeeGwei,
currentMinimumTip: gasSelector.perGasTipLimitFloor,
currentAverageTip: gasSelector.perGasTipLimitAverage,
tipLimit: gasSelector.selectedTipLimit,
suggestedTipLimit: gasSelector.perGasTipLimitFloor,
priceLimit: gasSelector.selectedOverallLimit,
suggestedPriceLimit: gasSelector.latestBaseFeeGwei + gasSelector.perGasTipLimitFloor,
showPriceLimitWarning: gasSelector.showPriceLimitWarning,
showTipLimitWarning: gasSelector.showTipLimitWarning,
onConfirm: function(){
stack.next();
}
})
return
}
}
stack.next() stack.next()
} }
} }

View File

@ -5,6 +5,7 @@ import QtQuick.Dialogs 1.3
import "../../imports" import "../../imports"
import "../../shared" import "../../shared"
import "../../shared/status" import "../../shared/status"
import "../../app/AppLayouts/Wallet/"
ModalPopup { ModalPopup {
id: root id: root
@ -12,13 +13,15 @@ ModalPopup {
property string assetPrice property string assetPrice
property string contractAddress property string contractAddress
property var estimateGasFunction: (function(userAddress, uuid) { return 0; }) property var estimateGasFunction: (function(userAddress, uuid) { return 0; })
property var onSendTransaction: (function(userAddress, gasLimit, gasPrice, password){ return ""; }) property var onSendTransaction: (function(userAddress, gasLimit, gasPrice, tipLimit, overallLimit, password){ return ""; })
property var onSuccess: (function(){}) property var onSuccess: (function(){})
Component.onCompleted: { Component.onCompleted: {
walletModel.gasView.getGasPricePredictions() walletModel.gasView.getGasPrice()
} }
height: 540
//% "Authorize %1 %2" //% "Authorize %1 %2"
title: qsTrId("authorize--1--2").arg(Utils.stripTrailingZeros(assetPrice)).arg(asset.symbol) title: qsTrId("authorize--1--2").arg(Utils.stripTrailingZeros(assetPrice)).arg(asset.symbol)
@ -33,13 +36,16 @@ ModalPopup {
function setAsyncGasLimitResult(uuid, value) { function setAsyncGasLimitResult(uuid, value) {
if (uuid === gasSelector.uuid) { if (uuid === gasSelector.uuid) {
gasSelector.selectedGasLimit = value gasSelector.selectedGasLimit = value
gasSelector.defaultGasLimit = value
} }
} }
function sendTransaction() { function sendTransaction() {
let responseStr = onSendTransaction(selectFromAccount.selectedAccount.address, let responseStr = onSendTransaction(selectFromAccount.selectedAccount.address,
gasSelector.selectedGasLimit, gasSelector.selectedGasLimit,
gasSelector.selectedGasPrice, gasSelector.eip1599Enabled ? "" : gasSelector.selectedGasPrice,
gasSelector.selectedTipLimit,
gasSelector.selectedOverallLimit,
transactionSigner.enteredPassword); transactionSigner.enteredPassword);
let response = JSON.parse(responseStr) let response = JSON.parse(responseStr)
@ -108,12 +114,12 @@ ModalPopup {
id: gasSelector id: gasSelector
anchors.top: selectFromAccount.bottom anchors.top: selectFromAccount.bottom
anchors.topMargin: Style.current.bigPadding * 2 anchors.topMargin: Style.current.bigPadding * 2
slowestGasPrice: parseFloat(walletModel.gasView.safeLowGasPrice) gasPrice: parseFloat(walletModel.gasView.gasPrice)
fastestGasPrice: parseFloat(walletModel.gasView.fastestGasPrice)
getGasEthValue: walletModel.gasView.getGasEthValue getGasEthValue: walletModel.gasView.getGasEthValue
getFiatValue: walletModel.balanceView.getFiatValue getFiatValue: walletModel.balanceView.getFiatValue
defaultCurrency: walletModel.balanceView.defaultCurrency defaultCurrency: walletModel.balanceView.defaultCurrency
width: stack.width width: stack.width
property var estimateGas: Backpressure.debounce(gasSelector, 600, function() { property var estimateGas: Backpressure.debounce(gasSelector, 600, function() {
let estimatedGas = root.estimateGasFunction(selectFromAccount.selectedAccount, uuid); let estimatedGas = root.estimateGasFunction(selectFromAccount.selectedAccount, uuid);
gasSelector.selectedGasLimit = estimatedGas gasSelector.selectedGasLimit = estimatedGas
@ -190,6 +196,14 @@ ModalPopup {
stack.back() stack.back()
} }
} }
Component {
id: transactionSettingsConfirmationPopupComponent
TransactionSettingsConfirmationPopup {
}
}
StatusButton { StatusButton {
id: btnNext id: btnNext
anchors.right: parent.right anchors.right: parent.right
@ -203,6 +217,27 @@ ModalPopup {
if (stack.isLastGroup) { if (stack.isLastGroup) {
return root.sendTransaction() return root.sendTransaction()
} }
if(gasSelector.eip1599Enabled && stack.currentGroup === group2 && gasSelector.advancedMode){
if(gasSelector.showPriceLimitWarning || gasSelector.showTipLimitWarning){
openPopup(transactionSettingsConfirmationPopupComponent, {
currentBaseFee: gasSelector.latestBaseFeeGwei,
currentMinimumTip: gasSelector.perGasTipLimitFloor,
currentAverageTip: gasSelector.perGasTipLimitAverage,
tipLimit: gasSelector.selectedTipLimit,
suggestedTipLimit: gasSelector.perGasTipLimitFloor, // TODO:
priceLimit: gasSelector.selectedOverallLimit,
suggestedPriceLimit: gasSelector.latestBaseFeeGwei + gasSelector.perGasTipLimitFloor,
showPriceLimitWarning: gasSelector.showPriceLimitWarning,
showTipLimitWarning: gasSelector.showTipLimitWarning,
onConfirm: function(){
stack.next();
}
})
return
}
}
stack.next() stack.next()
} }
} }

View File

@ -98,12 +98,14 @@ Item {
if (packId < 0 || !selectedAccount || !price) return 325000 if (packId < 0 || !selectedAccount || !price) return 325000
return chatsModel.stickers.estimate(packId, selectedAccount.address, price, uuid) return chatsModel.stickers.estimate(packId, selectedAccount.address, price, uuid)
} }
onSendTransaction: function(selectedAddress, gasLimit, gasPrice, password) { onSendTransaction: function(selectedAddress, gasLimit, gasPrice, tipLimit, overallLimit, password) {
return chatsModel.stickers.buy(packId, return chatsModel.stickers.buy(packId,
selectedAddress, selectedAddress,
price, price,
gasLimit, gasLimit,
gasPrice, gasPrice,
tipLimit,
overallLimit,
password) password)
} }
onClosed: { onClosed: {

View File

@ -60,12 +60,14 @@ ModalPopup {
if (packId < 0 || !selectedAccount || !price) return 325000 if (packId < 0 || !selectedAccount || !price) return 325000
return chatsModel.stickers.estimate(packId, selectedAccount.address, price, uuid) return chatsModel.stickers.estimate(packId, selectedAccount.address, price, uuid)
} }
onSendTransaction: function(selectedAddress, gasLimit, gasPrice, password) { onSendTransaction: function(selectedAddress, gasLimit, gasPrice, tipLimit, overallLimit, password) {
return chatsModel.stickers.buy(packId, return chatsModel.stickers.buy(packId,
selectedAddress, selectedAddress,
price, price,
gasLimit, gasLimit,
gasPrice, gasPrice,
tipLimit,
overallLimit,
password) password)
} }
onClosed: { onClosed: {

2
vendor/status-lib vendored

@ -1 +1 @@
Subproject commit efe2790db6cf5e3f01d4b3265d2a671fed70e2d1 Subproject commit c1d61a13c0592e83083161e4b6d5b2e126a8a424