feat(SwapModal): Integrate Sign and Approve modals

- remove old popup and related components/adaptors
- simplify the API of the Sign/Approve modals to take pre-formatted
amounts as strings
- correct login/auth icon
- SB: make it possible to open the new secondary modals
- SB: fix the `fetchSuggestedRoutes` response handling, pass around the
`uuid` and inject it to the mocked suggested routes
- adjust SB pages and tests

Fixes #15443
This commit is contained in:
Lukáš Tinkl 2024-07-10 20:35:24 +02:00 committed by Lukáš Tinkl
parent 52b1cc3824
commit 38d155f590
29 changed files with 214 additions and 745 deletions

View File

@ -83,16 +83,15 @@ SplitView {
accountAddress: priv.selectedAccount.address
accountEmoji: priv.selectedAccount.emoji
accountColor: Utils.getColorForId(priv.selectedAccount.colorId)
accountBalanceAmount: "120.55489"
accountBalanceFormatted: "120.55489 USD"
networkShortName: priv.selectedNetwork.shortName
networkName: priv.selectedNetwork.chainName
networkIconPath: Style.svg(priv.selectedNetwork.iconUrl)
networkBlockExplorerUrl: priv.selectedNetwork.blockExplorerUrl
networkBlockExplorerUrl: priv.selectedNetwork.blockExplorerURL
currentCurrency: "EUR"
fiatFees: "1.54"
cryptoFees: "0.001"
fiatFees: "1.54 USD"
cryptoFees: "0.001 ETH"
estimatedTime: ctrlEstimatedTime.currentValue
loginType: ctrlLoginType.currentIndex

View File

@ -14,6 +14,7 @@ import utils 1.0
import Storybook 1.0
import Models 1.0
import mainui 1.0
import shared.stores 1.0
import AppLayouts.Wallet.stores 1.0
import AppLayouts.Wallet.popups.swap 1.0
@ -33,7 +34,7 @@ SplitView {
swapModal.createObject(root)
}
readonly property SwapTransactionRoutes dummySwapTransactionRoutes: SwapTransactionRoutes{}
readonly property SwapTransactionRoutes dummySwapTransactionRoutes: SwapTransactionRoutes {}
property string uuid
@ -46,6 +47,12 @@ SplitView {
}
}
Popups {
popupParent: root
rootStore: QtObject {}
communityTokensStore: QtObject {}
}
PopupBackground {
id: popupBg
@ -66,7 +73,7 @@ SplitView {
SwapStore {
id: dSwapStore
signal suggestedRoutesReady(var txRoutes)
signal transactionSent(var chainId,var txHash, var uuid, var error)
signal transactionSent(var chainId, var txHash, var uuid, var error)
signal transactionSendingComplete(var txHash, var success)
readonly property var accounts: WalletAccountsModel {}
@ -79,6 +86,7 @@ SplitView {
accountTo, "amount = ", amount, " tokenFrom = ", tokenFrom, " tokenTo = ", tokenTo,
" disabledFromChainIDs = ", disabledFromChainIDs, " disabledToChainIDs = ", disabledToChainIDs,
" preferredChainIDs = ", preferredChainIDs, " sendType =", sendType, " lockedInAmounts = ", lockedInAmounts)
d.uuid = uuid
fetchSuggestedRoutesSignal()
}
function authenticateAndTransfer(uuid, accountFrom, accountTo, tokenFrom,
@ -89,9 +97,6 @@ SplitView {
d.uuid = uuid
authenticateAndTransferSignal()
}
function getWei2Eth(wei, decimals) {
return wei/(10**decimals)
}
// only for testing
signal fetchSuggestedRoutesSignal()
@ -127,7 +132,7 @@ SplitView {
}
currencyStore: CurrenciesStore {}
swapFormData: SwapInputParamsForm {
defaultToTokenKey: "STT"
defaultToTokenKey: Constants.swap.testStatusTokenKey
onSelectedAccountAddressChanged: {
if (selectedAccountAddress !== accountComboBox.currentValue)
accountComboBox.currentIndex = accountComboBox.indexOfValue(selectedAccountAddress)
@ -146,6 +151,7 @@ SplitView {
destroyOnClose: true
swapInputParamsForm: adaptor.swapFormData
swapAdaptor: adaptor
loginType: Constants.LoginType.Password
Binding {
target: swapInputParamsForm
property: "fromTokensKey"
@ -190,7 +196,7 @@ SplitView {
Connections {
target: approveTxButton
function onClicked() {
modal.swapAdaptor.sendApproveTx()
modal.swapAdaptor.sendApproveTx()
}
}
}
@ -348,7 +354,9 @@ SplitView {
Button {
text: "emit no routes found event"
onClicked: {
dSwapStore.suggestedRoutesReady(d.dummySwapTransactionRoutes.txNoRoutes)
const txRoutes = d.dummySwapTransactionRoutes.txNoRoutes
txRoutes.uuid = d.uuid
dSwapStore.suggestedRoutesReady(txRoutes)
}
visible: advancedSignalsCheckBox.checked
}
@ -356,7 +364,9 @@ SplitView {
Button {
text: "emit no approval needed route"
onClicked: {
dSwapStore.suggestedRoutesReady(d.dummySwapTransactionRoutes.txHasRouteNoApproval)
const txRoutes = d.dummySwapTransactionRoutes.txHasRouteNoApproval
txRoutes.uuid = d.uuid
dSwapStore.suggestedRoutesReady(txRoutes)
}
visible: advancedSignalsCheckBox.checked
}
@ -364,7 +374,9 @@ SplitView {
Button {
text: "emit approval needed route"
onClicked: {
dSwapStore.suggestedRoutesReady(d.dummySwapTransactionRoutes.txHasRoutesApprovalNeeded)
const txRoutes = d.dummySwapTransactionRoutes.txHasRoutesApprovalNeeded
txRoutes.uuid = d.uuid
dSwapStore.suggestedRoutesReady(txRoutes)
}
visible: advancedSignalsCheckBox.checked
}

View File

@ -1,113 +0,0 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import Storybook 1.0
import Models 1.0
import shared.stores 1.0
import AppLayouts.Wallet.stores 1.0
import AppLayouts.Wallet.popups.swap 1.0
SplitView {
id: root
Logs { id: logs }
orientation: Qt.Horizontal
QtObject {
id: d
function launchPopup() {
swapSignApproveModal.createObject(root)
}
}
PopupBackground {
id: popupBg
SplitView.fillWidth: true
SplitView.fillHeight: true
Button {
id: reopenButton
anchors.centerIn: parent
text: "Reopen"
enabled: !swapSignApproveModal.visible
onClicked: d.launchPopup()
}
Component.onCompleted: d.launchPopup()
Component {
id: swapSignApproveModal
SwapSignApprovePopup {
id: modal
visible: true
modal: false
closePolicy: Popup.CloseOnEscape
destroyOnClose: true
loading: loadingCheckBox.checked
swapSignApproveInputForm: SwapSignApproveInputForm {
selectedAccountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240"
selectedNetworkChainId: 11155111
estimatedTime: 3
swapProviderName: "ParaSwap"
approvalGasFees: "2.789231893824e-06"
approvalAmountRequired: "10000000000000"
approvalContractAddress: "0x216b4b4ba9f3e719726886d34a177484278bfcae"
fromTokensKey: "DAI"
toTokensKey: "ETH"
fromTokensAmount: "0.00100000000000003"
toTokensAmount: "0.02925100"
selectedSlippage: 0.5
swapFees: 2.789231893824e-06
}
adaptor: SwapSignApproveAdaptor {
swapStore: SwapStore {
readonly property var accounts: WalletAccountsModel {}
readonly property var flatNetworks: NetworksModel.flatNetworks
}
walletAssetsStore: WalletAssetsStore {
id: thisWalletAssetStore
walletTokensStore: TokensStore {
readonly property var plainTokensBySymbolModel: TokensBySymbolModel {}
getDisplayAssetsBelowBalanceThresholdDisplayAmount: () => 0
}
readonly property var baseGroupedAccountAssetModel: GroupedAccountsAssetsModel {}
assetsWithFilteredBalances: thisWalletAssetStore.groupedAccountsAssetsModel
}
currencyStore: CurrenciesStore {}
inputFormData: modal.swapSignApproveInputForm
}
txType: isApprovalTx.checked ? SwapSignApprovePopup.TxType.Approve : SwapSignApprovePopup.TxType.Swap
}
}
}
Pane {
id: rightPanel
SplitView.minimumWidth: 300
SplitView.preferredWidth: 300
SplitView.minimumHeight: 300
ColumnLayout {
spacing: 10
CheckBox {
id: loadingCheckBox
text: "loading"
checked: false
}
CheckBox {
id: isApprovalTx
text: "approve tx"
checked: false
}
}
}
}
// category: Popups

View File

@ -90,11 +90,10 @@ SplitView {
networkShortName: priv.selectedNetwork.shortName
networkName: priv.selectedNetwork.chainName
networkIconPath: Style.svg(priv.selectedNetwork.iconUrl)
networkBlockExplorerUrl: priv.selectedNetwork.blockExplorerUrl
networkBlockExplorerUrl: priv.selectedNetwork.blockExplorerURL
currentCurrency: "EUR"
fiatFees: "1.54"
cryptoFees: "0.001"
fiatFees: "1.54 EUR"
cryptoFees: "0.001 ETH"
slippage: 0.5
loginType: ctrlLoginType.currentIndex

View File

@ -30,16 +30,15 @@ Item {
accountAddress: "0x7F47C2e98a4BBf5487E6fb082eC2D9Ab0E6d8881"
accountEmoji: "🚗"
accountColor: Utils.getColorForId(Constants.walletAccountColors.primary)
accountBalanceAmount: "120.55489"
accountBalanceFormatted: "120.55489 DAI"
networkShortName: Constants.networkShortChainNames.mainnet
networkName: "Mainnet"
networkIconPath: Style.svg("network/Network=Ethereum")
networkBlockExplorerUrl: "https://etherscan.io/"
currentCurrency: "EUR"
fiatFees: "1.54"
cryptoFees: "0.001"
fiatFees: "1.54 USD"
cryptoFees: "0.001 ETH"
estimatedTime: Constants.TransactionEstimatedTime.Unknown
loginType: Constants.LoginType.Password
@ -125,8 +124,16 @@ Item {
compare(accountBox.asset.color, controlUnderTest.accountColor)
}
function test_tokenInfo() {
function test_tokenInfo_data() {
return [
{tag: "ETH", fromTokenSymbol: "ETH"},
{tag: "DAI", fromTokenSymbol: "DAI"},
]
}
function test_tokenInfo(data) {
verify(!!controlUnderTest)
controlUnderTest.fromTokenSymbol = data.fromTokenSymbol
// token box
const tokenBox = findChild(controlUnderTest.contentItem, "tokenBox")
@ -134,7 +141,9 @@ Item {
compare(tokenBox.caption, qsTr("Token"))
compare(tokenBox.primaryText, controlUnderTest.fromTokenSymbol)
compare(tokenBox.secondaryText, SQUtils.Utils.elideAndFormatWalletAddress(controlUnderTest.fromTokenContractAddress))
compare(tokenBox.secondaryText,
controlUnderTest.fromTokenSymbol === "ETH" ? ""
: SQUtils.Utils.elideAndFormatWalletAddress(controlUnderTest.fromTokenContractAddress))
compare(tokenBox.icon, Constants.tokenIcon(controlUnderTest.fromTokenSymbol))
compare(tokenBox.badge, controlUnderTest.networkIconPath)
}
@ -176,11 +185,11 @@ Item {
const fiatFeesText = findChild(feesBox, "fiatFeesText")
verify(!!fiatFeesText)
compare(fiatFeesText.text, "%1 %2".arg(controlUnderTest.fiatFees).arg(controlUnderTest.currentCurrency))
compare(fiatFeesText.text, controlUnderTest.fiatFees)
const cryptoFeesText = findChild(feesBox, "cryptoFeesText")
verify(!!cryptoFeesText)
compare(cryptoFeesText.text, "%1 ETH".arg(controlUnderTest.cryptoFees))
compare(cryptoFeesText.text, controlUnderTest.cryptoFees)
}
function test_loginType_data() {
@ -243,7 +252,7 @@ Item {
const fiatFeesText = findChild(controlUnderTest.footer, "footerFiatFeesText")
verify(!!fiatFeesText)
compare(fiatFeesText.text, "%1 %2".arg(controlUnderTest.fiatFees).arg(controlUnderTest.currentCurrency))
compare(fiatFeesText.text, controlUnderTest.fiatFees)
const footerEstimatedTime = findChild(controlUnderTest.footer, "footerEstimatedTime")
verify(!!footerEstimatedTime)

View File

@ -74,7 +74,7 @@ Item {
}
property SwapInputParamsForm swapFormData: SwapInputParamsForm {
defaultToTokenKey: "STT"
defaultToTokenKey: Constants.swap.testStatusTokenKey
}
Component {
@ -82,6 +82,7 @@ Item {
SwapModal {
swapInputParamsForm: root.swapFormData
swapAdaptor: root.swapAdaptor
loginType: Constants.LoginType.Password
}
}

View File

@ -39,9 +39,8 @@ Item {
networkIconPath: Style.svg("network/Network=Ethereum")
networkBlockExplorerUrl: "https://etherscan.io/"
currentCurrency: "EUR"
fiatFees: "1.54"
cryptoFees: "0.001"
fiatFees: "1.54 EUR"
cryptoFees: "0.001 ETH"
slippage: 0.2
loginType: Constants.LoginType.Password
@ -78,23 +77,30 @@ Item {
verify(controlUnderTest.height > 0)
}
function test_fromToProps() {
function test_fromToProps_data() {
return [
{tag: "ETH", toTokenSymbol: "ETH"},
{tag: "DAI", toTokenSymbol: "DAI"},
]
}
function test_fromToProps(data) {
verify(!!controlUnderTest)
controlUnderTest.fromTokenSymbol = "SNT"
controlUnderTest.fromTokenAmount = "1000.123456789"
controlUnderTest.fromTokenContractAddress = "Oxdeadbeef"
controlUnderTest.toTokenSymbol = "ETH"
controlUnderTest.toTokenSymbol = data.toTokenSymbol
controlUnderTest.toTokenAmount = "1.42"
controlUnderTest.toTokenContractAddress = "0xdeadcaff"
// title & subtitle
compare(controlUnderTest.title, qsTr("Sign Swap"))
compare(controlUnderTest.subtitle, qsTr("1000.1235 SNT to 1.42 ETH")) // subtitle rounded amounts
compare(controlUnderTest.subtitle, qsTr("1000.1235 SNT to 1.42 %2").arg(data.toTokenSymbol)) // subtitle rounded amounts
// info box
const headerText = findChild(controlUnderTest.contentItem, "headerText")
verify(!!headerText)
compare(headerText.text, qsTr("Swap 1000.123456789 SNT to 1.42 ETH in %1 on %2").arg(controlUnderTest.accountName).arg(controlUnderTest.networkName))
compare(headerText.text, qsTr("Swap 1000.123456789 SNT to 1.42 %3 in %1 on %2").arg(controlUnderTest.accountName).arg(controlUnderTest.networkName).arg(data.toTokenSymbol))
const fromImage = findChild(controlUnderTest.contentItem, "fromImageIdenticon")
verify(!!fromImage)
compare(fromImage.asset.name, Constants.tokenIcon(controlUnderTest.fromTokenSymbol))
@ -114,7 +120,9 @@ Item {
verify(!!receiveBox)
compare(receiveBox.caption, qsTr("Receive"))
compare(receiveBox.primaryText, "%1 %2".arg(controlUnderTest.toTokenAmount).arg(controlUnderTest.toTokenSymbol))
compare(receiveBox.secondaryText, SQUtils.Utils.elideAndFormatWalletAddress(controlUnderTest.toTokenContractAddress))
compare(receiveBox.secondaryText,
data.toTokenSymbol === "ETH" ? ""
: SQUtils.Utils.elideAndFormatWalletAddress(controlUnderTest.toTokenContractAddress))
}
function test_accountInfo() {
@ -155,11 +163,11 @@ Item {
const fiatFeesText = findChild(feesBox, "fiatFeesText")
verify(!!fiatFeesText)
compare(fiatFeesText.text, "%1 %2".arg(controlUnderTest.fiatFees).arg(controlUnderTest.currentCurrency))
compare(fiatFeesText.text, controlUnderTest.fiatFees)
const cryptoFeesText = findChild(feesBox, "cryptoFeesText")
verify(!!cryptoFeesText)
compare(cryptoFeesText.text, "%1 ETH".arg(controlUnderTest.cryptoFees))
compare(cryptoFeesText.text, controlUnderTest.cryptoFees)
}
function test_loginType_data() {
@ -217,7 +225,7 @@ Item {
const fiatFeesText = findChild(controlUnderTest.footer, "footerFiatFeesText")
verify(!!fiatFeesText)
compare(fiatFeesText.text, "%1 %2".arg(controlUnderTest.fiatFees).arg(controlUnderTest.currentCurrency))
compare(fiatFeesText.text, controlUnderTest.fiatFees)
const maxSlippageText = findChild(controlUnderTest.footer, "footerMaxSlippageText")
verify(!!maxSlippageText)

View File

@ -57,7 +57,7 @@ QtObject {
{
chainId: 1,
chainName: "Mainnet",
blockExplorerUrl: "https://etherscan.io/",
blockExplorerURL: "https://etherscan.io/",
iconUrl: "network/Network=Ethereum",
chainColor: "#627EEA",
shortName: "eth",
@ -71,7 +71,7 @@ QtObject {
{
chainId: 11155111,
chainName: "Sepolia Mainnet",
blockExplorerUrl: "https://sepolia.etherscan.io/",
blockExplorerURL: "https://sepolia.etherscan.io/",
iconUrl: "network/Network=Ethereum",
chainColor: "#627EEA",
shortName: "eth",
@ -85,7 +85,7 @@ QtObject {
{
chainId: 10,
chainName: "Optimism",
blockExplorerUrl: "https://optimistic.etherscan.io",
blockExplorerURL: "https://optimistic.etherscan.io",
iconUrl: "network/Network=Optimism",
chainColor: "#E90101",
shortName: "oeth",
@ -99,7 +99,7 @@ QtObject {
{
chainId: 420,
chainName: "Optimism Goerli Testnet",
blockExplorerUrl: "https://goerli-optimism.etherscan.io/",
blockExplorerURL: "https://goerli-optimism.etherscan.io/",
iconUrl: "network/Network=Optimism",
chainColor: "#939BA1",
shortName: "goOpt",
@ -113,7 +113,7 @@ QtObject {
{
chainId: 42161,
chainName: "Arbitrum",
blockExplorerUrl: "https://arbiscan.io/",
blockExplorerURL: "https://arbiscan.io/",
iconUrl: "network/Network=Arbitrum",
chainColor: "#51D0F0",
shortName: "arb1",
@ -127,7 +127,7 @@ QtObject {
{
chainId: 421613,
chainName: "Arbitrum Goerli",
blockExplorerUrl: "https://goerli.arbiscan.io/",
blockExplorerURL: "https://goerli.arbiscan.io/",
iconUrl: "network/Network=Arbitrum",
chainColor: "#939BA1",
shortName: "goArb",

View File

@ -28,7 +28,7 @@ QtObject {
totalTokenFees:-0.004508663259772343,
totalTime:2
},
amountToReceive: 379295138519599728000,
amountToReceive: "379295138519599728000",
toNetworksModel: root.toModel
})
@ -39,7 +39,7 @@ QtObject {
totalTokenFees:-0.004508663259772343,
totalTime:2
},
amountToReceive: 379295138519599728000,
amountToReceive: "379295138519599728000",
toNetworksModel: root.toModel
})
@ -52,10 +52,6 @@ QtObject {
}
}
property ListModel goodRouteNoApprovalNeeded: ListModel {
function rowCount() {
return count
}
Component.onCompleted: append(suggestesRoutes)
property var suggestesRoutes: [
@ -94,10 +90,6 @@ QtObject {
]
}
property ListModel goodRouteApprovalNeeded: ListModel {
function rowCount() {
return count
}
Component.onCompleted: append(suggestesRoutes)
property var suggestesRoutes: [
@ -129,16 +121,12 @@ QtObject {
isFirstBridgeTx:true,
approvalRequired:true,
approvalGasFees:0.100000000000000007,
approvalAmountRequired:"0",
approvalAmountRequired:"100000000000000000000",
approvalContractAddress:"0x216b4b4ba9f3e719726886d34a177484278bfcae"
}
}
]
}
property ListModel noRoutes: ListModel {
function rowCount() {
return count
}
}
property ListModel noRoutes: ListModel {}
}

View File

@ -27,6 +27,10 @@ QtObject {
return parseFloat(balance)
}
function getCryptoValue(balance, symbol) {
return balance
}
function getCurrencyAmount(amount, symbol) {
return ({
amount: amount,

View File

@ -73,7 +73,7 @@ QtObject {
/*!
\qmlmethod AmountsArithmetic::fromString(numStr)
\brief Construct an amount from a textual representation.
\brief Construct an amount from a textual or numeric representation
The constructed amount is of arbitrary precision. Using ordinary js
numbers, precision might not be sufficient:
@ -86,12 +86,12 @@ QtObject {
The obtained amount can be multiplied or compared.
Provided number is assumed to be an amount in basic units, an integer.
Provided number is assumed to be an amount in basic units, e.g. an integer or a float number.
returns NaN in case the conversion fails.
*/
function fromString(numStr) {
console.assert(typeof numStr === "string")
console.assert(typeof numStr === "string" || typeof numStr === "number")
try {
return new Big.Big(numStr)
} catch(e) {

View File

@ -129,7 +129,7 @@ QObject {
FastExpressionFilter {
function isPresentOnEnabledNetworks(addressPerChain) {
if(!addressPerChain)
return true
return true
return !!ModelUtils.getFirstModelEntryIf(
addressPerChain,
(addPerChain) => {

View File

@ -1,28 +0,0 @@
import QtQuick 2.15
import QtQuick.Layouts 1.15
import StatusQ.Core 0.1
import StatusQ.Controls 0.1
import StatusQ.Core.Theme 0.1
ColumnLayout {
id: root
property alias titleText: titleText.text
property alias infoText: infoText.text
property alias loading: infoText.loading
StatusBaseText {
id: titleText
Layout.fillWidth: true
elide: Text.ElideRight
color: Theme.palette.baseColor1
font.pixelSize: 13
}
StatusTextWithLoadingState {
id: infoText
Layout.fillWidth: true
elide: Text.ElideRight
font.pixelSize: 13
}
}

View File

@ -18,6 +18,5 @@ StatusDateRangePicker 1.0 StatusDateRangePicker.qml
StatusNetworkListItemTag 1.0 StatusNetworkListItemTag.qml
StatusTxProgressBar 1.0 StatusTxProgressBar.qml
SwapExchangeButton 1.0 SwapExchangeButton.qml
SwapModalFooterInfoComponent 1.0 SwapModalFooterInfoComponent.qml
TokenSelector 1.0 TokenSelector.qml
TokenSelectorNew 1.0 TokenSelectorNew.qml

View File

@ -28,10 +28,10 @@ StatusFlatButton {
onClicked: moreMenu.popup(-moreMenu.width + width, height + 4)
function getExplorerName() {
if (root.networkShortName === Constants.networkShortChainNames.arbitrum) {
if (root.networkShortName === Constants.networkShortChainNames.arbitrum || root.networkShortName === Constants.networkShortChainNames.arbitrum_goerli) {
return qsTr("Arbiscan")
}
if (root.networkShortName === Constants.networkShortChainNames.optimism) {
if (root.networkShortName === Constants.networkShortChainNames.optimism || root.networkShortName === Constants.networkShortChainNames.optimism_goerli) {
return qsTr("Optimistic")
}
return qsTr("Etherscan")
@ -54,6 +54,7 @@ StatusFlatButton {
text: qsTr("Copy contract address")
successText: qsTr("Copied")
icon.name: "copy"
autoDismissMenu: true
onTriggered: Utils.copyToClipboard(root.contractAddress)
}
}

View File

@ -13,6 +13,7 @@ ColumnLayout {
property string caption
property string primaryText
property color primaryTextCustomColor: Theme.palette.directColor1
property string secondaryText
property string icon
property string badge
@ -31,6 +32,7 @@ ColumnLayout {
title: root.primaryText
statusListItemTitle.font.pixelSize: Style.current.additionalTextSize
statusListItemTitle.elide: Text.ElideMiddle
statusListItemTitle.customColor: root.primaryTextCustomColor
subTitle: root.secondaryText
statusListItemSubTitle.font.pixelSize: Style.current.additionalTextSize
asset.name: root.icon

View File

@ -58,6 +58,7 @@ Control {
readonly property int rawValueMultiplierIndex: amountToSendInput.multiplierIndex
readonly property bool valueValid: amountToSendInput.inputNumberValid
readonly property bool amountEnteredGreaterThanBalance: value > maxSendButton.maxSafeValue
readonly property string accountBalanceFormatted: root.currencyStore.formatCurrencyAmount(d.maxCryptoBalance, d.selectedHolding.symbol)
// visual properties
property int swapExchangeButtonWidth: 44
@ -103,7 +104,7 @@ Control {
showAllTokens: true
enabledChainIds: root.selectedNetworkChainId !== -1 ? [root.selectedNetworkChainId] : []
accountAddress: root.selectedAccountAddress || ""
accountAddress: root.selectedAccountAddress
searchString: holdingSelector.searchString
}

View File

@ -43,6 +43,7 @@ StatusDialog {
id: signButton
interactive: !root.feesLoading && root.signButtonEnabled
icon.name: Constants.authenticationIconByType[root.loginType]
disabledColor: Theme.palette.directColor8
text: qsTr("Sign")
onClicked: root.accept() // close and emit accepted() signal
}

View File

@ -28,14 +28,13 @@ SignTransactionModalBase {
required property string accountAddress
required property string accountEmoji
required property color accountColor
required property string accountBalanceAmount
required property string accountBalanceFormatted
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
@ -44,6 +43,7 @@ SignTransactionModalBase {
property string serviceProviderName: "Paraswap"
property string serviceProviderURL: Constants.swap.paraswapUrl // TODO https://github.com/status-im/status-desktop/issues/15329
property string serviceProviderContractAddress: "0x1bD435F3C054b6e901B7b108a0ab7617C808677b"
property string serviceProviderIcon: Style.png("swap/%1".arg(Constants.swap.paraswapIcon)) // FIXME svg
title: qsTr("Approve spending cap")
subtitle: serviceProviderURL
@ -69,7 +69,7 @@ SignTransactionModalBase {
]
headerIconComponent: StatusSmartIdenticon {
asset.name: Style.png("swap/paraswap") // FIXME svg
asset.name: root.serviceProviderIcon
asset.isImage: true
asset.bgWidth: 40
asset.bgHeight: 40
@ -88,7 +88,7 @@ SignTransactionModalBase {
}
StatusTextWithLoadingState {
objectName: "footerFiatFeesText"
text: "%1 %2".arg(formatBigNumber(root.fiatFees)).arg(root.currentCurrency)
text: loading ? Constants.dummyText : root.fiatFees
loading: root.feesLoading
}
}
@ -143,7 +143,7 @@ SignTransactionModalBase {
asset.isLetterIdenticon: !!root.accountEmoji
components: [
InformationTag {
tagPrimaryLabel.text: "%1 %2".arg(formatBigNumber(root.accountBalanceAmount, 2)).arg(root.fromTokenSymbol)
tagPrimaryLabel.text: root.accountBalanceFormatted
rightComponent: StatusRoundedImage {
width: 16
height: 16
@ -160,11 +160,12 @@ SignTransactionModalBase {
objectName: "tokenBox"
caption: qsTr("Token")
primaryText: root.fromTokenSymbol
secondaryText: SQUtils.Utils.elideAndFormatWalletAddress(root.fromTokenContractAddress)
secondaryText: root.fromTokenSymbol !== Constants.ethToken ? SQUtils.Utils.elideAndFormatWalletAddress(root.fromTokenContractAddress) : ""
icon: Constants.tokenIcon(root.fromTokenSymbol)
badge: root.networkIconPath
components: [
ContractInfoButtonWithMenu {
visible: root.fromTokenSymbol !== Constants.ethToken
symbol: root.fromTokenSymbol
contractAddress: root.fromTokenContractAddress
networkName: root.networkName
@ -183,7 +184,7 @@ SignTransactionModalBase {
caption: qsTr("Via smart contract")
primaryText: root.serviceProviderName
secondaryText: SQUtils.Utils.elideAndFormatWalletAddress(root.serviceProviderContractAddress)
icon: Style.png("swap/paraswap") // FIXME svg
icon: root.serviceProviderIcon
components: [
ContractInfoButtonWithMenu {
symbol: ""
@ -213,6 +214,7 @@ SignTransactionModalBase {
objectName: "feesBox"
caption: qsTr("Fees")
primaryText: qsTr("Max. fees on %1").arg(root.networkName)
primaryTextCustomColor: Theme.palette.baseColor1
secondaryText: " "
components: [
ColumnLayout {
@ -220,7 +222,7 @@ SignTransactionModalBase {
StatusTextWithLoadingState {
objectName: "fiatFeesText"
Layout.alignment: Qt.AlignRight
text: "%1 %2".arg(formatBigNumber(root.fiatFees)).arg(root.currentCurrency)
text: loading ? Constants.dummyText : root.fiatFees
horizontalAlignment: Text.AlignRight
font.pixelSize: Style.current.additionalTextSize
loading: root.feesLoading
@ -228,7 +230,7 @@ SignTransactionModalBase {
StatusTextWithLoadingState {
objectName: "cryptoFeesText"
Layout.alignment: Qt.AlignRight
text: "%1 ETH".arg(formatBigNumber(root.cryptoFees))
text: loading ? Constants.dummyText : root.cryptoFees
horizontalAlignment: Text.AlignRight
font.pixelSize: Style.current.additionalTextSize
customColor: Theme.palette.baseColor1

View File

@ -56,9 +56,7 @@ QtObject {
return !!root.selectedAccountAddress &&
root.selectedNetworkChainId !== -1 &&
!!root.fromTokensKey && !!root.toTokenKey &&
(!!root.fromTokenAmount &&
!isNaN(bigIntNumber) &&
SQUtils.AmountsArithmetic.cmp(bigIntNumber, 0) === 1) &&
(!!root.fromTokenAmount && !isNaN(bigIntNumber) && bigIntNumber.gt(0)) &&
root.selectedSlippage > 0
}
}

View File

@ -24,6 +24,7 @@ StatusDialog {
parameters to the modal when being launched from elsewhere */
required property SwapInputParamsForm swapInputParamsForm
required property SwapModalAdaptor swapAdaptor
required property int loginType
objectName: "swapModal"
@ -377,7 +378,8 @@ StatusDialog {
objectName: "signButton"
readonly property string fromTokenSymbol: !!root.swapAdaptor.fromToken ? root.swapAdaptor.fromToken.symbol ?? "" : ""
loading: root.swapAdaptor.approvalPending
icon.name: "password"
icon.name: root.swapAdaptor.selectedAccount.migratedToKeycard ? Constants.authenticationIconByType[Constants.LoginType.Keycard]
: Constants.authenticationIconByType[root.loginType]
text: {
if(root.swapAdaptor.validSwapProposalReceived) {
if (root.swapAdaptor.approvalPending) {
@ -398,8 +400,10 @@ StatusDialog {
!root.swapAdaptor.approvalPending
onClicked: {
if (root.swapAdaptor.validSwapProposalReceived) {
let txType = root.swapAdaptor.swapOutputData.approvalNeeded ? SwapSignApprovePopup.TxType.Approve : SwapSignApprovePopup.TxType.Swap
Global.openPopup(swapSignApprovePopup, {"txType": txType})
if (root.swapAdaptor.swapOutputData.approvalNeeded)
Global.openPopup(swapApproveModalComponent)
else
Global.openPopup(swapSignModalComponent)
}
}
}
@ -407,46 +411,96 @@ StatusDialog {
}
}
/* TODO: this is only temporary placeholder and should be replaced completely by
https://github.com/status-im/status-desktop/issues/14785 */
Component {
id: swapSignApprovePopup
SwapSignApprovePopup {
id: approvePopup
id: swapApproveModalComponent
SwapApproveCapModal {
destroyOnClose: true
loading: root.swapAdaptor.swapProposalLoading
swapSignApproveInputForm: SwapSignApproveInputForm {
selectedAccountAddress: root.swapInputParamsForm.selectedAccountAddress
selectedNetworkChainId: root.swapInputParamsForm.selectedNetworkChainId
estimatedTime: root.swapAdaptor.swapOutputData.estimatedTime
swapProviderName: root.swapAdaptor.swapOutputData.txProviderName
approvalGasFees: root.swapAdaptor.swapOutputData.approvalGasFees
approvalAmountRequired: root.swapAdaptor.swapOutputData.approvalAmountRequired
approvalContractAddress: root.swapAdaptor.swapOutputData.approvalContractAddress
fromTokensKey: root.swapInputParamsForm.fromTokensKey
fromTokensAmount: root.swapInputParamsForm.fromTokenAmount
toTokensKey: root.swapInputParamsForm.toTokenKey
toTokensAmount: root.swapAdaptor.swapOutputData.toTokenAmount
swapFees: root.swapAdaptor.swapOutputData.totalFees
selectedSlippage: root.swapInputParamsForm.selectedSlippage
loginType: root.swapAdaptor.selectedAccount.migratedToKeycard ? Constants.LoginType.Keycard : root.loginType
feesLoading: root.swapAdaptor.swapProposalLoading
fromTokenSymbol: root.swapAdaptor.fromToken.symbol
fromTokenAmount: SQUtils.AmountsArithmetic.div(
SQUtils.AmountsArithmetic.fromString(root.swapAdaptor.swapOutputData.approvalAmountRequired),
SQUtils.AmountsArithmetic.fromNumber(1, root.swapAdaptor.fromToken.decimals ?? 18)).toFixed()
fromTokenContractAddress: SQUtils.ModelUtils.getByKey(root.swapAdaptor.fromToken.addressPerChain,
"chainId", root.swapInputParamsForm.selectedNetworkChainId,
"address")
accountName: root.swapAdaptor.selectedAccount.name
accountAddress: root.swapAdaptor.selectedAccount.address
accountEmoji: root.swapAdaptor.selectedAccount.emoji
accountColor: Utils.getColorForId(root.swapAdaptor.selectedAccount.colorId)
accountBalanceFormatted: payPanel.accountBalanceFormatted // FIXME https://github.com/status-im/status-desktop/issues/15554
networkShortName: networkFilter.singleSelectionItemData.shortName
networkName: networkFilter.singleSelectionItemData.chainName
networkIconPath: Style.svg(networkFilter.singleSelectionItemData.iconUrl)
networkBlockExplorerUrl: networkFilter.singleSelectionItemData.blockExplorerURL
fiatFees: {
const feesInFloat = root.swapAdaptor.currencyStore.getFiatValue(root.swapAdaptor.swapOutputData.approvalGasFees, Constants.ethToken)
return root.swapAdaptor.currencyStore.formatCurrencyAmount(feesInFloat, root.swapAdaptor.currencyStore.currentCurrency)
}
adaptor: SwapSignApproveAdaptor {
swapStore: root.swapAdaptor.swapStore
walletAssetsStore: root.swapAdaptor.walletAssetsStore
currencyStore: root.swapAdaptor.currencyStore
inputFormData: approvePopup.swapSignApproveInputForm
cryptoFees: root.swapAdaptor.currencyStore.formatCurrencyAmount(root.swapAdaptor.swapOutputData.approvalGasFees, Constants.ethToken)
estimatedTime: root.swapAdaptor.swapOutputData.estimatedTime
serviceProviderName: root.swapAdaptor.swapOutputData.txProviderName
// serviceProviderURL: "" // FIXME get the service provider URL from backend
// serviceProviderIcon: "" // FIXME get the service icon from backend
serviceProviderContractAddress: root.swapAdaptor.swapOutputData.approvalContractAddress
onAccepted: {
root.swapAdaptor.sendApproveTx()
}
onSign: {
if(txType === SwapSignApprovePopup.TxType.Approve) {
root.swapAdaptor.sendApproveTx()
close()
} else {
root.swapAdaptor.sendSwapTx()
close()
root.close()
}
}
}
Component {
id: swapSignModalComponent
SwapSignModal {
destroyOnClose: true
loginType: root.swapAdaptor.selectedAccount.migratedToKeycard ? Constants.LoginType.Keycard : root.loginType
feesLoading: root.swapAdaptor.swapProposalLoading
fromTokenSymbol: root.swapAdaptor.fromToken.symbol
fromTokenAmount: root.swapInputParamsForm.fromTokenAmount
fromTokenContractAddress: SQUtils.ModelUtils.getByKey(root.swapAdaptor.fromToken.addressPerChain,
"chainId", root.swapInputParamsForm.selectedNetworkChainId,
"address")
toTokenSymbol: root.swapAdaptor.toToken.symbol
toTokenAmount: root.swapAdaptor.swapOutputData.toTokenAmount
toTokenContractAddress: SQUtils.ModelUtils.getByKey(root.swapAdaptor.toToken.addressPerChain,
"chainId", root.swapInputParamsForm.selectedNetworkChainId,
"address")
accountName: root.swapAdaptor.selectedAccount.name
accountAddress: root.swapAdaptor.selectedAccount.address
accountEmoji: root.swapAdaptor.selectedAccount.emoji
accountColor: Utils.getColorForId(root.swapAdaptor.selectedAccount.colorId)
networkShortName: networkFilter.singleSelectionItemData.shortName
networkName: networkFilter.singleSelectionItemData.chainName
networkIconPath: Style.svg(networkFilter.singleSelectionItemData.iconUrl)
networkBlockExplorerUrl: networkFilter.singleSelectionItemData.blockExplorerURL
fiatFees: root.swapAdaptor.currencyStore.formatCurrencyAmount(root.swapAdaptor.swapOutputData.totalFees,
root.swapAdaptor.currencyStore.currentCurrency)
cryptoFees: {
const cryptoValue = root.swapAdaptor.currencyStore.getCryptoValue(root.swapAdaptor.swapOutputData.totalFees, Constants.ethToken)
return root.swapAdaptor.currencyStore.formatCurrencyAmount(cryptoValue, Constants.ethToken)
}
slippage: root.swapInputParamsForm.selectedSlippage
serviceProviderName: root.swapAdaptor.swapOutputData.txProviderName
// serviceProviderURL: "" // FIXME get the service provider URL from backend
onAccepted: {
root.swapAdaptor.sendSwapTx()
root.close()
}
onReject: close()
}
}
}

View File

@ -30,6 +30,7 @@ QObject {
// To expose the selected from and to Token from the SwapModal
readonly property var fromToken: fromTokenEntry.item
readonly property var toToken: toTokenEntry.item
readonly property var selectedAccount: selectedAccountEntry.item
readonly property string uuid: d.uuid
@ -245,8 +246,7 @@ QObject {
root.swapProposalLoading = true
let account = selectedAccountEntry.item
let accountAddress = account.address
let accountAddress = root.swapFormData.selectedAccountAddress
let disabledChainIds = getDisabledChainIds(root.swapFormData.selectedNetworkChainId)
root.swapStore.fetchSuggestedRoutes(d.uuid, accountAddress, accountAddress,
@ -260,9 +260,7 @@ QObject {
function sendApproveTx() {
root.approvalPending = true
let account = selectedAccountEntry.item
let accountAddress = account.address
const accountAddress = root.swapFormData.selectedAccountAddress
root.swapStore.authenticateAndTransfer(d.uuid, accountAddress, accountAddress,
root.swapFormData.fromTokensKey, root.swapFormData.toTokenKey,
@ -270,8 +268,7 @@ QObject {
}
function sendSwapTx() {
let account = selectedAccountEntry.item
let accountAddress = account.address
const accountAddress = root.swapFormData.selectedAccountAddress
root.swapStore.authenticateAndTransfer(d.uuid, accountAddress, accountAddress,
root.swapFormData.fromTokensKey, root.swapFormData.toTokenKey,

View File

@ -1,68 +0,0 @@
import QtQml 2.15
import StatusQ 0.1
import StatusQ.Core.Utils 0.1
import shared.stores 1.0
import AppLayouts.Wallet.stores 1.0 as WalletStore
QObject {
id: root
required property CurrenciesStore currencyStore
required property WalletStore.WalletAssetsStore walletAssetsStore
required property WalletStore.SwapStore swapStore
required property SwapSignApproveInputForm inputFormData
// To expose the selected from and to Token from the SwapModal
readonly property var fromToken: fromTokenEntry.item
readonly property var toToken: toTokenEntry.item
readonly property var selectedAccount: selectedAccountEntry.item
readonly property var selectedNetwork: selectedNetworkEntry.item
readonly property var fromTokenContractAddress: fromTokenContractAddress.item
readonly property var toTokenContractAddress: toTokenContractAddress.item
ModelEntry {
id: fromTokenEntry
sourceModel: root.walletAssetsStore.walletTokensStore.plainTokensBySymbolModel
key: "key"
value: root.inputFormData.fromTokensKey
}
ModelEntry {
id: selectedAccountEntry
sourceModel: root.swapStore.accounts
key: "address"
value: root.inputFormData.selectedAccountAddress
}
ModelEntry {
id: selectedNetworkEntry
sourceModel: root.swapStore.flatNetworks
key: "chainId"
value: root.inputFormData.selectedNetworkChainId
}
ModelEntry {
id: toTokenEntry
sourceModel: root.walletAssetsStore.walletTokensStore.plainTokensBySymbolModel
key: "key"
value: root.inputFormData.toTokensKey
}
ModelEntry {
id: fromTokenContractAddress
sourceModel: !!root.fromToken ?
root.fromToken.addressPerChain ?? null : null
key: "chainId"
value: root.inputFormData.selectedNetworkChainId
}
ModelEntry {
id: toTokenContractAddress
sourceModel: !!root.toToken ?
root.toToken.addressPerChain ?? null : null
key: "chainId"
value: root.inputFormData.selectedNetworkChainId
}
}

View File

@ -1,24 +0,0 @@
import QtQml 2.15
/* This is used so that there is an easy way to fill in the data
needed to launch the Approve/Sign Modal with pre-filled requisites. */
QtObject {
id: root
required property string selectedAccountAddress
required property int selectedNetworkChainId
required property string fromTokensKey
required property string fromTokensAmount
required property string toTokensKey
required property string toTokensAmount
required property double selectedSlippage
// TODO: this should be string but backend gas_estimate_item.nim passes this as float
required property double swapFees
// need to check how this is done in new router, right now it is Enum type
required property int estimatedTime
required property string swapProviderName
required property string approvalGasFees
required property string approvalAmountRequired
required property string approvalContractAddress
}

View File

@ -1,375 +0,0 @@
import QtQuick 2.15
import QtQuick.Layouts 1.15
import QtQml.Models 2.15
import StatusQ 0.1
import StatusQ.Core 0.1
import StatusQ.Popups.Dialog 0.1
import StatusQ.Controls 0.1
import StatusQ.Components 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Core.Utils 0.1 as SQUtils
import shared.controls 1.0
import AppLayouts.Wallet 1.0
import AppLayouts.Wallet.controls 1.0
import utils 1.0
// TODO: this is only temporary placeholder and should be replaced completely by https://github.com/status-im/status-desktop/issues/14785
StatusDialog {
id: root
required property bool loading
required property SwapSignApproveInputForm swapSignApproveInputForm
required property SwapSignApproveAdaptor adaptor
property int txType: SwapSignApprovePopup.TxType.Swap
signal sign()
signal reject()
enum TxType {
Swap,
Approve
}
QtObject {
id: d
readonly property bool isApproveTx: root.txType === SwapSignApprovePopup.TxType.Approve
readonly property int defaultDecmials: 18
}
objectName: "swapSignApproveModal"
implicitWidth: 480
padding: 20
title: d.isApproveTx ? qsTr("Approve spending cap"): qsTr("Sign Swap")
/* TODO: https://github.com/status-im/status-desktop/issues/15329
This is only added temporarily until we have an api from the backend in order to get
this list dynamically */
subtitle: d.isApproveTx ? Constants.swap.paraswapUrl : qsTr("%1 to %2").arg(payToken.title).arg(receiveToken.title)
contentItem: StatusScrollView {
id: scrollView
padding: 0
ColumnLayout {
spacing: Style.current.bigPadding
clip: true
width: scrollView.availableWidth
Column {
width: scrollView.availableWidth
spacing: Style.current.padding
StatusBaseText {
width: parent.width
text: qsTr("Set spending cap")
}
StatusListItem {
width: parent.width
title: {
let bigAmount = SQUtils.AmountsArithmetic.div(
SQUtils.AmountsArithmetic.fromString(swapSignApproveInputForm.approvalAmountRequired),
SQUtils.AmountsArithmetic.fromNumber(1, !!root.adaptor.fromToken ? root.adaptor.fromToken.decimals: d.defaultDecmials)).toFixed()
return bigAmount.replace('.', LocaleUtils.userInputLocale.decimalPoint)
}
border.width: 1
border.color: Theme.palette.baseColor2
components: [
StatusSmartIdenticon {
asset.name: !!root.adaptor.fromToken ?
Constants.tokenIcon(root.adaptor.fromToken.symbol): ""
asset.isImage: true
asset.width: 20
asset.height: 20
},
StatusBaseText {
text: !!root.adaptor.fromToken ?
root.adaptor.fromToken.symbol ?? "" : ""
}
]
}
visible: d.isApproveTx
}
Column {
width: scrollView.availableWidth
spacing: Style.current.padding
StatusBaseText {
width: parent.width
text: qsTr("Pay")
}
StatusListItem {
id: payToken
width: parent.width
height: 76
border.width: 1
border.color: Theme.palette.baseColor2
asset.name: !!root.adaptor.fromToken ?
Constants.tokenIcon(root.adaptor.fromToken.symbol): ""
asset.isImage: true
title: qsTr("%1 %2").arg(
SQUtils.AmountsArithmetic.fromString(swapSignApproveInputForm.fromTokensAmount).toFixed().replace('.', LocaleUtils.userInputLocale.decimalPoint)).arg(
!!root.adaptor.fromToken ? root.adaptor.fromToken.symbol: "")
subTitle: SQUtils.Utils.elideText(root.adaptor.fromTokenContractAddress.address, 6, 4)
components: [
StatusRoundButton {
type: StatusRoundButton.Type.Quinary
radius: 8
icon.name: "more"
icon.color: Theme.palette.directColor5
onClicked: {}
}
]
}
visible: !d.isApproveTx
}
Column {
width: scrollView.availableWidth
spacing: Style.current.padding
StatusBaseText {
width: parent.width
text: qsTr("Receive")
}
StatusListItem {
id: receiveToken
width: parent.width
height: 76
border.width: 1
border.color: Theme.palette.baseColor2
asset.name: !!root.adaptor.toToken ?
Constants.tokenIcon(root.adaptor.toToken.symbol): ""
asset.isImage: true
title: qsTr("%1 %2").arg(
SQUtils.AmountsArithmetic.fromString(swapSignApproveInputForm.toTokensAmount).toFixed().replace('.', LocaleUtils.userInputLocale.decimalPoint)).arg(
!!root.adaptor.toToken ? root.adaptor.toToken.symbol: "")
subTitle: SQUtils.Utils.elideText(root.adaptor.toTokenContractAddress.address, 6, 4)
components: [
StatusRoundButton {
type: StatusRoundButton.Type.Quinary
radius: 8
icon.name: "more"
icon.color: Theme.palette.directColor5
onClicked: {}
}
]
}
visible: !d.isApproveTx
}
Column {
width: scrollView.availableWidth
spacing: Style.current.padding
StatusBaseText {
width: parent.width
text: d.isApproveTx ? qsTr("Account") : qsTr("In account")
}
WalletAccountListItem {
width: parent.width
height: 76
border.width: 1
border.color: Theme.palette.baseColor2
name: !!root.adaptor.selectedAccount ? root.adaptor.selectedAccount.name: ""
address: !!root.adaptor.selectedAccount ? root.adaptor.selectedAccount.address: ""
chainShortNames: !!root.adaptor.selectedAccount ? root.adaptor.selectedAccount.colorizedChainPrefixes ?? "" : ""
emoji: !!root.adaptor.selectedAccount ? root.adaptor.selectedAccount.emoji: ""
walletColor: Utils.getColorForId(!!root.adaptor.selectedAccount ? root.adaptor.selectedAccount.colorId : "")
currencyBalance: !!root.adaptor.selectedAccount ? root.adaptor.selectedAccount.currencyBalance: ""
walletType: !!root.adaptor.selectedAccount ? root.adaptor.selectedAccount.walletType: ""
migratedToKeycard: !!root.adaptor.selectedAccount ? root.adaptor.selectedAccount.migratedToKeycard ?? false : false
accountBalance: !!root.adaptor.selectedAccount ? root.adaptor.selectedAccount.accountBalance : null
}
}
Column {
width: scrollView.availableWidth
spacing: Style.current.padding
StatusBaseText {
width: parent.width
text: qsTr("Token")
}
StatusListItem {
width: parent.width
height: 76
border.width: 1
border.color: Theme.palette.baseColor2
asset.name: !!root.adaptor.fromToken ?
Constants.tokenIcon(root.adaptor.fromToken.symbol): ""
asset.isImage: true
title: !!root.adaptor.fromToken ?
root.adaptor.fromToken.symbol ?? "" : ""
subTitle: SQUtils.Utils.elideText(payContractAddressOnSelectedNetwork.item.address, 6, 4)
ModelEntry {
id: payContractAddressOnSelectedNetwork
sourceModel: !!root.adaptor.fromToken ?
root.adaptor.fromToken.addressPerChain ?? null : null
key: "chainId"
value: root.swapSignApproveInputForm.selectedNetworkChainId
}
components: [
StatusRoundButton {
type: StatusRoundButton.Type.Quinary
radius: 8
icon.name: "more"
icon.color: Theme.palette.directColor5
onClicked: {}
}
]
}
visible: d.isApproveTx
}
Column {
width: scrollView.availableWidth
spacing: Style.current.padding
StatusBaseText {
width: parent.width
text: qsTr("Via smart contract")
}
StatusListItem {
width: parent.width
height: 76
border.width: 1
border.color: Theme.palette.baseColor2
title: root.swapSignApproveInputForm.swapProviderName
subTitle: SQUtils.Utils.elideText(root.swapSignApproveInputForm.approvalContractAddress, 6, 4)
/* TODO: https://github.com/status-im/status-desktop/issues/15329
This is only added temporarily until we have an api from the backend in order to get
this list dynamically */
asset.name: Style.png("swap/%1".arg(Constants.swap.paraswapIcon))
asset.isImage: true
components: [
StatusRoundButton {
type: StatusRoundButton.Type.Quinary
radius: 8
icon.name: "more"
icon.color: Theme.palette.directColor5
onClicked: {}
}
]
}
visible: d.isApproveTx
}
Column {
width: scrollView.availableWidth
spacing: Style.current.padding
StatusBaseText {
width: parent.width
text: qsTr("Network")
}
StatusListItem {
width: parent.width
height: 76
border.width: 1
border.color: Theme.palette.baseColor2
asset.name: !!root.adaptor.selectedNetwork ?
root.adaptor.selectedNetwork.isTest ?
Style.svg(root.adaptor.selectedNetwork.iconUrl + "-test") :
Style.svg(root.adaptor.selectedNetwork.iconUrl): ""
asset.isImage: true
asset.color: "transparent"
asset.bgColor: "transparent"
title: !!root.adaptor.selectedNetwork ?
root.adaptor.selectedNetwork.chainName ?? "" : ""
}
}
Column {
width: scrollView.availableWidth
spacing: Style.current.padding
StatusBaseText {
width: parent.width
text: qsTr("Fees")
}
StatusListItem {
width: parent.width
height: 76
border.width: 1
border.color: Theme.palette.baseColor2
title: qsTr("Max. fees on %1").arg(!!root.adaptor.selectedNetwork ?
root.adaptor.selectedNetwork.chainName : "")
components: [
Column {
anchors.verticalCenter: parent.verticalCenter
StatusTextWithLoadingState {
anchors.right: parent.right
loading: root.loading
text: {
if(d.isApproveTx) {
let feesInFoat = root.adaptor.currencyStore.getFiatValue(root.swapSignApproveInputForm.approvalGasFees, Constants.ethToken)
return root.adaptor.currencyStore.formatCurrencyAmount(feesInFoat, root.adaptor.currencyStore.currentCurrency)
} else {
return root.adaptor.currencyStore.formatCurrencyAmount(root.swapSignApproveInputForm.swapFees, root.adaptor.currencyStore.currentCurrency)
}
}
}
StatusTextWithLoadingState {
anchors.right: parent.right
loading: root.loading
text: {
if(d.isApproveTx) {
return root.adaptor.currencyStore.formatCurrencyAmount(root.swapSignApproveInputForm.approvalGasFees, Constants.ethToken)
}
else {
let cryptoValue = root.adaptor.currencyStore.getCryptoValue(root.swapSignApproveInputForm.swapFees, Constants.ethToken)
return root.adaptor.currencyStore.formatCurrencyAmount(cryptoValue, Constants.ethToken)
}
}
}
}
]
}
}
}
}
footer: StatusDialogFooter {
spacing: Style.current.xlPadding
leftButtons: ObjectModel {
SwapModalFooterInfoComponent {
titleText: qsTr("Max fees:")
infoText: {
if(d.isApproveTx) {
let feesInFoat = root.adaptor.currencyStore.getFiatValue(root.swapSignApproveInputForm.approvalGasFees, Constants.ethToken)
return root.adaptor.currencyStore.formatCurrencyAmount(feesInFoat, root.adaptor.currencyStore.currentCurrency)
} else {
return root.adaptor.currencyStore.formatCurrencyAmount(root.swapSignApproveInputForm.swapFees, root.adaptor.currencyStore.currentCurrency)
}
}
loading: root.loading
}
SwapModalFooterInfoComponent {
Layout.maximumWidth: 60
titleText: qsTr("Est. time:")
infoText: WalletUtils.getLabelForEstimatedTxTime(root.swapSignApproveInputForm.estimatedTime)
loading: root.loading
visible: d.isApproveTx
}
SwapModalFooterInfoComponent {
Layout.maximumWidth: 60
titleText: qsTr("Max slippage:")
infoText: "%1%".arg(LocaleUtils.numberToLocaleString(root.swapSignApproveInputForm.selectedSlippage))
visible: !d.isApproveTx
}
}
rightButtons: ObjectModel {
StatusButton {
objectName: "rejectButton"
text: qsTr("Reject")
normalColor: Theme.palette.transparent
onClicked: root.reject()
}
StatusButton {
objectName: "signButton"
icon.name: "password"
text: qsTr("Sign")
disabledColor: Theme.palette.directColor8
enabled: !root.loading
onClicked: root.sign()
}
}
}
}

View File

@ -36,7 +36,6 @@ SignTransactionModalBase {
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
required property double slippage
@ -106,7 +105,7 @@ SignTransactionModalBase {
}
StatusTextWithLoadingState {
objectName: "footerFiatFeesText"
text: "%1 %2".arg(formatBigNumber(root.fiatFees)).arg(root.currentCurrency)
text: loading ? Constants.dummyText : root.fiatFees
loading: root.feesLoading
}
}
@ -132,11 +131,12 @@ SignTransactionModalBase {
objectName: "payBox"
caption: qsTr("Pay")
primaryText: "%1 %2".arg(formatBigNumber(root.fromTokenAmount)).arg(root.fromTokenSymbol)
secondaryText: SQUtils.Utils.elideAndFormatWalletAddress(root.fromTokenContractAddress)
secondaryText: root.fromTokenSymbol !== Constants.ethToken ? SQUtils.Utils.elideAndFormatWalletAddress(root.fromTokenContractAddress) : ""
icon: Constants.tokenIcon(root.fromTokenSymbol)
badge: root.networkIconPath
components: [
ContractInfoButtonWithMenu {
visible: root.fromTokenSymbol !== Constants.ethToken
symbol: root.fromTokenSymbol
contractAddress: root.fromTokenContractAddress
networkName: root.networkName
@ -154,11 +154,12 @@ SignTransactionModalBase {
objectName: "receiveBox"
caption: qsTr("Receive")
primaryText: "%1 %2".arg(formatBigNumber(root.toTokenAmount)).arg(root.toTokenSymbol)
secondaryText: SQUtils.Utils.elideAndFormatWalletAddress(root.toTokenContractAddress)
secondaryText: root.toTokenSymbol !== Constants.ethToken ? SQUtils.Utils.elideAndFormatWalletAddress(root.toTokenContractAddress) : ""
icon: Constants.tokenIcon(root.toTokenSymbol)
badge: root.networkIconPath
components: [
ContractInfoButtonWithMenu {
visible: root.toTokenSymbol !== Constants.ethToken
symbol: root.toTokenSymbol
contractAddress: root.toTokenContractAddress
networkName: root.networkName
@ -200,6 +201,7 @@ SignTransactionModalBase {
objectName: "feesBox"
caption: qsTr("Fees")
primaryText: qsTr("Max. fees on %1").arg(root.networkName)
primaryTextCustomColor: Theme.palette.baseColor1
secondaryText: " "
components: [
ColumnLayout {
@ -207,7 +209,7 @@ SignTransactionModalBase {
StatusTextWithLoadingState {
objectName: "fiatFeesText"
Layout.alignment: Qt.AlignRight
text: "%1 %2".arg(formatBigNumber(root.fiatFees)).arg(root.currentCurrency)
text: loading ? Constants.dummyText : root.fiatFees
horizontalAlignment: Text.AlignRight
font.pixelSize: Style.current.additionalTextSize
loading: root.feesLoading
@ -215,7 +217,7 @@ SignTransactionModalBase {
StatusTextWithLoadingState {
objectName: "cryptoFeesText"
Layout.alignment: Qt.AlignRight
text: "%1 ETH".arg(formatBigNumber(root.cryptoFees))
text: loading ? Constants.dummyText : root.cryptoFees
horizontalAlignment: Text.AlignRight
font.pixelSize: Style.current.additionalTextSize
customColor: Theme.palette.baseColor1

View File

@ -2,9 +2,5 @@ SwapModal 1.0 SwapModal.qml
SwapInputParamsForm 1.0 SwapInputParamsForm.qml
SwapModalAdaptor 1.0 SwapModalAdaptor.qml
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

View File

@ -1253,6 +1253,7 @@ QtObject {
swapFormData: swapInputParamsForm
swapOutputData: SwapOutputData{}
}
loginType: root.rootStore.loginType
onClosed: destroy()
}
},

View File

@ -900,6 +900,9 @@ QtObject {
readonly property string mainnet: "eth"
readonly property string arbitrum: "arb1"
readonly property string optimism: "oeth"
readonly property string optimism_goerli: "goOpt"
readonly property string arbitrum_goerli: "goArb"
}
readonly property QtObject networkExplorerLinks: QtObject {