feat:[UI - Swap] Create swap approve dialog

- create a new UI component
- add it to StoryBook
- add QML tests
- integration will be done as part of
https://github.com/status-im/status-desktop/issues/15443

Fixes #15442
This commit is contained in:
Lukáš Tinkl 2024-07-04 13:55:16 +02:00 committed by Lukáš Tinkl
parent 7b8361ec0f
commit 5665602451
10 changed files with 759 additions and 37 deletions

View File

@ -0,0 +1,181 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import StatusQ 0.1
import Storybook 1.0
import Models 1.0
import AppLayouts.Wallet 1.0
import AppLayouts.Wallet.popups.swap 1.0
import utils 1.0
SplitView {
id: root
Logs { id: logs }
orientation: Qt.Horizontal
property var dialog
function createAndOpenDialog() {
dialog = dlgComponent.createObject(popupBg)
dialog.open()
}
Component.onCompleted: createAndOpenDialog()
QtObject {
id: priv
readonly property var accountsModel: WalletAccountsModel {}
readonly property var selectedAccount: selectedAccountEntry.item
readonly property var networksModel: NetworksModel.flatNetworks
readonly property var selectedNetwork: selectedNetworkEntry.item
}
ModelEntry {
id: selectedAccountEntry
sourceModel: priv.accountsModel
key: "address"
value: ctrlAccount.currentValue
}
ModelEntry {
id: selectedNetworkEntry
sourceModel: priv.networksModel
key: "chainId"
value: ctrlNetwork.currentValue
}
Item {
SplitView.fillWidth: true
SplitView.fillHeight: true
PopupBackground {
id: popupBg
anchors.fill: parent
Button {
anchors.centerIn: parent
text: "Reopen"
onClicked: createAndOpenDialog()
}
Component {
id: dlgComponent
SwapApproveCapModal {
anchors.centerIn: parent
destroyOnClose: true
modal: false
closePolicy: Popup.NoAutoClose
fromTokenSymbol: ctrlFromSymbol.text
fromTokenAmount: ctrlFromAmount.text
fromTokenContractAddress: "0x6B175474E89094C44Da98b954EedeAC495271d0F"
accountName: priv.selectedAccount.name
accountAddress: priv.selectedAccount.address
accountEmoji: priv.selectedAccount.emoji
accountColor: Utils.getColorForId(priv.selectedAccount.colorId)
accountBalanceAmount: "120.55489"
networkShortName: priv.selectedNetwork.shortName
networkName: priv.selectedNetwork.chainName
networkIconPath: Style.svg(priv.selectedNetwork.iconUrl)
networkBlockExplorerUrl: priv.selectedNetwork.blockExplorerUrl
currentCurrency: "EUR"
fiatFees: "1.54"
cryptoFees: "0.001"
estimatedTime: ctrlEstimatedTime.currentValue
loginType: ctrlLoginType.currentIndex
feesLoading: ctrlLoading.checked
}
}
}
}
LogsAndControlsPanel {
SplitView.minimumWidth: 250
SplitView.preferredWidth: 250
logsView.logText: logs.logText
ColumnLayout {
Layout.fillWidth: true
TextField {
Layout.fillWidth: true
id: ctrlFromSymbol
text: "DAI"
placeholderText: "From symbol"
}
TextField {
Layout.fillWidth: true
id: ctrlFromAmount
text: "115.478"
placeholderText: "From amount"
}
Text {
text: "Selected Account"
}
ComboBox {
id: ctrlAccount
textRole: "name"
valueRole: "address"
model: priv.accountsModel
currentIndex: 0
}
Text {
text: "Selected Network"
}
ComboBox {
id: ctrlNetwork
textRole: "chainName"
valueRole: "chainId"
model: priv.networksModel
currentIndex: 2
}
Switch {
id: ctrlLoading
text: "Fees loading"
}
Text {
text: "Login Type"
}
ComboBox {
id: ctrlLoginType
model: Constants.authenticationIconByType
}
Text {
text: "Estimated time"
}
ComboBox {
id: ctrlEstimatedTime
model: [Constants.TransactionEstimatedTime.Unknown,
Constants.TransactionEstimatedTime.LessThanOneMin,
Constants.TransactionEstimatedTime.LessThanThreeMins,
Constants.TransactionEstimatedTime.LessThanFiveMins,
Constants.TransactionEstimatedTime.MoreThanFiveMins
]
displayText: WalletUtils.getLabelForEstimatedTxTime(currentValue)
}
}
}
}
// category: Popups
// https://www.figma.com/design/TS0eQX9dAZXqZtELiwKIoK/Swap---Milestone-1?node-id=3517-435657&t=sRX8mAj4irR1bOuT-0

