From ada348486e4caa01b3eff93a9268700d2a0d1b45 Mon Sep 17 00:00:00 2001 From: Khushboo Mehta Date: Thu, 8 Aug 2024 15:52:19 +0200 Subject: [PATCH] feat(@desktop/wallet): Swap:: Added text that links to Paraswaps terms and conditions page --- storybook/pages/SwapSignModalPage.qml | 3 +- .../tests/tst_SwapApproveCapModal.qml | 4 +- .../qmlTests/tests/tst_SwapSignModal.qml | 3 +- .../SwapProvidersTermsAndConditionsText.qml | 49 +++++++++++++++++++ ui/app/AppLayouts/Wallet/controls/qmldir | 1 + .../popups/swap/SwapApproveCapModal.qml | 29 ++++++++--- .../Wallet/popups/swap/SwapModal.qml | 5 +- .../Wallet/popups/swap/SwapSignModal.qml | 24 +++------ ui/app/AppLayouts/Wallet/stores/RootStore.qml | 2 +- ui/app/mainui/AppMain.qml | 16 +++--- ui/imports/utils/Constants.qml | 5 +- 11 files changed, 99 insertions(+), 42 deletions(-) create mode 100644 ui/app/AppLayouts/Wallet/controls/SwapProvidersTermsAndConditionsText.qml diff --git a/storybook/pages/SwapSignModalPage.qml b/storybook/pages/SwapSignModalPage.qml index a8e84ff2b4..5a05bfdfeb 100644 --- a/storybook/pages/SwapSignModalPage.qml +++ b/storybook/pages/SwapSignModalPage.qml @@ -96,7 +96,8 @@ SplitView { networkBlockExplorerUrl: priv.selectedNetwork.blockExplorerURL serviceProviderName: Constants.swap.paraswapName - serviceProviderURL: Constants.swap.termsAndConditionParaswapUrl + serviceProviderURL: Constants.swap.paraswapUrl + serviceProviderTandCUrl: Constants.swap.paraswapTermsAndConditionUrl fiatFees: formatBigNumber(42.542567, "EUR") cryptoFees: formatBigNumber(0.06, "ETH") diff --git a/storybook/qmlTests/tests/tst_SwapApproveCapModal.qml b/storybook/qmlTests/tests/tst_SwapApproveCapModal.qml index e42085d8f7..767cba04db 100644 --- a/storybook/qmlTests/tests/tst_SwapApproveCapModal.qml +++ b/storybook/qmlTests/tests/tst_SwapApproveCapModal.qml @@ -84,14 +84,14 @@ Item { // title & subtitle compare(controlUnderTest.title, qsTr("Approve spending cap")) - compare(controlUnderTest.subtitle, controlUnderTest.serviceProviderURL) + compare(controlUnderTest.subtitle, controlUnderTest.serviceProviderHostname) // info box const headerText = findChild(controlUnderTest.contentItem, "headerText") verify(!!headerText) compare(headerText.text, qsTr("Set %1 spending cap in %2 for %3 on %4") .arg(controlUnderTest.formatBigNumber(controlUnderTest.fromTokenAmount, controlUnderTest.fromTokenSymbol)) - .arg(controlUnderTest.accountName).arg(controlUnderTest.serviceProviderURL).arg(controlUnderTest.networkName)) + .arg(controlUnderTest.accountName).arg(controlUnderTest.serviceProviderHostname).arg(controlUnderTest.networkName)) const fromImageHidden = findChild(controlUnderTest.contentItem, "fromImageIdenticon") compare(fromImageHidden.visible, false) diff --git a/storybook/qmlTests/tests/tst_SwapSignModal.qml b/storybook/qmlTests/tests/tst_SwapSignModal.qml index 266c63d88a..710d2a8098 100644 --- a/storybook/qmlTests/tests/tst_SwapSignModal.qml +++ b/storybook/qmlTests/tests/tst_SwapSignModal.qml @@ -42,7 +42,8 @@ Item { networkBlockExplorerUrl: "https://etherscan.io/" serviceProviderName: Constants.swap.paraswapName - serviceProviderURL: Constants.swap.termsAndConditionParaswapUrl + serviceProviderURL: Constants.swap.paraswapUrl + serviceProviderTandCUrl: Constants.swap.paraswapTermsAndConditionUrl fiatFees: "1.54 EUR" cryptoFees: "0.001 ETH" diff --git a/ui/app/AppLayouts/Wallet/controls/SwapProvidersTermsAndConditionsText.qml b/ui/app/AppLayouts/Wallet/controls/SwapProvidersTermsAndConditionsText.qml new file mode 100644 index 0000000000..2f7ab99694 --- /dev/null +++ b/ui/app/AppLayouts/Wallet/controls/SwapProvidersTermsAndConditionsText.qml @@ -0,0 +1,49 @@ +import QtQuick 2.15 +import QtQuick.Layouts 1.15 + +import StatusQ.Controls 0.1 +import StatusQ.Core 0.1 +import StatusQ.Core.Theme 0.1 + +import utils 1.0 + +RowLayout { + id: root + + required property string serviceProviderName + signal linkClicked() + signal termsAndConditionClicked() + + spacing: 4 + + StatusIcon { + Layout.preferredWidth: 16 + Layout.preferredHeight: 16 + icon: "external-link" + color: Theme.palette.directColor1 + } + StatusBaseText { + font.pixelSize: Style.current.additionalTextSize + text: qsTr("Powered by") + } + StatusLinkText { + Layout.topMargin: 1 // compensate for the underline + text: "%1.".arg(root.serviceProviderName) + normalColor: Theme.palette.directColor1 + linkColor: Theme.palette.directColor1 + font.weight: Font.Normal + onClicked: root.linkClicked() + } + StatusBaseText { + font.pixelSize: Style.current.additionalTextSize + text: qsTr("View") + } + StatusLinkText { + Layout.topMargin: 1 // compensate for the underline + text: qsTr("Terms & Conditions") + normalColor: Theme.palette.directColor1 + linkColor: Theme.palette.directColor1 + font.weight: Font.Normal + onClicked: root.termsAndConditionClicked() + } +} diff --git a/ui/app/AppLayouts/Wallet/controls/qmldir b/ui/app/AppLayouts/Wallet/controls/qmldir index a7e2a4e2ae..c13939bace 100644 --- a/ui/app/AppLayouts/Wallet/controls/qmldir +++ b/ui/app/AppLayouts/Wallet/controls/qmldir @@ -20,3 +20,4 @@ StatusTxProgressBar 1.0 StatusTxProgressBar.qml SwapExchangeButton 1.0 SwapExchangeButton.qml TokenSelector 1.0 TokenSelector.qml TokenSelectorNew 1.0 TokenSelectorNew.qml +SwapProvidersTermsAndConditionsText 1.0 SwapProvidersTermsAndConditionsText.qml diff --git a/ui/app/AppLayouts/Wallet/popups/swap/SwapApproveCapModal.qml b/ui/app/AppLayouts/Wallet/popups/swap/SwapApproveCapModal.qml index 8d261e5be3..e87343fe17 100644 --- a/ui/app/AppLayouts/Wallet/popups/swap/SwapApproveCapModal.qml +++ b/ui/app/AppLayouts/Wallet/popups/swap/SwapApproveCapModal.qml @@ -13,6 +13,7 @@ import StatusQ.Components 0.1 import AppLayouts.Wallet 1.0 import AppLayouts.Wallet.panels 1.0 import AppLayouts.Wallet.popups 1.0 +import AppLayouts.Wallet.controls 1.0 import shared.controls 1.0 import utils 1.0 @@ -41,12 +42,14 @@ SignTransactionModalBase { required property int estimatedTime // Constants.TransactionEstimatedTime.XXX enum property string serviceProviderName: Constants.swap.paraswapName + property string serviceProviderHostname: Constants.swap.paraswapHostname + property string serviceProviderTandCUrl: Constants.swap.paraswapTermsAndConditionUrl property string serviceProviderURL: Constants.swap.paraswapUrl // TODO https://github.com/status-im/status-desktop/issues/15329 property string serviceProviderContractAddress: Constants.swap.paraswapApproveContractAddress property string serviceProviderIcon: Style.png("swap/%1".arg(Constants.swap.paraswapIcon)) // FIXME svg title: qsTr("Approve spending cap") - subtitle: serviceProviderURL + subtitle: root.serviceProviderHostname gradientColor: Utils.setColorAlpha(root.accountColor, 0.05) // 5% of wallet color fromImageSmartIdenticon.asset.name: "filled-account" @@ -57,17 +60,27 @@ SignTransactionModalBase { //: e.g. "Set 100 DAI spending cap in for on " headerMainText: qsTr("Set %1 spending cap in %2 for %3 on %4").arg(formatBigNumber(root.fromTokenAmount, root.fromTokenSymbol)) - .arg(root.accountName).arg(root.serviceProviderURL).arg(root.networkName) + .arg(root.accountName).arg(root.serviceProviderHostname).arg(root.networkName) headerSubTextLayout: [ - StatusBaseText { - Layout.fillWidth: true - horizontalAlignment: Qt.AlignHCenter - wrapMode: Text.WrapAtWordBoundaryOrAnywhere - font.pixelSize: Style.current.additionalTextSize - text: qsTr("The smart contract specified will be able to spend up to %1 of your current or future balance.").arg(formatBigNumber(root.fromTokenAmount, root.fromTokenSymbol)) + ColumnLayout { + spacing: 12 + StatusBaseText { + Layout.fillWidth: true + horizontalAlignment: Qt.AlignHCenter + wrapMode: Text.WrapAtWordBoundaryOrAnywhere + font.pixelSize: Style.current.additionalTextSize + text: qsTr("The smart contract specified will be able to spend up to %1 of your current or future balance.").arg(formatBigNumber(root.fromTokenAmount, root.fromTokenSymbol)) + } + SwapProvidersTermsAndConditionsText { + serviceProviderName: root.serviceProviderName + onLinkClicked: root.openLinkWithConfirmation(root.serviceProviderURL) + onTermsAndConditionClicked: root.openLinkWithConfirmation(root.serviceProviderTandCUrl) + } } ] + infoTagText: qsTr("Review all details before signing") + headerIconComponent: StatusSmartIdenticon { asset.name: root.serviceProviderIcon asset.isImage: true diff --git a/ui/app/AppLayouts/Wallet/popups/swap/SwapModal.qml b/ui/app/AppLayouts/Wallet/popups/swap/SwapModal.qml index f70cb8e04a..49b47629d7 100644 --- a/ui/app/AppLayouts/Wallet/popups/swap/SwapModal.qml +++ b/ui/app/AppLayouts/Wallet/popups/swap/SwapModal.qml @@ -461,8 +461,10 @@ StatusDialog { serviceProviderName: root.swapAdaptor.swapOutputData.txProviderName serviceProviderURL: Constants.swap.paraswapUrl // TODO https://github.com/status-im/status-desktop/issues/15329 + serviceProviderTandCUrl: Constants.swap.paraswapTermsAndConditionUrl // TODO https://github.com/status-im/status-desktop/issues/15329 serviceProviderIcon: Style.png("swap/%1".arg(Constants.swap.paraswapIcon)) // FIXME svg serviceProviderContractAddress: root.swapAdaptor.swapOutputData.approvalContractAddress + serviceProviderHostname: Constants.swap.paraswapHostname onAccepted: { root.swapAdaptor.sendApproveTx() @@ -511,7 +513,8 @@ StatusDialog { slippage: root.swapInputParamsForm.selectedSlippage serviceProviderName: root.swapAdaptor.swapOutputData.txProviderName - serviceProviderURL: Constants.swap.termsAndConditionParaswapUrl // TODO https://github.com/status-im/status-desktop/issues/15329 + serviceProviderURL: Constants.swap.paraswapUrl // TODO https://github.com/status-im/status-desktop/issues/15329 + serviceProviderTandCUrl: Constants.swap.paraswapTermsAndConditionUrl // TODO https://github.com/status-im/status-desktop/issues/15329 onAccepted: { root.swapAdaptor.sendSwapTx() diff --git a/ui/app/AppLayouts/Wallet/popups/swap/SwapSignModal.qml b/ui/app/AppLayouts/Wallet/popups/swap/SwapSignModal.qml index 1146c6e115..e4da1f3358 100644 --- a/ui/app/AppLayouts/Wallet/popups/swap/SwapSignModal.qml +++ b/ui/app/AppLayouts/Wallet/popups/swap/SwapSignModal.qml @@ -12,6 +12,7 @@ import StatusQ.Components 0.1 import AppLayouts.Wallet.panels 1.0 import AppLayouts.Wallet.popups 1.0 +import AppLayouts.Wallet.controls 1.0 import utils 1.0 @@ -42,6 +43,7 @@ SignTransactionModalBase { required property string serviceProviderName required property string serviceProviderURL + required property string serviceProviderTandCUrl title: qsTr("Sign Swap") //: e.g. (swap) 100 DAI to 100 USDT @@ -55,24 +57,10 @@ SignTransactionModalBase { headerMainText: qsTr("Swap %1 to %2 in %3 on %4").arg(formatBigNumber(root.fromTokenAmount, root.fromTokenSymbol)) .arg(formatBigNumber(root.toTokenAmount, root.toTokenSymbol)).arg(root.accountName).arg(root.networkName) headerSubTextLayout: [ - StatusBaseText { - font.pixelSize: Style.current.additionalTextSize - text: qsTr("Powered by") - }, - StatusLinkText { - Layout.topMargin: 1 // compensate for the underline - text: root.serviceProviderName - normalColor: Theme.palette.directColor1 - linkColor: Theme.palette.directColor1 - font.weight: Font.Normal - onClicked: root.openLinkWithConfirmation(root.serviceProviderURL) - }, - StatusIcon { - Layout.leftMargin: -2 - width: 16 - height: 16 - icon: "external-link" - color: Theme.palette.directColor1 + SwapProvidersTermsAndConditionsText { + serviceProviderName: root.serviceProviderName + onLinkClicked: root.openLinkWithConfirmation(root.serviceProviderURL) + onTermsAndConditionClicked: root.openLinkWithConfirmation(root.serviceProviderTandCUrl) } ] infoTagText: qsTr("Review all details before signing") diff --git a/ui/app/AppLayouts/Wallet/stores/RootStore.qml b/ui/app/AppLayouts/Wallet/stores/RootStore.qml index 3d3d70063b..e31a69bcf0 100644 --- a/ui/app/AppLayouts/Wallet/stores/RootStore.qml +++ b/ui/app/AppLayouts/Wallet/stores/RootStore.qml @@ -576,7 +576,7 @@ QtObject { case Constants.swap.paraswapSwapContractAddress: return { "icon": Style.png("swap/%1".arg(Constants.swap.paraswapIcon)), - "url": Constants.swap.paraswapUrl, + "url": Constants.swap.paraswapHostname, "name": Constants.swap.paraswapName, "approvalContractAddress": Constants.swap.paraswapContractAddress, "swapContractAddress": Constants.swap.paraswapContractAddress, diff --git a/ui/app/mainui/AppMain.qml b/ui/app/mainui/AppMain.qml index dad66249fd..731be6ebe6 100644 --- a/ui/app/mainui/AppMain.qml +++ b/ui/app/mainui/AppMain.qml @@ -227,13 +227,13 @@ Item { const networkName = SQUtils.ModelUtils.getByKey(WalletStore.RootStore.filteredFlatModel, "chainId", chainId, "chainName") if(!!fromToken && !!fromAccountName && !!networkName) { const approvalAmount = currencyStore.formatCurrencyAmountFromBigInt(fromAmount, fromToken.symbol, fromToken.decimals) - let toastTitle = qsTr("Setting spending cap: %1 in %2 for %3 on %4").arg(approvalAmount).arg(fromAccountName).arg(Constants.swap.paraswapUrl).arg(networkName) + let toastTitle = qsTr("Setting spending cap: %1 in %2 for %3 on %4").arg(approvalAmount).arg(fromAccountName).arg(Constants.swap.paraswapHostname).arg(networkName) let toastSubtitle = qsTr("View on %1").arg(networkName) let urlLink = "%1/%2".arg(appMain.rootStore.getEtherscanLink(chainId)).arg(txHash) let toastType = Constants.ephemeralNotificationType.normal let icon = "" if(error) { - toastTitle = qsTr("Failed to set spending cap: %1 in %2 for %3 on %4").arg(approvalAmount).arg(fromAccountName).arg(Constants.swap.paraswapUrl).arg(networkName) + toastTitle = qsTr("Failed to set spending cap: %1 in %2 for %3 on %4").arg(approvalAmount).arg(fromAccountName).arg(Constants.swap.paraswapHostname).arg(networkName) toastSubtitle = "" urlLink = "" toastType = Constants.ephemeralNotificationType.danger @@ -251,13 +251,13 @@ Item { if(!!fromToken && !!toToken && !!fromAccountName && !!networkName) { const fromSwapAmount = currencyStore.formatCurrencyAmountFromBigInt(fromAmount, fromToken.symbol, fromToken.decimals) const toSwapAmount = currencyStore.formatCurrencyAmountFromBigInt(toAmount, toToken.symbol, toToken.decimals) - let toastTitle = qsTr("Swapping %1 to %2 in %3 using %4 on %5").arg(fromSwapAmount).arg(toSwapAmount).arg(fromAccountName).arg(Constants.swap.paraswapUrl).arg(networkName) + let toastTitle = qsTr("Swapping %1 to %2 in %3 using %4 on %5").arg(fromSwapAmount).arg(toSwapAmount).arg(fromAccountName).arg(Constants.swap.paraswapHostname).arg(networkName) let toastSubtitle = qsTr("View on %1").arg(networkName) let urlLink = "%1/%2".arg(appMain.rootStore.getEtherscanLink(chainId)).arg(txHash) let toastType = Constants.ephemeralNotificationType.normal let icon = "" if(error) { - toastTitle = qsTr("Failed to swap %1 to %2 in %3 using %4 on %5").arg(fromSwapAmount).arg(toSwapAmount).arg(fromAccountName).arg(Constants.swap.paraswapUrl).arg(networkName) + toastTitle = qsTr("Failed to swap %1 to %2 in %3 using %4 on %5").arg(fromSwapAmount).arg(toSwapAmount).arg(fromAccountName).arg(Constants.swap.paraswapHostname).arg(networkName) toastSubtitle = "" urlLink = "" toastType = Constants.ephemeralNotificationType.danger @@ -294,13 +294,13 @@ Item { const networkName = SQUtils.ModelUtils.getByKey(WalletStore.RootStore.filteredFlatModel, "chainId", chainId, "chainName") if(!!fromToken && !!fromAccountName && !!networkName) { const approvalAmount = currencyStore.formatCurrencyAmountFromBigInt(fromAmount, fromToken.symbol, fromToken.decimals) - let toastTitle = qsTr("Spending cap set: %1 in %2 for %3 on %4").arg(approvalAmount).arg(fromAccountName).arg(Constants.swap.paraswapUrl).arg(networkName) + let toastTitle = qsTr("Spending cap set: %1 in %2 for %3 on %4").arg(approvalAmount).arg(fromAccountName).arg(Constants.swap.paraswapHostname).arg(networkName) const toastSubtitle = qsTr("View on %1").arg(networkName) const urlLink = "%1/%2".arg(appMain.rootStore.getEtherscanLink(chainId)).arg(txHash) let toastType = Constants.ephemeralNotificationType.success let icon = "checkmark-circle" if(!success) { - toastTitle = qsTr("Failed to set spending cap: %1 in %2 for %3 on %4").arg(approvalAmount).arg(fromAccountName).arg(Constants.swap.paraswapUrl).arg(networkName) + toastTitle = qsTr("Failed to set spending cap: %1 in %2 for %3 on %4").arg(approvalAmount).arg(fromAccountName).arg(Constants.swap.paraswapHostname).arg(networkName) toastType = Constants.ephemeralNotificationType.danger icon = "warning" } @@ -316,13 +316,13 @@ Item { if(!!fromToken && !!toToken && !!fromAccountName && !!networkName) { const fromSwapAmount = currencyStore.formatCurrencyAmountFromBigInt(fromAmount, fromToken.symbol, fromToken.decimals) const toSwapAmount = currencyStore.formatCurrencyAmountFromBigInt(toAmount, toToken.symbol, toToken.decimals) - let toastTitle = qsTr("Swapped %1 to %2 in %3 using %4 on %5").arg(fromSwapAmount).arg(toSwapAmount).arg(fromAccountName).arg(Constants.swap.paraswapUrl).arg(networkName) + let toastTitle = qsTr("Swapped %1 to %2 in %3 using %4 on %5").arg(fromSwapAmount).arg(toSwapAmount).arg(fromAccountName).arg(Constants.swap.paraswapHostname).arg(networkName) const toastSubtitle = qsTr("View on %1").arg(networkName) const urlLink = "%1/%2".arg(appMain.rootStore.getEtherscanLink(chainId)).arg(txHash) let toastType = Constants.ephemeralNotificationType.success let icon = "checkmark-circle" if(!success) { - toastTitle = qsTr("Failed to swap %1 to %2 in %3 using %4 on %5").arg(fromSwapAmount).arg(toSwapAmount).arg(fromAccountName).arg(Constants.swap.paraswapUrl).arg(networkName) + toastTitle = qsTr("Failed to swap %1 to %2 in %3 using %4 on %5").arg(fromSwapAmount).arg(toSwapAmount).arg(fromAccountName).arg(Constants.swap.paraswapHostname).arg(networkName) toastType = Constants.ephemeralNotificationType.danger icon = "warning" } diff --git a/ui/imports/utils/Constants.qml b/ui/imports/utils/Constants.qml index c28590db65..136801e9f2 100644 --- a/ui/imports/utils/Constants.qml +++ b/ui/imports/utils/Constants.qml @@ -1426,10 +1426,11 @@ QtObject { this list dynamically */ readonly property string paraswapName: "Paraswap" readonly property string paraswapIcon: "paraswap" - readonly property string paraswapUrl: "app.paraswap.io" + readonly property string paraswapHostname: "app.paraswap.io" + readonly property string paraswapUrl: "https://www.paraswap.io/" readonly property string paraswapApproveContractAddress: "0x216B4B4Ba9F3e719726886d34a177484278Bfcae" readonly property string paraswapSwapContractAddress: "0xDEF171Fe48CF0115B1d80b88dc8eAB59176FEe57" - readonly property string termsAndConditionParaswapUrl: "https://files.paraswap.io/tos_v4.pdf" + readonly property string paraswapTermsAndConditionUrl: "https://files.paraswap.io/tos_v4.pdf" // TOOD #15874: Unify with WalletUtils router error code handling readonly property QtObject errorCodes: QtObject {