feat(wallet): transaction settings component added

Closes #16193 #16194
This commit is contained in:
Sale Djenic 2025-01-21 23:16:28 +01:00
parent ed8564d228
commit 73d4f30dc4
10 changed files with 755 additions and 26 deletions

View File

@ -3,6 +3,7 @@ import QtQuick.Controls 2.14
import QtQuick.Layouts 1.14
import StatusQ.Controls 0.1
import StatusQ.Core.Theme 0.1
import Storybook 1.0
import Models 1.0
@ -32,6 +33,14 @@ SplitView {
enabled: enabledCheckBox.checked
input.edit.readOnly: readOnlyCheckBox.checked
input.clearable: clearableCheckBox.checked
label: "main label"
secondaryLabel: "secondary label"
labelIcon: "info"
labelIconColor: Theme.palette.baseColor1
labelIconClickable: true
leftPadding: 10
bottomLabelMessageRightCmp.text: "Current: 8.2 GWEI"
bottomLabelMessageLeftCmp.text: "0.0031 ETH"
}
}

View File

@ -0,0 +1,88 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import StatusQ.Controls 0.1
import StatusQ.Core.Theme 0.1
import AppLayouts.Wallet.views 1.0
import utils 1.0
import Storybook 1.0
SplitView {
id: root
SplitView {
SplitView.fillWidth: true
SplitView.fillHeight: true
orientation: Qt.Vertical
Rectangle {
SplitView.fillWidth: true
SplitView.fillHeight: true
color: Theme.palette.baseColor3
TransactionSettings {
id: txSettings
anchors.centerIn: parent
currentBaseFee: "8.2"
currentSuggestedMinPriorityFee: "0.06"
currentSuggestedMaxPriorityFee: "5.1"
currentGasAmount: "31500"
currentNonce: 21
normalPrice: "1.45 EUR"
normalTime: "~60s"
fastPrice: "1.65 EUR"
fastTime: "~40s"
urgentPrice: "1.85 EUR"
urgentTime: "~15s"
customBaseFee: "6.6"
customPriorityFee: "7.7"
customGasAmount: "35000"
customNonce: "22"
selectedFeeMode: StatusFeeOption.Type.Normal
fnGetPriceInCurrencyForFee: function(feeInWei) {
return "0.25 USD"
}
onConfirmClicked: {
logs.logEvent("confirm clicked...")
logs.logEvent(`selected fee mode: ${txSettings.selectedFeeMode}`)
if (selectedFeeMode === StatusFeeOption.Type.Custom) {
logs.logEvent(`selected customBaseFee...${txSettings.customBaseFee}`)
logs.logEvent(`selected customPriorityFee...${txSettings.customPriorityFee}`)
logs.logEvent(`selected customGasAmount...${txSettings.customGasAmount}`)
logs.logEvent(`selected customNonce...${txSettings.customNonce}`)
}
}
}
}
Logs {
id: logs
}
LogsView {
clip: true
SplitView.preferredHeight: 150
SplitView.fillWidth: true
logText: logs.logText
}
}
Pane {
SplitView.preferredWidth: 300
}
}
// category: Panel

View File