View File

@ -0,0 +1,279 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtTest 1.15
import QtQml 2.15
import Models 1.0
import StatusQ.Core.Utils 0.1 as SQUtils
import AppLayouts.Wallet 1.0
import AppLayouts.Wallet.popups.swap 1.0
import utils 1.0
Item {
id: root
width: 600
height: 400
Component {
id: componentUnderTest
SwapApproveCapModal {
anchors.centerIn: parent
fromTokenSymbol: "DAI"
fromTokenAmount: "100.07"
fromTokenContractAddress: "0x6B175474E89094C44Da98b954EedeAC495271d0F"
accountName: "Hot wallet (generated)"
accountAddress: "0x7F47C2e98a4BBf5487E6fb082eC2D9Ab0E6d8881"
accountEmoji: "🚗"
accountColor: Utils.getColorForId(Constants.walletAccountColors.primary)
accountBalanceAmount: "120.55489"
networkShortName: Constants.networkShortChainNames.mainnet
networkName: "Mainnet"
networkIconPath: Style.svg("network/Network=Ethereum")
networkBlockExplorerUrl: "https://etherscan.io/"
currentCurrency: "EUR"
fiatFees: "1.54"
cryptoFees: "0.001"
estimatedTime: Constants.TransactionEstimatedTime.Unknown
loginType: Constants.LoginType.Password
}
}
SignalSpy {
id: signalSpyAccepted
target: controlUnderTest
signalName: "accepted"
}
SignalSpy {
id: signalSpyRejected
target: controlUnderTest
signalName: "rejected"
}
property SwapApproveCapModal controlUnderTest: null
TestCase {
name: "SwapApproveCapModal"
when: windowShown
function init() {
controlUnderTest = createTemporaryObject(componentUnderTest, root)
signalSpyAccepted.clear()
signalSpyRejected.clear()
}
function test_basicGeometry() {
verify(!!controlUnderTest)
verify(controlUnderTest.width > 0)
verify(controlUnderTest.height > 0)
}
function test_fromToProps() {
verify(!!controlUnderTest)
controlUnderTest.fromTokenSymbol = "SNT"
controlUnderTest.fromTokenAmount = "1000.123456789"
controlUnderTest.fromTokenContractAddress = "Oxdeadbeef"
// title & subtitle
compare(controlUnderTest.title, qsTr("Approve spending cap"))
compare(controlUnderTest.subtitle, controlUnderTest.serviceProviderURL)
// info box
const headerText = findChild(controlUnderTest.contentItem, "headerText")
verify(!!headerText)
compare(headerText.text, qsTr("Set %1 %2 spending cap in %3 for %4 on %5")
.arg(controlUnderTest.formatBigNumber(controlUnderTest.fromTokenAmount)).arg(controlUnderTest.fromTokenSymbol)
.arg(controlUnderTest.accountName).arg(controlUnderTest.serviceProviderURL).arg(controlUnderTest.networkName))
const fromImageHidden = findChild(controlUnderTest.contentItem, "fromImage")
compare(fromImageHidden.visible, false)
const fromImage = findChild(controlUnderTest.contentItem, "fromImageIdenticon")
verify(!!fromImage)
compare(fromImage.asset.emoji, controlUnderTest.accountEmoji)
compare(fromImage.asset.color, controlUnderTest.accountColor)
const toImage = findChild(controlUnderTest.contentItem, "toImage")
verify(!!toImage)
compare(toImage.image.source, Constants.tokenIcon(controlUnderTest.fromTokenSymbol))
// spending cap box
const spendingCapBox = findChild(controlUnderTest.contentItem, "spendingCapBox")
verify(!!spendingCapBox)
compare(spendingCapBox.caption, qsTr("Set spending cap"))
compare(spendingCapBox.primaryText, controlUnderTest.formatBigNumber(controlUnderTest.fromTokenAmount))
}
function test_accountInfo() {
verify(!!controlUnderTest)
// account box
const accountBox = findChild(controlUnderTest.contentItem, "accountBox")
verify(!!accountBox)
compare(accountBox.caption, qsTr("Account"))
compare(accountBox.primaryText, controlUnderTest.accountName)
compare(accountBox.secondaryText, SQUtils.Utils.elideAndFormatWalletAddress(controlUnderTest.accountAddress))
compare(accountBox.asset.emoji, controlUnderTest.accountEmoji)
compare(accountBox.asset.color, controlUnderTest.accountColor)
}
function test_tokenInfo() {
verify(!!controlUnderTest)
// token box
const tokenBox = findChild(controlUnderTest.contentItem, "tokenBox")
verify(!!tokenBox)
compare(tokenBox.caption, qsTr("Token"))
compare(tokenBox.primaryText, controlUnderTest.fromTokenSymbol)
compare(tokenBox.secondaryText, SQUtils.Utils.elideAndFormatWalletAddress(controlUnderTest.fromTokenContractAddress))
compare(tokenBox.icon, Constants.tokenIcon(controlUnderTest.fromTokenSymbol))
compare(tokenBox.badge, controlUnderTest.networkIconPath)
}
function test_smartContractInfo() {
verify(!!controlUnderTest)
// smart contract box
const smartContractBox = findChild(controlUnderTest.contentItem, "smartContractBox")
verify(!!smartContractBox)
compare(smartContractBox.caption, qsTr("Via smart contract"))
compare(smartContractBox.primaryText, controlUnderTest.serviceProviderName)
compare(smartContractBox.secondaryText, SQUtils.Utils.elideAndFormatWalletAddress(controlUnderTest.serviceProviderContractAddress))
compare(smartContractBox.icon, Style.png("swap/paraswap"))
}
function test_networkInfo() {
verify(!!controlUnderTest)
// network box
const networkBox = findChild(controlUnderTest.contentItem, "networkBox")
verify(!!networkBox)
compare(networkBox.caption, qsTr("Network"))
compare(networkBox.primaryText, controlUnderTest.networkName)
compare(networkBox.icon, controlUnderTest.networkIconPath)
}
function test_feesInfo() {
verify(!!controlUnderTest)
// fees box
const feesBox = findChild(controlUnderTest.contentItem, "feesBox")
verify(!!feesBox)
compare(feesBox.caption, qsTr("Fees"))
compare(feesBox.primaryText, qsTr("Max. fees on %1").arg(controlUnderTest.networkName))
const fiatFeesText = findChild(feesBox, "fiatFeesText")
verify(!!fiatFeesText)
compare(fiatFeesText.text, "%1 %2".arg(controlUnderTest.fiatFees).arg(controlUnderTest.currentCurrency))
const cryptoFeesText = findChild(feesBox, "cryptoFeesText")
verify(!!cryptoFeesText)
compare(cryptoFeesText.text, "%1 ETH".arg(controlUnderTest.cryptoFees))
}
function test_loginType_data() {
return [
{ tag: "password", loginType: Constants.LoginType.Password, iconName: "password" },
{ tag: "touchId", loginType: Constants.LoginType.Biometrics, iconName: "touch-id" },
{ tag: "keycard", loginType: Constants.LoginType.Keycard, iconName: "keycard" }
]
}
function test_loginType(data) {
const loginType = data.loginType
const iconName = data.iconName
verify(!!controlUnderTest)
controlUnderTest.loginType = loginType
const signButton = findChild(controlUnderTest.footer, "signButton")
verify(!!signButton)
compare(signButton.icon.name, iconName)
}
function test_loading() {
verify(!!controlUnderTest)
compare(controlUnderTest.feesLoading, false)
const signButton = findChild(controlUnderTest.footer, "signButton")
verify(!!signButton)
compare(signButton.interactive, true)
const footerFiatFeesText = findChild(controlUnderTest.footer, "footerFiatFeesText")
verify(!!footerFiatFeesText)
compare(footerFiatFeesText.loading, false)
const footerEstimatedTime = findChild(controlUnderTest.footer, "footerEstimatedTime")
verify(!!footerEstimatedTime)
compare(footerEstimatedTime.loading, false)
const fiatFeesText = findChild(controlUnderTest.contentItem, "fiatFeesText")
verify(!!fiatFeesText)
compare(fiatFeesText.loading, false)
const cryptoFeesText = findChild(controlUnderTest.contentItem, "cryptoFeesText")
verify(!!cryptoFeesText)
compare(cryptoFeesText.loading, false)
controlUnderTest.feesLoading = true
compare(signButton.interactive, false)
compare(footerFiatFeesText.loading, true)
compare(footerEstimatedTime.loading, true)
compare(fiatFeesText.loading, true)
compare(cryptoFeesText.loading, true)
}
function test_footerInfo() {
verify(!!controlUnderTest)
const fiatFeesText = findChild(controlUnderTest.footer, "footerFiatFeesText")
verify(!!fiatFeesText)
compare(fiatFeesText.text, "%1 %2".arg(controlUnderTest.fiatFees).arg(controlUnderTest.currentCurrency))
const footerEstimatedTime = findChild(controlUnderTest.footer, "footerEstimatedTime")
verify(!!footerEstimatedTime)
compare(footerEstimatedTime.text, WalletUtils.getLabelForEstimatedTxTime(controlUnderTest.estimatedTime))
}
function test_signButton() {
verify(!!controlUnderTest)
const signButton = findChild(controlUnderTest.footer, "signButton")
verify(!!signButton)
compare(signButton.interactive, true)
signButton.clicked()
compare(signalSpyAccepted.count, 1)
compare(controlUnderTest.opened, false)
compare(controlUnderTest.result, Dialog.Accepted)
}
function test_rejectButton() {
verify(!!controlUnderTest)
const rejectButton = findChild(controlUnderTest.footer, "rejectButton")
verify(!!rejectButton)
compare(rejectButton.interactive, true)
rejectButton.clicked()
compare(signalSpyRejected.count, 1)
compare(controlUnderTest.opened, false)
compare(controlUnderTest.result, Dialog.Rejected)
}
}
}

