From 38d155f5909f75a5f3afa17ea3e99d29bc41911a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Tinkl?= Date: Wed, 10 Jul 2024 20:35:24 +0200 Subject: [PATCH] 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 --- storybook/pages/SwapApproveCapModalPage.qml | 9 +- storybook/pages/SwapModalPage.qml | 32 +- storybook/pages/SwapSignApprovePopupPage.qml | 113 ------ storybook/pages/SwapSignModalPage.qml | 7 +- .../tests/tst_SwapApproveCapModal.qml | 27 +- storybook/qmlTests/tests/tst_SwapModal.qml | 3 +- .../qmlTests/tests/tst_SwapSignModal.qml | 30 +- storybook/src/Models/NetworksModel.qml | 12 +- .../src/Models/SwapTransactionRoutes.qml | 20 +- .../stubs/shared/stores/CurrenciesStore.qml | 4 + .../StatusQ/Core/Utils/AmountsArithmetic.qml | 6 +- .../adaptors/TokenSelectorViewAdaptor.qml | 2 +- .../controls/SwapModalFooterInfoComponent.qml | 28 -- ui/app/AppLayouts/Wallet/controls/qmldir | 1 - .../panels/ContractInfoButtonWithMenu.qml | 5 +- .../AppLayouts/Wallet/panels/SignInfoBox.qml | 2 + .../Wallet/panels/SwapInputPanel.qml | 3 +- .../popups/SignTransactionModalBase.qml | 1 + .../popups/swap/SwapApproveCapModal.qml | 20 +- .../popups/swap/SwapInputParamsForm.qml | 4 +- .../Wallet/popups/swap/SwapModal.qml | 130 ++++-- .../Wallet/popups/swap/SwapModalAdaptor.qml | 11 +- .../popups/swap/SwapSignApproveAdaptor.qml | 68 ---- .../popups/swap/SwapSignApproveInputForm.qml | 24 -- .../popups/swap/SwapSignApprovePopup.qml | 375 ------------------ .../Wallet/popups/swap/SwapSignModal.qml | 14 +- ui/app/AppLayouts/Wallet/popups/swap/qmldir | 4 - ui/app/mainui/Popups.qml | 1 + ui/imports/utils/Constants.qml | 3 + 29 files changed, 214 insertions(+), 745 deletions(-) delete mode 100644 storybook/pages/SwapSignApprovePopupPage.qml delete mode 100644 ui/app/AppLayouts/Wallet/controls/SwapModalFooterInfoComponent.qml delete mode 100644 ui/app/AppLayouts/Wallet/popups/swap/SwapSignApproveAdaptor.qml delete mode 100644 ui/app/AppLayouts/Wallet/popups/swap/SwapSignApproveInputForm.qml delete mode 100644 ui/app/AppLayouts/Wallet/popups/swap/SwapSignApprovePopup.qml diff --git a/storybook/pages/SwapApproveCapModalPage.qml b/storybook/pages/SwapApproveCapModalPage.qml index 88062ce3b3..7da582f47f 100644 --- a/storybook/pages/SwapApproveCapModalPage.qml +++ b/storybook/pages/SwapApproveCapModalPage.qml @@ -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 diff --git a/storybook/pages/SwapModalPage.qml b/storybook/pages/SwapModalPage.qml index 525de63f49..8a9def6bdb 100644 --- a/storybook/pages/SwapModalPage.qml +++ b/storybook/pages/SwapModalPage.qml @@ -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 } diff --git a/storybook/pages/SwapSignApprovePopupPage.qml b/storybook/pages/SwapSignApprovePopupPage.qml deleted file mode 100644 index 96dcbedf2a..0000000000 --- a/storybook/pages/SwapSignApprovePopupPage.qml +++ /dev/null @@ -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 diff --git a/storybook/pages/SwapSignModalPage.qml b/storybook/pages/SwapSignModalPage.qml index 2af399e054..f46ca9498e 100644 --- a/storybook/pages/SwapSignModalPage.qml +++ b/storybook/pages/SwapSignModalPage.qml @@ -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 diff --git a/storybook/qmlTests/tests/tst_SwapApproveCapModal.qml b/storybook/qmlTests/tests/tst_SwapApproveCapModal.qml index cb7b63f39e..2cb4d3bae8 100644 --- a/storybook/qmlTests/tests/tst_SwapApproveCapModal.qml +++ b/storybook/qmlTests/tests/tst_SwapApproveCapModal.qml @@ -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) diff --git a/storybook/qmlTests/tests/tst_SwapModal.qml b/storybook/qmlTests/tests/tst_SwapModal.qml index 170da96806..4f32bea8af 100644 --- a/storybook/qmlTests/tests/tst_SwapModal.qml +++ b/storybook/qmlTests/tests/tst_SwapModal.qml @@ -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 } } diff --git a/storybook/qmlTests/tests/tst_SwapSignModal.qml b/storybook/qmlTests/tests/tst_SwapSignModal.qml index beaa50e282..255a50816d 100644 --- a/storybook/qmlTests/tests/tst_SwapSignModal.qml +++ b/storybook/qmlTests/tests/tst_SwapSignModal.qml @@ -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) diff --git a/storybook/src/Models/NetworksModel.qml b/storybook/src/Models/NetworksModel.qml index 004f0eea85..edd11d6bbb 100644 --- a/storybook/src/Models/NetworksModel.qml +++ b/storybook/src/Models/NetworksModel.qml @@ -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", diff --git a/storybook/src/Models/SwapTransactionRoutes.qml b/storybook/src/Models/SwapTransactionRoutes.qml index 576cc03ba4..5a43a22096 100644 --- a/storybook/src/Models/SwapTransactionRoutes.qml +++ b/storybook/src/Models/SwapTransactionRoutes.qml @@ -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 {} } diff --git a/storybook/stubs/shared/stores/CurrenciesStore.qml b/storybook/stubs/shared/stores/CurrenciesStore.qml index 65b1bccf2d..ed34c0c74c 100644 --- a/storybook/stubs/shared/stores/CurrenciesStore.qml +++ b/storybook/stubs/shared/stores/CurrenciesStore.qml @@ -27,6 +27,10 @@ QtObject { return parseFloat(balance) } + function getCryptoValue(balance, symbol) { + return balance + } + function getCurrencyAmount(amount, symbol) { return ({ amount: amount, diff --git a/ui/StatusQ/src/StatusQ/Core/Utils/AmountsArithmetic.qml b/ui/StatusQ/src/StatusQ/Core/Utils/AmountsArithmetic.qml index 2d555af74b..13e5cd3916 100644 --- a/ui/StatusQ/src/StatusQ/Core/Utils/AmountsArithmetic.qml +++ b/ui/StatusQ/src/StatusQ/Core/Utils/AmountsArithmetic.qml @@ -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) { diff --git a/ui/app/AppLayouts/Wallet/adaptors/TokenSelectorViewAdaptor.qml b/ui/app/AppLayouts/Wallet/adaptors/TokenSelectorViewAdaptor.qml index a4f21463f5..644513833d 100644 --- a/ui/app/AppLayouts/Wallet/adaptors/TokenSelectorViewAdaptor.qml +++ b/ui/app/AppLayouts/Wallet/adaptors/TokenSelectorViewAdaptor.qml @@ -129,7 +129,7 @@ QObject { FastExpressionFilter { function isPresentOnEnabledNetworks(addressPerChain) { if(!addressPerChain) - return true + return true return !!ModelUtils.getFirstModelEntryIf( addressPerChain, (addPerChain) => { diff --git a/ui/app/AppLayouts/Wallet/controls/SwapModalFooterInfoComponent.qml b/ui/app/AppLayouts/Wallet/controls/SwapModalFooterInfoComponent.qml deleted file mode 100644 index 58ecad606e..0000000000 --- a/ui/app/AppLayouts/Wallet/controls/SwapModalFooterInfoComponent.qml +++ /dev/null @@ -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 - } -} diff --git a/ui/app/AppLayouts/Wallet/controls/qmldir b/ui/app/AppLayouts/Wallet/controls/qmldir index 8f6225cb09..a7e2a4e2ae 100644 --- a/ui/app/AppLayouts/Wallet/controls/qmldir +++ b/ui/app/AppLayouts/Wallet/controls/qmldir @@ -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 diff --git a/ui/app/AppLayouts/Wallet/panels/ContractInfoButtonWithMenu.qml b/ui/app/AppLayouts/Wallet/panels/ContractInfoButtonWithMenu.qml index 3accdef01f..0d2e70eedd 100644 --- a/ui/app/AppLayouts/Wallet/panels/ContractInfoButtonWithMenu.qml +++ b/ui/app/AppLayouts/Wallet/panels/ContractInfoButtonWithMenu.qml @@ -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) } } diff --git a/ui/app/AppLayouts/Wallet/panels/SignInfoBox.qml b/ui/app/AppLayouts/Wallet/panels/SignInfoBox.qml index 5d44fea358..9d37244aa0 100644 --- a/ui/app/AppLayouts/Wallet/panels/SignInfoBox.qml +++ b/ui/app/AppLayouts/Wallet/panels/SignInfoBox.qml @@ -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 diff --git a/ui/app/AppLayouts/Wallet/panels/SwapInputPanel.qml b/ui/app/AppLayouts/Wallet/panels/SwapInputPanel.qml index 926cdee4db..5ded9029f2 100644 --- a/ui/app/AppLayouts/Wallet/panels/SwapInputPanel.qml +++ b/ui/app/AppLayouts/Wallet/panels/SwapInputPanel.qml @@ -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 } diff --git a/ui/app/AppLayouts/Wallet/popups/SignTransactionModalBase.qml b/ui/app/AppLayouts/Wallet/popups/SignTransactionModalBase.qml index c9b88707f3..663c78e5f7 100644 --- a/ui/app/AppLayouts/Wallet/popups/SignTransactionModalBase.qml +++ b/ui/app/AppLayouts/Wallet/popups/SignTransactionModalBase.qml @@ -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 } diff --git a/ui/app/AppLayouts/Wallet/popups/swap/SwapApproveCapModal.qml b/ui/app/AppLayouts/Wallet/popups/swap/SwapApproveCapModal.qml index b45a414c55..d9c33e33cd 100644 --- a/ui/app/AppLayouts/Wallet/popups/swap/SwapApproveCapModal.qml +++ b/ui/app/AppLayouts/Wallet/popups/swap/SwapApproveCapModal.qml @@ -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 diff --git a/ui/app/AppLayouts/Wallet/popups/swap/SwapInputParamsForm.qml b/ui/app/AppLayouts/Wallet/popups/swap/SwapInputParamsForm.qml index 07f232535a..fdd9da578a 100644 --- a/ui/app/AppLayouts/Wallet/popups/swap/SwapInputParamsForm.qml +++ b/ui/app/AppLayouts/Wallet/popups/swap/SwapInputParamsForm.qml @@ -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 } } diff --git a/ui/app/AppLayouts/Wallet/popups/swap/SwapModal.qml b/ui/app/AppLayouts/Wallet/popups/swap/SwapModal.qml index d3e1ebb6cf..d891e11862 100644 --- a/ui/app/AppLayouts/Wallet/popups/swap/SwapModal.qml +++ b/ui/app/AppLayouts/Wallet/popups/swap/SwapModal.qml @@ -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() } } } diff --git a/ui/app/AppLayouts/Wallet/popups/swap/SwapModalAdaptor.qml b/ui/app/AppLayouts/Wallet/popups/swap/SwapModalAdaptor.qml index ded8183c5c..79c1d1a1d1 100644 --- a/ui/app/AppLayouts/Wallet/popups/swap/SwapModalAdaptor.qml +++ b/ui/app/AppLayouts/Wallet/popups/swap/SwapModalAdaptor.qml @@ -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, diff --git a/ui/app/AppLayouts/Wallet/popups/swap/SwapSignApproveAdaptor.qml b/ui/app/AppLayouts/Wallet/popups/swap/SwapSignApproveAdaptor.qml deleted file mode 100644 index adc42fda80..0000000000 --- a/ui/app/AppLayouts/Wallet/popups/swap/SwapSignApproveAdaptor.qml +++ /dev/null @@ -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 - } -} diff --git a/ui/app/AppLayouts/Wallet/popups/swap/SwapSignApproveInputForm.qml b/ui/app/AppLayouts/Wallet/popups/swap/SwapSignApproveInputForm.qml deleted file mode 100644 index 78cf9300bb..0000000000 --- a/ui/app/AppLayouts/Wallet/popups/swap/SwapSignApproveInputForm.qml +++ /dev/null @@ -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 -} diff --git a/ui/app/AppLayouts/Wallet/popups/swap/SwapSignApprovePopup.qml b/ui/app/AppLayouts/Wallet/popups/swap/SwapSignApprovePopup.qml deleted file mode 100644 index a4c03d258a..0000000000 --- a/ui/app/AppLayouts/Wallet/popups/swap/SwapSignApprovePopup.qml +++ /dev/null @@ -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() - } - } - } -} diff --git a/ui/app/AppLayouts/Wallet/popups/swap/SwapSignModal.qml b/ui/app/AppLayouts/Wallet/popups/swap/SwapSignModal.qml index 0beff6fd77..c41be3a4cd 100644 --- a/ui/app/AppLayouts/Wallet/popups/swap/SwapSignModal.qml +++ b/ui/app/AppLayouts/Wallet/popups/swap/SwapSignModal.qml @@ -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 diff --git a/ui/app/AppLayouts/Wallet/popups/swap/qmldir b/ui/app/AppLayouts/Wallet/popups/swap/qmldir index fa0fa04f49..eabef78284 100644 --- a/ui/app/AppLayouts/Wallet/popups/swap/qmldir +++ b/ui/app/AppLayouts/Wallet/popups/swap/qmldir @@ -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 diff --git a/ui/app/mainui/Popups.qml b/ui/app/mainui/Popups.qml index 45bc74acf0..727a00595d 100644 --- a/ui/app/mainui/Popups.qml +++ b/ui/app/mainui/Popups.qml @@ -1253,6 +1253,7 @@ QtObject { swapFormData: swapInputParamsForm swapOutputData: SwapOutputData{} } + loginType: root.rootStore.loginType onClosed: destroy() } }, diff --git a/ui/imports/utils/Constants.qml b/ui/imports/utils/Constants.qml index a8f7b3ba7d..ffac945cfb 100644 --- a/ui/imports/utils/Constants.qml +++ b/ui/imports/utils/Constants.qml @@ -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 {