@ -96,6 +96,42 @@ Item {
\endqml
*/
property alias errorMessageCmp: errorMessage
/*!
\qmlproperty bottomLabelMessageLeftCmp
This property represents the bottomLabelMessageLeft shown on statusInput on the left of the input component.
By default this component is hidden and doesn't have any text set, once the text is set it will become visible.
Regardless of the set text and visibility bottomLabelMessageLeftCmp will be visible only if there is no error
meaning no errorMessageCmp visible.
Examples of usage
\qml
StatusInput {
bottomLabelMessageLeftCmp.text: "some text"
bottomLabelMessageLeftCmp.font.pixelSize: 15
bottomLabelMessageLeftCmp.font.weight: Font.Medium
}
\endqml
*/
property alias bottomLabelMessageLeftCmp: bottomLabelMessageLeft
/*!
\qmlproperty bottomLabelMessageCmp
This property represents the bottomLabelMessageRight shown on statusInput on the right of the input component.
By default this component is hidden and doesn't have any text set, once the text is set it will become visible.
Regardless of the set text and visibility bottomLabelMessageRightCmp will be visible only if there is no error
meaning no errorMessageCmp visible.
Examples of usage
\qml
StatusInput {
bottomLabelMessageRightCmp.text: "some text"
bottomLabelMessageRightCmp.font.pixelSize: 15
bottomLabelMessageRightCmp.font.weight: Font.Medium
}
\endqml
*/
property alias bottomLabelMessageRightCmp: bottomLabelMessageRight
/*!
\qmlproperty int StatusInput::labelPadding
This property sets the padding of the label text.
@ -111,6 +147,21 @@ Item {
This property sets the secondary label text.
*/
property string secondaryLabel: ""
/*!
\qmlproperty string StatusInput::labelIcon
This property sets the icon displayd on the right of the label.
*/
property string labelIcon: ""
/*!
\qmlproperty string StatusInput::labelIconColor
This property sets the color of the label icon.
*/
property string labelIconColor: Theme.palette.baseColor1
/*!
\qmlproperty string StatusInput::labelIconClickable
This property sets if the label icon is clickable or not, if clickable labelIconClicked signal will be emitted.
*/
property bool labelIconClickable: false
/*!
\qmlproperty int StatusInput::charLimit
This property sets the character limit of the text input.
@ -205,6 +256,11 @@ Item {
This signal is emitted when the icon is clicked.
*/
signal iconClicked()
/*!
\qmlsignal
This signal is emitted when the label icon is clicked.
*/
signal labelIconClicked()
/*!
\qmlsignal
This signal is emitted when a hard key is pressed passing as parameter the keyboard event.
@ -428,6 +484,23 @@ Item {
color: Theme.palette.baseColor1
}
StatusIcon {
id: labelIcon
visible: !!root.labelIcon
width: 16
height: 16
icon: root.labelIcon
color: root.labelIconColor
MouseArea {
anchors.fill: parent
enabled: root.labelIconClickable
hoverEnabled: root.labelIconClickable
cursorShape: containsMouse ? Qt.PointingHandCursor : Qt.ArrowCursor
onClicked: root.labelIconClicked()
}
}
Item {
Layout.fillWidth: true
}
@ -467,8 +540,24 @@ Item {
}
}
RowLayout {
id: bottomRow
Layout.topMargin: 8
Layout.fillWidth: true
Layout.preferredHeight: Math.max(bottomLabelMessageLeft.height, errorMessage.height, bottomLabelMessageLeft.height)
StatusBaseText {
id: bottomLabelMessageLeft
Layout.fillWidth: true
visible: !errorMessage.visible && !!text
horizontalAlignment: Text.AlignLeft
font.pixelSize: Theme.tertiaryTextFontSize
elide: Text.ElideMiddle
}
StatusBaseText {
id: errorMessage
Layout.fillWidth: true
visible: {
if (!text)
return false;
@ -479,12 +568,19 @@ Item {
return false;
}
font.pixelSize: 12
font.pixelSize: Theme.tertiaryTextFontSize
color: Theme.palette.dangerColor1
wrapMode: Text.WordWrap
horizontalAlignment: Text.AlignRight
Layout.topMargin: 8
Layout.fillWidth: true
}
StatusBaseText {
id: bottomLabelMessageRight
visible: !errorMessage.visible && !!text
horizontalAlignment: Text.AlignRight
font.pixelSize: Theme.tertiaryTextFontSize
elide: Text.ElideMiddle
}
}
}
}

View File

@ -142,7 +142,7 @@ QObject {
const totalMaxFees = Math.ceil(bestPath.gasFees.maxFeePerGasM) * bestPath.gasAmount
const totalMaxFeesInEth = AmountsArithmetic.div(
AmountsArithmetic.fromString(totalMaxFees),
AmountsArithmetic.fromNumber(1, Constants.gweiExponent))
AmountsArithmetic.fromNumber(1, Constants.ethTokenGWeiDecimals))
root.swapOutputData.maxFeesToReserveRaw = AmountsArithmetic.times(totalMaxFeesInEth, AmountsArithmetic.fromExponent(Constants.ethTokenDecimals)).toString()
root.swapOutputData.approvalNeeded = !!bestPath ? bestPath.approvalRequired: false

View File

@ -0,0 +1,491 @@
import QtQuick 2.15
import QtQuick.Layouts 1.15
import QtQuick.Controls 2.15
import StatusQ.Core 0.1
import StatusQ.Controls 0.1
import StatusQ.Controls.Validators 0.1
import StatusQ.Core.Utils 0.1 as SQUtils
import StatusQ.Core.Theme 0.1
import shared.controls 1.0
import shared.popups 1.0
import utils 1.0
Rectangle {
id: root
required property string currentBaseFee
required property string currentSuggestedMinPriorityFee
required property string currentSuggestedMaxPriorityFee
required property string currentGasAmount
required property int currentNonce
property alias normalPrice: optionNormal.subText
property alias normalTime: optionNormal.additionalText
property alias fastPrice: optionFast.subText
property alias fastTime: optionFast.additionalText
property alias urgentPrice: optionUrgent.subText
property alias urgentTime: optionUrgent.additionalText
property alias customBaseFee: customBaseFeeInput.text
property alias customBaseFeeDirty: customBaseFeeInput.input.dirty
property alias customPriorityFee: customPriorityFeeInput.text
property alias customPriorityFeeDirty: customPriorityFeeInput.input.dirty
property alias customGasAmount: customGasAmountInput.text
property alias customGasAmountDirty: customGasAmountInput.input.dirty
property alias customNonce: customNonceInput.text
property alias customNonceDirty: customNonceInput.input.dirty
required property int selectedFeeMode
required property var fnGetPriceInCurrencyForFee
signal confirmClicked()
signal cancelClicked()
color: Theme.palette.statusModal.backgroundColor
radius: Theme.radius
implicitHeight: layout.implicitHeight
implicitWidth: layout.implicitWidth
focus: true
Keys.onReleased: {
if (event.key === Qt.Key_Escape) {
root.cancelClicked()
}
}
function recalculateCustomPrice() {
d.recalculateCustomPrice()
}
Component.onCompleted: root.forceActiveFocus()
QtObject {
id: d
readonly property bool customMode: root.selectedFeeMode === StatusFeeOption.Type.Custom
function showAlert(title, text, note, url) {
infoBox.title = title
infoBox.text = text
infoBox.note = note
infoBox.url = url
infoBox.active = true
}
function recalculateCustomBaseFeePrice() {
if (!customBaseFeeInput.text) {
customBaseFeeInput.bottomLabelMessageRightCmp.text = ""
return
}
const weiValue = Utils.gweiToWei(customBaseFeeInput.text).toFixed()
customBaseFeeInput.bottomLabelMessageRightCmp.text = root.fnGetPriceInCurrencyForFee(weiValue)
}
function recalculateCustomPriorityFeePrice() {
if (!customPriorityFeeInput.text) {
customPriorityFeeInput.bottomLabelMessageRightCmp.text = ""
return
}
const weiValue = Utils.gweiToWei(customPriorityFeeInput.text).toFixed()
customPriorityFeeInput.bottomLabelMessageRightCmp.text = root.fnGetPriceInCurrencyForFee(weiValue)
}
function recalculateCustomPrice() {
if (!customBaseFeeInput.text || !customPriorityFeeInput.text || !customGasAmountInput.text) {
optionCustom.subText = ""
return
}
const baseFeeWei = Utils.gweiToWei(customBaseFeeInput.text)
const priorityFeeWei = Utils.gweiToWei(customPriorityFeeInput.text)
const totalFee = SQUtils.AmountsArithmetic.sum(baseFeeWei, priorityFeeWei)
const feeInWei = SQUtils.AmountsArithmetic.times(totalFee, SQUtils.AmountsArithmetic.fromString(customGasAmountInput.text)).toFixed()
optionCustom.subText = root.fnGetPriceInCurrencyForFee(feeInWei)
}
}
Loader {
id: infoBox
anchors.centerIn: root
active: false
property string title
property string text
property string note
property string url
sourceComponent: AlertPopup {
title: infoBox.title
width: root.width - 2 * 20
acceptBtnText: qsTr("Got it")
cancelBtn.text: !!infoBox.url? qsTr("Read more") : ""
cancelBtn.icon.name: "external-link"
cancelBtn.visible: !!infoBox.url
alertLabel.text: infoBox.text
alertNote.visible: !!infoBox.note
alertNote.text: infoBox.note
alertNote.color: Theme.palette.baseColor1
onCancelClicked: {
Qt.openUrlExternally(infoBox.url)
}
onClosed: {
infoBox.active = false
}
}
onLoaded: {
infoBox.item.open()
}
}
ColumnLayout {
id: layout
ColumnLayout {
Layout.margins: 20
spacing: 16
StatusBaseText {
Layout.preferredWidth: parent.width
text: qsTr("Transaction settings")
font.pixelSize: Theme.secondaryAdditionalTextSize
font.bold: true
elide: Text.ElideMiddle
}
RowLayout {
id: options
spacing: 12
StatusFeeOption {
id: optionNormal
type: StatusFeeOption.Type.Normal
selected: root.selectedFeeMode === StatusFeeOption.Type.Normal
showSubText: true
// showAdditionalText: true // TODO: temoporary disabled until we figure out how to estimate time more granularly
onClicked: root.selectedFeeMode = StatusFeeOption.Type.Normal
}
StatusFeeOption {
id: optionFast
type: StatusFeeOption.Type.Fast
selected: root.selectedFeeMode === StatusFeeOption.Type.Fast
showSubText: true
// showAdditionalText: true // TODO: temoporary disabled until we figure out how to estimate time more granularly
onClicked: root.selectedFeeMode = StatusFeeOption.Type.Fast
}
StatusFeeOption {
id: optionUrgent
type: StatusFeeOption.Type.Urgent
selected: root.selectedFeeMode === StatusFeeOption.Type.Urgent
showSubText: true
// showAdditionalText: true // TODO: temoporary disabled until we figure out how to estimate time more granularly
onClicked: root.selectedFeeMode = StatusFeeOption.Type.Urgent
}
StatusFeeOption {
id: optionCustom
type: StatusFeeOption.Type.Custom
selected: root.selectedFeeMode === StatusFeeOption.Type.Custom
showSubText: !!selected
// showAdditionalText: !!selected // TODO: temoporary disabled until we figure out how to estimate time more granularly
unselectedText: "Set your own fees & nonce"
onClicked: root.selectedFeeMode = StatusFeeOption.Type.Custom
}
}
StatusBaseText {
Layout.preferredWidth: parent.width
visible: !d.customMode
text: qsTr("Increased base and priority fee, incentivising miners to confirm more quickly")
color: Theme.palette.baseColor1
font.pixelSize: Theme.tertiaryTextFontSize
elide: Text.ElideMiddle
}
ShapeRectangle {
Layout.preferredWidth: parent.width
Layout.preferredHeight: customLayout.height + customLayout.anchors.margins
visible: d.customMode
ColumnLayout {
id: customLayout
anchors.left: parent.left
anchors.margins: 20
width: parent.width - 2 * anchors.margins
spacing: 16
StatusInput {
id: customBaseFeeInput
readonly property bool displayLowBaseFeeWarning: {
if (!customBaseFeeInput.text) {
return
}
const weiCurrentValue = SQUtils.AmountsArithmetic.fromString(root.currentBaseFee)
const decreasedCurrentValue = SQUtils.AmountsArithmetic.times(weiCurrentValue, SQUtils.AmountsArithmetic.fromString("0.9")) // up to -10% is acceptable
const weiEnteredValue = Utils.gweiToWei(customBaseFeeInput.text)
return decreasedCurrentValue.cmp(weiEnteredValue) === 1
}
readonly property bool displayHighBaseFeeWarning: {
if (!customBaseFeeInput.text) {
return
}
const weiCurrentValue = SQUtils.AmountsArithmetic.fromString(root.currentBaseFee)
const increasedCurrentValue = SQUtils.AmountsArithmetic.times(weiCurrentValue, SQUtils.AmountsArithmetic.fromString("1.2")) // up to 20% higher value is acceptable
const weiEnteredValue = Utils.gweiToWei(customBaseFeeInput.text)
return weiEnteredValue.cmp(increasedCurrentValue) === 1
}
Layout.preferredWidth: parent.width
Layout.topMargin: 20
label: qsTr("Max base fee")
labelIcon: "info"
labelIconColor: Theme.palette.baseColor1
labelIconClickable: true
bottomLabelMessageLeftCmp.color: customBaseFeeInput.displayLowBaseFeeWarning
|| customBaseFeeInput.displayHighBaseFeeWarning?
Theme.palette.miscColor6
: Theme.palette.baseColor1
bottomLabelMessageLeftCmp.text: customBaseFeeInput.displayLowBaseFeeWarning?
qsTr("Lower than necessary (current %1)").arg(Utils.weiToGWei(root.currentBaseFee))
: customBaseFeeInput.displayHighBaseFeeWarning?
qsTr("Higher than necessary (current %1)").arg(Utils.weiToGWei(root.currentBaseFee))
: qsTr("Current: %1 GWEI").arg(Utils.weiToGWei(root.currentBaseFee))
rightPadding: leftPadding
input.rightComponent: StatusBaseText {
text: "GWEI"
color: Theme.palette.baseColor1
}
validators: [
StatusRegularExpressionValidator {
regularExpression: Constants.regularExpressions.positiveRealNumbers
errorMessage: Constants.errorMessages.positiveRealNumbers
}
]
onTextChanged: Qt.callLater(() => {
if (!customBaseFeeInput.valid) {
return
}
d.recalculateCustomBaseFeePrice()
d.recalculateCustomPrice()
})
onLabelIconClicked: d.showAlert(label,
qsTr("When your transaction gets included in the block, any difference between your max base fee and the actual base fee will be refunded.\n"),
qsTr("Note: the ETH amount shown for this value is calculated:\nMax base fee (in GWEI) * Max gas amount"),
"")
}
StatusInput {
id: customPriorityFeeInput
readonly property bool displayHigherPriorityFeeWarning: {
if (!customPriorityFeeInput.text) {
return false
}
const weiCurrentValue = SQUtils.AmountsArithmetic.fromString(root.currentSuggestedMaxPriorityFee)
const weiEnteredValue = Utils.gweiToWei(customPriorityFeeInput.text)
return weiEnteredValue.cmp(weiCurrentValue) === 1
}
readonly property bool displayHigherThanBaseFeeWarning: {
if (!customPriorityFeeInput.text || !customBaseFeeInput.text) {
return false
}
const weiBaseFeeValue = Utils.gweiToWei(customBaseFeeInput.text)
const weiEnteredValue = Utils.gweiToWei(customPriorityFeeInput.text)
return weiEnteredValue.cmp(weiBaseFeeValue) === 1
}
Layout.preferredWidth: parent.width
label: qsTr("Priority fee")
labelIcon: "info"
labelIconColor: Theme.palette.baseColor1
labelIconClickable: true
bottomLabelMessageLeftCmp.color: customPriorityFeeInput.displayHigherThanBaseFeeWarning
|| customPriorityFeeInput.displayHigherPriorityFeeWarning?
Theme.palette.miscColor6
: Theme.palette.baseColor1
bottomLabelMessageLeftCmp.text: customPriorityFeeInput.displayHigherThanBaseFeeWarning?
qsTr("Higher than max base fee: %1 GWEI").arg(customBaseFeeInput.text)
: customPriorityFeeInput.displayHigherPriorityFeeWarning?
qsTr("Higher than necessary (current %1 - %2)").arg(Utils.weiToGWei(root.currentSuggestedMinPriorityFee)).arg(Utils.weiToGWei(root.currentSuggestedMaxPriorityFee))
: qsTr("Current: %1 - %2 GWEI").arg(Utils.weiToGWei(root.currentSuggestedMinPriorityFee)).arg(Utils.weiToGWei(root.currentSuggestedMaxPriorityFee))
rightPadding: leftPadding
input.rightComponent: StatusBaseText {
text: "GWEI"
color: Theme.palette.baseColor1
}
validators: [
StatusRegularExpressionValidator {
regularExpression: Constants.regularExpressions.positiveRealNumbers
errorMessage: Constants.errorMessages.positiveRealNumbers
}
]
onTextChanged: Qt.callLater(() => {
if (!customPriorityFeeInput.valid) {
return
}
d.recalculateCustomPriorityFeePrice()
d.recalculateCustomPrice()
})
onLabelIconClicked: d.showAlert(label,
qsTr("AKA miner tip. A voluntary fee you can add to incentivise miners or validators to prioritise your transaction.\n\nThe higher the tip, the faster your transaction is likely to be processed, especially curing periods of higher network congestion.\n"),
qsTr("Note: the ETH amount shown for this value is calculated: Priority fee (in GWEI) * Max gas amount"),
"")
}
StatusInput {
id: customGasAmountInput
readonly property bool displayTooLowAmountWarning: {
if (!customGasAmountInput.text) {
return false
}
const minValue = SQUtils.AmountsArithmetic.fromString(Constants.minGasForTx)
const enteredValue = SQUtils.AmountsArithmetic.fromString(customGasAmountInput.text)
return minValue.cmp(enteredValue) === 1
}
readonly property bool displayTooHighAmountWarning: {
if (!customGasAmountInput.text) {
return false
}
const maxValue = SQUtils.AmountsArithmetic.fromString(Constants.maxGasForTx)
const enteredValue = SQUtils.AmountsArithmetic.fromString(customGasAmountInput.text)
return enteredValue.cmp(maxValue) === 1
}
Layout.preferredWidth: parent.width
label: qsTr("Max gas amount")
labelIcon: "info"
labelIconColor: Theme.palette.baseColor1
labelIconClickable: true
bottomLabelMessageLeftCmp.color: customGasAmountInput.displayTooLowAmountWarning
|| customGasAmountInput.displayTooHighAmountWarning?
Theme.palette.dangerColor1
: Theme.palette.baseColor1
bottomLabelMessageLeftCmp.text: customGasAmountInput.displayTooLowAmountWarning?
qsTr("Too low (should be between %1 and %2)").arg(Constants.minGasForTx).arg(Constants.maxGasForTx)
: customGasAmountInput.displayTooHighAmountWarning?
qsTr("Too high (should be between %1 and %2)").arg(Constants.minGasForTx).arg(Constants.maxGasForTx)
: qsTr("Current: %1").arg(root.currentGasAmount)
rightPadding: leftPadding
input.rightComponent: StatusBaseText {
text: "UNITS"
color: Theme.palette.baseColor1
}
validators: [
StatusRegularExpressionValidator {
regularExpression: Constants.regularExpressions.wholeNumbers
errorMessage: Constants.errorMessages.wholeNumbers
}
]
onTextChanged: Qt.callLater(() => {
if (!customGasAmountInput.valid) {
return
}
d.recalculateCustomPrice()
})
onLabelIconClicked: d.showAlert(qsTr("Gas amount"),
qsTr("AKA gas limit. Refers to the maximum number of computational steps (or units of gas) that a transaction can consume. It represents the complexity or amount of work required to execute a transaction or smart contract.\n\nThe gas limit is a cap on how much work the transaction can do on the blockchain. If the gas limit is set too low, the transaction may fail due to insufficient gas."),
"",
"")
}
StatusInput {
id: customNonceInput
readonly property bool displayHighNonceWarning: {
if (!customNonceInput.text) {
return false
}
try {
const expectedValue = parseInt(root.currentNonce)
const enteredValue = parseInt(customNonceInput.text)
return enteredValue > expectedValue
} catch (e) {
return false
}
}
Layout.preferredWidth: parent.width
label: qsTr("Nonce")
labelIcon: "info"
labelIconColor: Theme.palette.baseColor1
labelIconClickable: true
bottomLabelMessageLeftCmp.color: customNonceInput.displayHighNonceWarning?
Theme.palette.miscColor6
: Theme.palette.baseColor1
bottomLabelMessageLeftCmp.text: {
if (customNonceInput.displayHighNonceWarning) {
return qsTr("Higher than suggested nonce of %1").arg(root.currentNonce)
}
let lastUsedNonce = root.currentNonce - 1
if (lastUsedNonce < 0) {
lastUsedNonce = "-"
}
return qsTr("Last transaction: %1").arg(lastUsedNonce)
}
rightPadding: leftPadding
input.leftComponent: input.rightComponent // for the reference to the `input`
validators: [
StatusRegularExpressionValidator {
regularExpression: Constants.regularExpressions.wholeNumbers
errorMessage: Constants.errorMessages.wholeNumbers
}
]
onLabelIconClicked: d.showAlert(label,
qsTr("Transaction counter ensuring transactions from your account are processed in the correct order and cant be replayed. Each new transaction increments the nonce by 1, ensuring uniqueness and preventing double-spending.\n\nIf a transaction with a lower nonce is pending, higher nonce transactions will remain in the queue until the earlier one is confirmed."),
"",
"")
}
}
}
StatusButton {
Layout.preferredWidth: parent.width
enabled: !d.customMode
|| customBaseFeeInput.valid &&
customPriorityFeeInput.valid &&
customGasAmountInput.valid &&
customNonceInput.valid &&
!customGasAmountInput.displayTooHighAmountWarning &&
!customGasAmountInput.displayTooLowAmountWarning
text: qsTr("Confirm")
onClicked: root.confirmClicked()
}
}
}
}

View File

@ -8,3 +8,4 @@ TokenSelectorSectionDelegate 1.0 TokenSelectorSectionDelegate.qml
AccountContextMenu 1.0 AccountContextMenu.qml
RecipientView 1.0 RecipientView.qml
SendModalFooter 1.0 SendModalFooter.qml
TransactionSettings 1.0 TransactionSettings.qml

View File

@ -745,7 +745,7 @@ QtObject {
readonly property var estimatedTimeAggregator: FunctionAggregator {
model: !!handler.fetchedPathModel ?
handler.fetchedPathModel: null
initialValue: Constants.TransactionEstimatedTime.Unknown
initialValue: -1
roleName: "estimatedTime"
aggregateFunction: (aggr, value) => aggr < value? value : aggr

View File

@ -15,8 +15,11 @@ StatusDialog {
id: root
property alias acceptBtnText: acceptBtn.text
property alias acceptBtn: acceptBtn
property alias cancelBtn: cancelBtn
property alias alertText: contentTextItem.text
property alias alertLabel: contentTextItem
property alias alertNote: contentNoteItem
property int acceptBtnType: StatusBaseButton.Type.Danger
property StatusAssetSettings asset: StatusAssetSettings {
@ -36,13 +39,23 @@ StatusDialog {
implicitWidth: 400 // by design
topPadding: Theme.padding
bottomPadding: topPadding
contentItem: StatusBaseText {
contentItem: Column {
StatusBaseText {
id: contentTextItem
width: parent.width
font.pixelSize: Theme.primaryTextFontSize
wrapMode: Text.WordWrap
lineHeight: 1.2
}
StatusBaseText {
id: contentNoteItem
visible: false
width: parent.width
font.pixelSize: Theme.primaryTextFontSize
wrapMode: Text.WordWrap
lineHeight: 1.2
}
}
header: StatusDialogHeader {
visible: root.title || root.subtitle
@ -61,6 +74,7 @@ StatusDialog {
rightButtons: ObjectModel {
StatusButton {
id: cancelBtn
text: qsTr("Cancel")
normalColor: "transparent"
@ -75,6 +89,8 @@ StatusDialog {
type: root.acceptBtnType
Component.onCompleted: acceptBtn.forceActiveFocus()
onClicked: {
root.acceptClicked()
close()

View File

@ -664,6 +664,8 @@ QtObject {
readonly property var numerical: /^$|^[0-9]+$/
readonly property var emoji: /\ud83c\udff4(\udb40[\udc61-\udc7a])+\udb40\udc7f|(\ud83c[\udde6-\uddff]){2}|([\#\*0-9]\ufe0f?\u20e3)|(\u00a9|\u00ae|[\u203c\u2049\u20e3\u2122\u2139\u2194-\u2199\u21a9\u21aa\u231a\u231b\u2328\u23cf\u23e9-\u23fa\u24c2\u25aa\u25ab\u25b6\u25c0\u25fb-\u25fe\u2600-\u2604\u260e\u2611\u2614\u2615\u2618\u261d\u2620\u2622\u2623\u2626\u262a\u262e\u262f\u2638-\u263a\u2640\u2642\u2648-\u2653\u265f\u2660\u2663\u2665\u2666\u2668\u267b\u267e\u267f\u2692-\u2697\u2699\u269b\u269c\u26a0\u26a1\u26a7\u26aa\u26ab\u26b0\u26b1\u26bd\u26be\u26c4\u26c5\u26c8\u26ce\u26cf\u26d1\u26d3\u26d4\u26e9\u26ea\u26f0-\u26f5\u26f7-\u26fa\u26fd\u2702\u2705\u2708-\u270d\u270f\u2712\u2714\u2716\u271d\u2721\u2728\u2733\u2734\u2744\u2747\u274c\u274e\u2753-\u2755\u2757\u2763\u2764\u2795-\u2797\u27a1\u27b0\u27bf\u2934\u2935\u2b05-\u2b07\u2b1b\u2b1c\u2b50\u2b55\u3030\u303d\u3297\u3299]|\ud83c[\udc04\udccf\udd70\udd71\udd7e\udd7f\udd8e\udd91-\udd9a\udde6-\uddff\ude01\ude02\ude1a\ude2f\ude32-\ude3a\ude50\ude51\udf00-\udf21\udf24-\udf93\udf96\udf97\udf99-\udf9b\udf9e-\udff0\udff3-\udff5\udff7-\udfff]|\ud83d[\udc00-\udcfd\udcff-\udd3d\udd49-\udd4e\udd50-\udd67\udd6f\udd70\udd73-\udd7a\udd87\udd8a-\udd8d\udd90\udd95\udd96\udda4\udda5\udda8\uddb1\uddb2\uddbc\uddc2-\uddc4\uddd1-\uddd3\udddc-\uddde\udde1\udde3\udde8\uddef\uddf3\uddfa-\ude4f\ude80-\udec5\udecb-\uded2\uded5-\uded7\udedc-\udee5\udee9\udeeb\udeec\udef0\udef3-\udefc\udfe0-\udfeb\udff0]|\ud83e[\udd0c-\udd3a\udd3c-\udd45\udd47-\ude7c\ude80-\ude88\ude90-\udebd\udebf-\udec5\udece-\udedb\udee0-\udee8\udef0-\udef8])((\ud83c[\udffb-\udfff])?(\ud83e[\uddb0-\uddb3])?(\ufe0f?\u200d([\u2000-\u3300]|[\ud83c-\ud83e][\ud000-\udfff])\ufe0f?)?)*/g;
readonly property var asciiWithEmoji: /^[\u00a9\u00ae\u2000-\u3300\ud83c\ud000-\udfff\ud83d\ud000-\udfff\ud83e\ud000-\udfff\u0000-\u007F]+$/
readonly property var wholeNumbers: /^(0|[1-9][0-9]*)$/
readonly property var positiveRealNumbers: /^(0|[1-9][0-9]*)([.,][0-9]+)?$/
}
readonly property QtObject errorMessages: QtObject {
@ -673,6 +675,8 @@ QtObject {
readonly property string alphanumericalWithSpaceRegExp: qsTr("Special characters are not allowed")
readonly property string asciiRegExp: qsTr("Only letters, numbers and ASCII characters allowed")
readonly property string emojRegExp: qsTr("Name is too cool (use A-Z and 0-9, single whitespace, hyphens and underscores only)")
readonly property var wholeNumbers: qsTr("Whole numbers only")
readonly property var positiveRealNumbers: qsTr("Positive real numbers only")
}
readonly property QtObject socialLinkType: QtObject {
@ -892,8 +896,12 @@ QtObject {
readonly property string networkRopsten: "Ropsten"
readonly property string ethToken: "ETH"
readonly property int ethTokenDecimals: 18
readonly property int gweiExponent: 9
readonly property int ethTokenWeiDecimals: 18
readonly property int ethTokenGWeiDecimals: 9
readonly property string minGasForTx: "21000"
readonly property string maxGasForTx: "30000000"
readonly property QtObject networkShortChainNames: QtObject {
readonly property string mainnet: "eth"

View File

@ -980,4 +980,24 @@ QtObject {
return typeName
}
function weiToEth(value) {
return StatusQUtils.AmountsArithmetic.div(StatusQUtils.AmountsArithmetic.fromString(value),
StatusQUtils.AmountsArithmetic.fromNumber(1, Constants.ethTokenWeiDecimals))
}
function ethToWei(value) {
return StatusQUtils.AmountsArithmetic.times(StatusQUtils.AmountsArithmetic.fromString(value),
StatusQUtils.AmountsArithmetic.fromNumber(1, Constants.ethTokenWeiDecimals))
}
function weiToGWei(value) {
return StatusQUtils.AmountsArithmetic.div(StatusQUtils.AmountsArithmetic.fromString(value),
StatusQUtils.AmountsArithmetic.fromNumber(1, Constants.ethTokenGWeiDecimals))
}
function gweiToWei(value) {
return StatusQUtils.AmountsArithmetic.times(StatusQUtils.AmountsArithmetic.fromString(value),
StatusQUtils.AmountsArithmetic.fromNumber(1, Constants.ethTokenGWeiDecimals))
}
}