View File

@ -146,7 +146,7 @@ Item {
function test_feesInfo() {
verify(!!controlUnderTest)
// network box
// fees box
const feesBox = findChild(controlUnderTest.contentItem, "feesBox")
verify(!!feesBox)
@ -192,14 +192,24 @@ Item {
verify(!!signButton)
compare(signButton.interactive, true)
const fiatFeesText = findChild(controlUnderTest.footer, "footerFiatFeesText")
const footerFiatFeesText = findChild(controlUnderTest.footer, "footerFiatFeesText")
verify(!!footerFiatFeesText)
compare(footerFiatFeesText.loading, false)
const fiatFeesText = findChild(controlUnderTest.contentItem, "fiatFeesText")
verify(!!fiatFeesText)
compare(fiatFeesText.loading, false)
const cryptoFeesText = findChild(controlUnderTest.contentItem, "cryptoFeesText")
verify(!!cryptoFeesText)
compare(cryptoFeesText.loading, false)
controlUnderTest.feesLoading = true
compare(signButton.interactive, false)
compare(footerFiatFeesText.loading, true)
compare(fiatFeesText.loading, true)
compare(cryptoFeesText.loading, true)
}
function test_footerInfo() {

View File

@ -96,7 +96,6 @@ QtObject {
locale = locale || Qt.locale()
const numberOfDigits = integralPartLength(num)
let oneArgStrFormat = "%1"
let formattedNumber = num
let multiplier = 1
if(numberOfDigits >=4 && numberOfDigits < 7) { // 1K - 999K
multiplier = 1 / 1000

View File

@ -16,7 +16,7 @@ StatusFlatButton {
required property string symbol
required property string contractAddress
required property string networkName
required property string explorerName
required property string networkShortName
required property string networkBlockExplorerUrl
signal openLink(string link)
@ -27,12 +27,23 @@ StatusFlatButton {
highlighted: moreMenu.opened
onClicked: moreMenu.popup(-moreMenu.width + width, height + 4)
function getExplorerName() {
if (root.networkShortName === Constants.networkShortChainNames.arbitrum) {
return qsTr("Arbiscan")
}
if (root.networkShortName === Constants.networkShortChainNames.optimism) {
return qsTr("Optimistic")
}
return qsTr("Etherscan")
}
StatusMenu {
id: moreMenu
StatusAction {
//: e.g. "View Optimism DAI contract address on Optimistic"
text: qsTr("View %1 %2 contract address on %3").arg(root.networkName).arg(root.symbol).arg(root.explorerName)
//: e.g. "View Optimism (DAI) contract address on Optimistic"
text: !!root.symbol ? qsTr("View %1 %2 contract address on %3").arg(root.networkName).arg(root.symbol).arg(getExplorerName())
: qsTr("View %1 contract address on %2").arg(root.networkName).arg(getExplorerName())
icon.name: "external-link"
onTriggered: {
var link = "%1/%2/%3".arg(root.networkBlockExplorerUrl).arg(Constants.networkExplorerLinks.addressPath).arg(root.contractAddress)

View File

@ -18,6 +18,7 @@ ColumnLayout {
property string badge
property alias asset: listItem.asset
property alias components: listItem.components
property int listItemHeight: 76
StatusBaseText {
text: root.caption
@ -26,7 +27,7 @@ ColumnLayout {
StatusListItem {
id: listItem
Layout.fillWidth: true
Layout.preferredHeight: 76
Layout.preferredHeight: root.listItemHeight
title: root.primaryText
statusListItemTitle.font.pixelSize: Style.current.additionalTextSize
statusListItemTitle.elide: Text.ElideMiddle
@ -34,6 +35,8 @@ ColumnLayout {
statusListItemSubTitle.font.pixelSize: Style.current.additionalTextSize
asset.name: root.icon
asset.isImage: true
asset.bgWidth: 40
asset.bgHeight: 40
border.width: 1
border.color: Theme.palette.baseColor2

View File

@ -49,6 +49,7 @@ StatusDialog {
property color gradientColor: backgroundColor
property url fromImageSource
property alias fromImageSmartIdenticon: fromImageSmartIdenticon
property url toImageSource
property alias headerMainText: headerMainText.text
property alias headerSubTextLayout: headerSubTextLayout.children
@ -67,6 +68,10 @@ StatusDialog {
return resultNum.replace('.', Qt.locale().decimalPoint)
}
function openLinkWithConfirmation(linkUrl) {
Global.openLinkWithConfirmation(linkUrl, SQUtils.StringUtils.extractDomainFromLink(linkUrl))
}
header: StatusDialogHeader {
visible: root.title || root.subtitle
headline.title: root.title
@ -102,7 +107,7 @@ StatusDialog {
Layout.fillWidth: true
Layout.leftMargin: -parent.anchors.leftMargin - scrollView.leftPadding
Layout.rightMargin: -parent.anchors.rightMargin - scrollView.rightPadding
Layout.preferredHeight: 266 // design
Layout.preferredHeight: childrenRect.height + 80 // 40 + 40 top/bottomMargin
gradient: Gradient {
GradientStop { position: 0.0; color: root.gradientColor }
GradientStop { position: 1.0; color: root.backgroundColor }
@ -117,6 +122,15 @@ StatusDialog {
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 4
spacing: -10
StatusSmartIdenticon {
objectName: "fromImageIdenticon"
id: fromImageSmartIdenticon
width: 40
height: 40
asset.bgWidth: 40
asset.bgHeight: 40
visible: !!asset.name
}
StatusRoundedImage {
objectName: "fromImage"
width: 42
@ -124,6 +138,7 @@ StatusDialog {
border.width: 2
border.color: "transparent"
image.source: root.fromImageSource
visible: root.fromImageSource.toString() !== ""
}
StatusRoundedImage {
objectName: "toImage"

View File

@ -0,0 +1,240 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtQml.Models 2.15
import StatusQ 0.1
import StatusQ.Core 0.1
import StatusQ.Core.Utils 0.1 as SQUtils
import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1
import StatusQ.Components 0.1
import AppLayouts.Wallet 1.0
import AppLayouts.Wallet.panels 1.0
import AppLayouts.Wallet.popups 1.0
import shared.controls 1.0
import utils 1.0
SignTransactionModalBase {
id: root
required property string fromTokenSymbol
required property string fromTokenAmount
required property string fromTokenContractAddress
required property string accountName
required property string accountAddress
required property string accountEmoji
required property color accountColor
required property string accountBalanceAmount
required property string networkShortName // e.g. "oeth"
required property string networkName // e.g. "Optimism"
required property string networkIconPath // e.g. `Style.svg("network/Network=Optimism")`
required property string networkBlockExplorerUrl
required property string currentCurrency
required property string fiatFees
required property string cryptoFees
// need to check how this is done in new router, right now it is Enum type
required property int estimatedTime // Constants.TransactionEstimatedTime.XXX enum
property string serviceProviderName: "Paraswap"
property string serviceProviderURL: Constants.swap.paraswapUrl // TODO https://github.com/status-im/status-desktop/issues/15329
property string serviceProviderContractAddress: "0x1bD435F3C054b6e901B7b108a0ab7617C808677b"
title: qsTr("Approve spending cap")
subtitle: serviceProviderURL
gradientColor: Utils.setColorAlpha(root.accountColor, 0.05) // 5% of wallet color
fromImageSmartIdenticon.asset.name: "filled-account"
fromImageSmartIdenticon.asset.emoji: root.accountEmoji
fromImageSmartIdenticon.asset.color: root.accountColor
fromImageSmartIdenticon.asset.isLetterIdenticon: !!root.accountEmoji
toImageSource: Constants.tokenIcon(root.fromTokenSymbol)
//: e.g. "Set 100 DAI spending cap in <account name> for <service> on <network name>"
headerMainText: qsTr("Set %1 %2 spending cap in %3 for %4 on %5").arg(formatBigNumber(root.fromTokenAmount)).arg(root.fromTokenSymbol)
.arg(root.accountName).arg(root.serviceProviderURL).arg(root.networkName)
headerSubTextLayout: [
StatusBaseText {
Layout.fillWidth: true
horizontalAlignment: Qt.AlignHCenter
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
font.pixelSize: Style.current.additionalTextSize
text: qsTr("The smart contract specified will be able to spend up to %1 %2 of your current or future balance.").arg(formatBigNumber(root.fromTokenAmount)).arg(root.fromTokenSymbol)
}
]
headerIconComponent: StatusSmartIdenticon {
asset.name: Style.png("swap/paraswap") // FIXME svg
asset.isImage: true
asset.bgWidth: 40
asset.bgHeight: 40
}
leftFooterContents: ObjectModel {
RowLayout {
Layout.leftMargin: 4
spacing: Style.current.bigPadding
ColumnLayout {
spacing: 2
StatusBaseText {
text: qsTr("Max fees:")
color: Theme.palette.baseColor1
font.pixelSize: Style.current.additionalTextSize
}
StatusTextWithLoadingState {
objectName: "footerFiatFeesText"
text: "%1 %2".arg(formatBigNumber(root.fiatFees)).arg(root.currentCurrency)
loading: root.feesLoading
}
}
ColumnLayout {
spacing: 2
StatusBaseText {
text: qsTr("Est. time:")
color: Theme.palette.baseColor1
font.pixelSize: Style.current.additionalTextSize
}
StatusTextWithLoadingState {
objectName: "footerEstimatedTime"
text: WalletUtils.getLabelForEstimatedTxTime(root.estimatedTime)
loading: root.feesLoading
}
}
}
}
// spending cap
SignInfoBox {
Layout.fillWidth: true
Layout.bottomMargin: Style.current.bigPadding
objectName: "spendingCapBox"
caption: qsTr("Set spending cap")
primaryText: formatBigNumber(root.fromTokenAmount)
listItemHeight: 44
components: [
StatusSmartIdenticon {
asset.name: Constants.tokenIcon(root.fromTokenSymbol)
asset.isImage: true
asset.width: 20
asset.height: 20
},
StatusBaseText {
text: root.fromTokenSymbol
}
]
}
// Account
SignInfoBox {
Layout.fillWidth: true
Layout.bottomMargin: Style.current.bigPadding
objectName: "accountBox"
caption: qsTr("Account")
primaryText: root.accountName
secondaryText: SQUtils.Utils.elideAndFormatWalletAddress(root.accountAddress)
asset.name: "filled-account"
asset.emoji: root.accountEmoji
asset.color: root.accountColor
asset.isLetterIdenticon: !!root.accountEmoji
components: [
InformationTag {
tagPrimaryLabel.text: "%1 %2".arg(formatBigNumber(root.accountBalanceAmount, 2)).arg(root.fromTokenSymbol)
rightComponent: StatusRoundedImage {
width: 16
height: 16
image.source: root.networkIconPath
}
}
]
}
// Token
SignInfoBox {
Layout.fillWidth: true
Layout.bottomMargin: Style.current.bigPadding
objectName: "tokenBox"
caption: qsTr("Token")
primaryText: root.fromTokenSymbol
secondaryText: SQUtils.Utils.elideAndFormatWalletAddress(root.fromTokenContractAddress)
icon: Constants.tokenIcon(root.fromTokenSymbol)
badge: root.networkIconPath
components: [
ContractInfoButtonWithMenu {
symbol: root.fromTokenSymbol
contractAddress: root.fromTokenContractAddress
networkName: root.networkName
networkShortName: root.networkShortName
networkBlockExplorerUrl: root.networkBlockExplorerUrl
onOpenLink: (link) => root.openLinkWithConfirmation(link)
}
]
}
// Smart contract
SignInfoBox {
Layout.fillWidth: true
Layout.bottomMargin: Style.current.bigPadding
objectName: "smartContractBox"
caption: qsTr("Via smart contract")
primaryText: root.serviceProviderName
secondaryText: SQUtils.Utils.elideAndFormatWalletAddress(root.serviceProviderContractAddress)
icon: Style.png("swap/paraswap") // FIXME svg
components: [
ContractInfoButtonWithMenu {
symbol: ""
contractAddress: root.serviceProviderContractAddress
networkName: root.serviceProviderName
networkShortName: root.networkShortName
networkBlockExplorerUrl: root.networkBlockExplorerUrl
onOpenLink: (link) => root.openLinkWithConfirmation(link)
}
]
}
// Network
SignInfoBox {
Layout.fillWidth: true
Layout.bottomMargin: Style.current.bigPadding
objectName: "networkBox"
caption: qsTr("Network")
primaryText: root.networkName
icon: root.networkIconPath
}
// Fees
SignInfoBox {
Layout.fillWidth: true
Layout.bottomMargin: Style.current.bigPadding
objectName: "feesBox"
caption: qsTr("Fees")
primaryText: qsTr("Max. fees on %1").arg(root.networkName)
secondaryText: " "
components: [
ColumnLayout {
spacing: 2
StatusTextWithLoadingState {
objectName: "fiatFeesText"
Layout.alignment: Qt.AlignRight
text: "%1 %2".arg(formatBigNumber(root.fiatFees)).arg(root.currentCurrency)
horizontalAlignment: Text.AlignRight
font.pixelSize: Style.current.additionalTextSize
loading: root.feesLoading
}
StatusTextWithLoadingState {
objectName: "cryptoFeesText"
Layout.alignment: Qt.AlignRight
text: "%1 ETH".arg(formatBigNumber(root.cryptoFees))
horizontalAlignment: Text.AlignRight
font.pixelSize: Style.current.additionalTextSize
customColor: Theme.palette.baseColor1
loading: root.feesLoading
}
}
]
}
}

View File

@ -67,7 +67,7 @@ SignTransactionModalBase {
normalColor: Theme.palette.directColor1
linkColor: Theme.palette.directColor1
font.weight: Font.Normal
onClicked: d.openLinkWithConfirmation(root.serviceProviderURL)
onClicked: root.openLinkWithConfirmation(root.serviceProviderURL)
},
StatusIcon {
Layout.leftMargin: -2
@ -79,24 +79,6 @@ SignTransactionModalBase {
]
infoTagText: qsTr("Review all details before signing")
readonly property var d: QtObject {
id: d
function openLinkWithConfirmation(linkUrl) {
Global.openLinkWithConfirmation(linkUrl, SQUtils.StringUtils.extractDomainFromLink(linkUrl))
}
function getExplorerName() {
if (root.networkShortName === Constants.networkShortChainNames.arbitrum) {
return qsTr("Arbiscan")
}
if (root.networkShortName === Constants.networkShortChainNames.optimism) {
return qsTr("Optimistic")
}
return qsTr("Etherscan")
}
}
headerIconComponent: StatusSmartIdenticon {
asset.name: "filled-account"
asset.emoji: root.accountEmoji
@ -158,9 +140,9 @@ SignTransactionModalBase {
symbol: root.fromTokenSymbol
contractAddress: root.fromTokenContractAddress
networkName: root.networkName
explorerName: d.getExplorerName()
networkShortName: root.networkShortName
networkBlockExplorerUrl: root.networkBlockExplorerUrl
onOpenLink: (link) => d.openLinkWithConfirmation(link)
onOpenLink: (link) => root.openLinkWithConfirmation(link)
}
]
}
@ -180,9 +162,9 @@ SignTransactionModalBase {
symbol: root.toTokenSymbol
contractAddress: root.toTokenContractAddress
networkName: root.networkName
explorerName: d.getExplorerName()
networkShortName: root.networkShortName
networkBlockExplorerUrl: root.networkBlockExplorerUrl
onOpenLink: (link) => d.openLinkWithConfirmation(link)
onOpenLink: (link) => root.openLinkWithConfirmation(link)
}
]
}
@ -195,12 +177,10 @@ SignTransactionModalBase {
caption: qsTr("In account")
primaryText: root.accountName
secondaryText: SQUtils.Utils.elideAndFormatWalletAddress(root.accountAddress)
asset.name: !!root.accountEmoji ? "" : "filled-account"
asset.name: "filled-account"
asset.emoji: root.accountEmoji
asset.color: root.accountColor
asset.isLetterIdenticon: !!root.accountEmoji
asset.bgWidth: 40
asset.bgHeight: 40
}
// Network
@ -224,20 +204,22 @@ SignTransactionModalBase {
components: [
ColumnLayout {
spacing: 2
StatusBaseText {
StatusTextWithLoadingState {
objectName: "fiatFeesText"
Layout.alignment: Qt.AlignRight
text: "%1 %2".arg(formatBigNumber(root.fiatFees)).arg(root.currentCurrency)
horizontalAlignment: Text.AlignRight
font.pixelSize: Style.current.additionalTextSize
loading: root.feesLoading
}
StatusBaseText {
StatusTextWithLoadingState {
objectName: "cryptoFeesText"
Layout.alignment: Qt.AlignRight
text: "%1 ETH".arg(formatBigNumber(root.cryptoFees))
horizontalAlignment: Text.AlignRight
font.pixelSize: Style.current.additionalTextSize
color: Theme.palette.baseColor1
customColor: Theme.palette.baseColor1
loading: root.feesLoading
}
}
]

View File

@ -5,4 +5,6 @@ SwapOutputData 1.0 SwapOutputData.qml
SwapSignApprovePopup 1.0 SwapSignApprovePopup.qml
SwapSignApproveInputForm 1.0 SwapSignApproveInputForm.qml
SwapSignApproveAdaptor 1.0 SwapSignApproveAdaptor.qml
SwapApproveCapModal 1.0 SwapApproveCapModal.qml
SwapSignModal 1.0 SwapSignModal.qml