2024-05-28 17:39:41 +00:00
|
|
|
import QtQuick 2.15
|
2024-05-13 17:23:01 +00:00
|
|
|
import QtQuick.Layouts 1.15
|
2024-06-04 11:58:37 +00:00
|
|
|
import QtQml.Models 2.15
|
2024-05-13 17:23:01 +00:00
|
|
|
|
|
|
|
import utils 1.0
|
|
|
|
|
2024-06-10 12:51:33 +00:00
|
|
|
import StatusQ.Controls 0.1
|
2024-05-13 17:23:01 +00:00
|
|
|
import StatusQ.Core 0.1
|
|
|
|
import StatusQ.Core.Theme 0.1
|
2024-06-10 12:51:33 +00:00
|
|
|
import StatusQ.Core.Utils 0.1 as SQUtils
|
2024-05-13 17:23:01 +00:00
|
|
|
import StatusQ.Popups.Dialog 0.1
|
|
|
|
|
2024-05-15 21:22:13 +00:00
|
|
|
import shared.popups.send.controls 1.0
|
2024-06-06 14:05:31 +00:00
|
|
|
import shared.controls 1.0
|
2024-05-15 21:22:13 +00:00
|
|
|
|
2024-05-29 15:42:26 +00:00
|
|
|
import AppLayouts.Wallet.controls 1.0
|
2024-06-13 00:45:33 +00:00
|
|
|
import AppLayouts.Wallet.panels 1.0
|
2024-05-29 15:42:26 +00:00
|
|
|
|
2024-05-13 17:23:01 +00:00
|
|
|
StatusDialog {
|
|
|
|
id: root
|
|
|
|
|
2024-05-15 21:22:13 +00:00
|
|
|
/* This should be the only property which should be used to input
|
|
|
|
parameters to the modal when being launched from elsewhere */
|
|
|
|
required property SwapInputParamsForm swapInputParamsForm
|
|
|
|
required property SwapModalAdaptor swapAdaptor
|
|
|
|
|
|
|
|
objectName: "swapModal"
|
2024-05-13 17:23:01 +00:00
|
|
|
|
2024-05-29 15:42:26 +00:00
|
|
|
implicitWidth: 556
|
|
|
|
topPadding: 0
|
2024-06-04 11:58:37 +00:00
|
|
|
bottomPadding: Style.current.xlPadding
|
2024-05-29 15:42:26 +00:00
|
|
|
leftPadding: Style.current.xlPadding
|
|
|
|
rightPadding: Style.current.xlPadding
|
|
|
|
backgroundColor: Theme.palette.baseColor3
|
2024-05-13 17:23:01 +00:00
|
|
|
|
2024-06-06 14:05:31 +00:00
|
|
|
QtObject {
|
|
|
|
id: d
|
2024-06-13 00:45:33 +00:00
|
|
|
property var debounceFetchSuggestedRoutes: Backpressure.debounce(root, 1000, function() {
|
2024-06-18 22:51:49 +00:00
|
|
|
root.swapAdaptor.fetchSuggestedRoutes(payPanel.rawValue)
|
2024-06-06 14:05:31 +00:00
|
|
|
})
|
2024-06-13 00:45:33 +00:00
|
|
|
|
|
|
|
function fetchSuggestedRoutes() {
|
2024-06-18 17:24:07 +00:00
|
|
|
if (payPanel.valueValid && root.swapInputParamsForm.isFormFilledCorrectly()) {
|
|
|
|
root.swapAdaptor.validSwapProposalReceived = false
|
2024-06-14 11:34:20 +00:00
|
|
|
root.swapAdaptor.swapProposalLoading = true
|
2024-06-18 17:24:07 +00:00
|
|
|
root.swapAdaptor.swapOutputData.resetAllButReceivedTokenValuesForSwap()
|
2024-06-14 11:34:20 +00:00
|
|
|
debounceFetchSuggestedRoutes()
|
|
|
|
}
|
2024-06-13 00:45:33 +00:00
|
|
|
}
|
2024-06-06 14:05:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Connections {
|
|
|
|
target: root.swapInputParamsForm
|
|
|
|
function onFormValuesChanged() {
|
|
|
|
d.fetchSuggestedRoutes()
|
|
|
|
}
|
2024-06-13 00:45:33 +00:00
|
|
|
// refresh the selected asset in payPanel when account/network changes
|
|
|
|
function onSelectedAccountAddressChanged() {
|
|
|
|
payPanel.reevaluateSelectedId()
|
|
|
|
}
|
|
|
|
function onSelectedNetworkChainIdChanged() {
|
2024-06-18 22:51:49 +00:00
|
|
|
networkFilter.selection = [root.swapInputParamsForm.selectedNetworkChainId]
|
2024-06-13 00:45:33 +00:00
|
|
|
payPanel.reevaluateSelectedId()
|
|
|
|
}
|
2024-06-06 14:05:31 +00:00
|
|
|
}
|
|
|
|
|
2024-06-04 11:58:37 +00:00
|
|
|
Behavior on implicitHeight {
|
|
|
|
NumberAnimation { duration: 1000; easing.type: Easing.OutExpo; alwaysRunToEnd: true}
|
|
|
|
}
|
2024-06-18 22:51:49 +00:00
|
|
|
|
2024-06-06 14:05:31 +00:00
|
|
|
onClosed: root.swapAdaptor.reset()
|
|
|
|
|
2024-06-07 12:27:56 +00:00
|
|
|
header: Item {
|
2024-06-20 14:32:56 +00:00
|
|
|
implicitHeight: selector.implicitHeight
|
|
|
|
implicitWidth: selector.implicitWidth
|
2024-05-15 21:22:13 +00:00
|
|
|
anchors.top: parent.top
|
|
|
|
anchors.topMargin: -height - 18
|
2024-06-07 12:27:56 +00:00
|
|
|
AccountSelectorHeader {
|
|
|
|
id: selector
|
|
|
|
control.popup.width: 512
|
|
|
|
model: root.swapAdaptor.nonWatchAccounts
|
|
|
|
selectedAddress: root.swapInputParamsForm.selectedAccountAddress
|
|
|
|
onCurrentAccountAddressChanged: {
|
|
|
|
if (currentAccountAddress !== "" && currentAccountAddress !== root.swapInputParamsForm.selectedAccountAddress) {
|
|
|
|
root.swapInputParamsForm.selectedAccountAddress = currentAccountAddress
|
|
|
|
}
|
|
|
|
}
|
2024-05-15 21:22:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-29 15:42:26 +00:00
|
|
|
contentItem: ColumnLayout {
|
2024-06-06 14:05:31 +00:00
|
|
|
spacing: Style.current.padding
|
2024-06-04 11:58:37 +00:00
|
|
|
clip: true
|
2024-05-29 15:42:26 +00:00
|
|
|
|
2024-06-06 14:05:31 +00:00
|
|
|
// without this Column, the whole popup resizing when the network selector popup is clicked
|
|
|
|
Column {
|
2024-05-29 15:42:26 +00:00
|
|
|
Layout.fillWidth: true
|
2024-06-06 14:05:31 +00:00
|
|
|
spacing: 0
|
|
|
|
RowLayout {
|
|
|
|
width: parent.width
|
|
|
|
spacing: 12
|
|
|
|
HeaderTitleText {
|
|
|
|
Layout.fillWidth: true
|
|
|
|
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
|
|
|
text: qsTr("Swap")
|
|
|
|
}
|
|
|
|
StatusBaseText {
|
|
|
|
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
|
|
|
text: qsTr("On:")
|
|
|
|
color: Theme.palette.baseColor1
|
|
|
|
font.pixelSize: 13
|
|
|
|
lineHeight: 38
|
|
|
|
lineHeightMode: Text.FixedHeight
|
|
|
|
verticalAlignment: Text.AlignVCenter
|
|
|
|
}
|
|
|
|
NetworkFilter {
|
|
|
|
id: networkFilter
|
|
|
|
objectName: "networkFilter"
|
|
|
|
Layout.alignment: Qt.AlignVCenter
|
|
|
|
multiSelection: false
|
2024-06-15 21:33:12 +00:00
|
|
|
showSelectionIndicator: false
|
2024-06-06 14:05:31 +00:00
|
|
|
showTitle: false
|
|
|
|
flatNetworks: root.swapAdaptor.filteredFlatNetworksModel
|
2024-06-15 21:33:12 +00:00
|
|
|
selection: [root.swapInputParamsForm.selectedNetworkChainId]
|
|
|
|
onSelectionChanged: {
|
|
|
|
if (root.swapInputParamsForm.selectedNetworkChainId !== selection[0]) {
|
|
|
|
root.swapInputParamsForm.selectedNetworkChainId = selection[0]
|
|
|
|
}
|
2024-06-07 12:27:56 +00:00
|
|
|
}
|
|
|
|
}
|
2024-05-29 15:42:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-13 00:45:33 +00:00
|
|
|
Item {
|
2024-06-06 14:05:31 +00:00
|
|
|
Layout.fillWidth: true
|
2024-06-13 00:45:33 +00:00
|
|
|
Layout.topMargin: 2
|
|
|
|
Layout.preferredHeight: payPanel.height + receivePanel.height + 4
|
|
|
|
|
|
|
|
SwapInputPanel {
|
|
|
|
id: payPanel
|
|
|
|
objectName: "payPanel"
|
|
|
|
|
|
|
|
anchors {
|
|
|
|
left: parent.left
|
|
|
|
right: parent.right
|
|
|
|
top: parent.top
|
2024-06-06 14:05:31 +00:00
|
|
|
}
|
2024-06-13 00:45:33 +00:00
|
|
|
|
|
|
|
currencyStore: root.swapAdaptor.currencyStore
|
2024-06-18 22:51:49 +00:00
|
|
|
flatNetworksModel: root.swapAdaptor.swapStore.flatNetworks
|
|
|
|
processedAssetsModel: root.swapAdaptor.walletAssetsStore.groupedAccountAssetsModel
|
2024-06-13 00:45:33 +00:00
|
|
|
|
|
|
|
tokenKey: root.swapInputParamsForm.fromTokensKey
|
2024-06-18 22:51:49 +00:00
|
|
|
tokenAmount: root.swapInputParamsForm.fromTokenAmount
|
|
|
|
|
|
|
|
selectedNetworkChainId: root.swapInputParamsForm.selectedNetworkChainId
|
|
|
|
selectedAccountAddress: root.swapInputParamsForm.selectedAccountAddress
|
|
|
|
nonInteractiveTokensKey: receivePanel.selectedHoldingId
|
2024-06-13 00:45:33 +00:00
|
|
|
|
|
|
|
swapSide: SwapInputPanel.SwapSide.Pay
|
2024-06-18 17:24:07 +00:00
|
|
|
swapExchangeButtonWidth: swapExchangeButton.width
|
2024-06-13 00:45:33 +00:00
|
|
|
|
|
|
|
onSelectedHoldingIdChanged: root.swapInputParamsForm.fromTokensKey = selectedHoldingId
|
2024-06-18 17:24:07 +00:00
|
|
|
onValueChanged: {
|
|
|
|
if(root.swapInputParamsForm.fromTokensKey === selectedHoldingId) {
|
|
|
|
root.swapInputParamsForm.fromTokenAmount = !tokenAmount && value === 0 ? "" : value.toLocaleString(locale, 'f', -128)
|
|
|
|
}
|
|
|
|
}
|
2024-06-14 11:34:20 +00:00
|
|
|
onValueValidChanged: d.fetchSuggestedRoutes()
|
2024-06-06 14:05:31 +00:00
|
|
|
}
|
2024-06-13 00:45:33 +00:00
|
|
|
|
|
|
|
SwapInputPanel {
|
|
|
|
id: receivePanel
|
|
|
|
objectName: "receivePanel"
|
|
|
|
|
|
|
|
anchors {
|
|
|
|
left: parent.left
|
|
|
|
right: parent.right
|
|
|
|
bottom: parent.bottom
|
2024-06-10 12:51:33 +00:00
|
|
|
}
|
2024-06-13 00:45:33 +00:00
|
|
|
|
|
|
|
currencyStore: root.swapAdaptor.currencyStore
|
2024-06-18 22:51:49 +00:00
|
|
|
flatNetworksModel: root.swapAdaptor.swapStore.flatNetworks
|
|
|
|
processedAssetsModel: root.swapAdaptor.walletAssetsStore.groupedAccountAssetsModel
|
2024-06-13 00:45:33 +00:00
|
|
|
|
|
|
|
tokenKey: root.swapInputParamsForm.toTokenKey
|
|
|
|
tokenAmount: root.swapAdaptor.validSwapProposalReceived && root.swapAdaptor.toToken ? root.swapAdaptor.swapOutputData.toTokenAmount: root.swapInputParamsForm.toTokenAmount
|
|
|
|
|
2024-06-18 22:51:49 +00:00
|
|
|
selectedNetworkChainId: root.swapInputParamsForm.selectedNetworkChainId
|
|
|
|
selectedAccountAddress: root.swapInputParamsForm.selectedAccountAddress
|
|
|
|
nonInteractiveTokensKey: payPanel.selectedHoldingId
|
|
|
|
|
2024-06-13 00:45:33 +00:00
|
|
|
swapSide: SwapInputPanel.SwapSide.Receive
|
2024-06-18 17:24:07 +00:00
|
|
|
swapExchangeButtonWidth: swapExchangeButton.width
|
2024-06-13 00:45:33 +00:00
|
|
|
|
2024-06-14 11:34:20 +00:00
|
|
|
mainInputLoading: root.swapAdaptor.swapProposalLoading
|
|
|
|
bottomTextLoading: root.swapAdaptor.swapProposalLoading
|
2024-06-13 00:45:33 +00:00
|
|
|
|
|
|
|
onSelectedHoldingIdChanged: root.swapInputParamsForm.toTokenKey = selectedHoldingId
|
|
|
|
|
|
|
|
/* TODO: keep this input as disabled until the work for adding a param to handle to
|
|
|
|
and from tokens inputed is supported by backend under
|
|
|
|
https://github.com/status-im/status-desktop/issues/15095 */
|
|
|
|
interactive: false
|
|
|
|
}
|
|
|
|
|
|
|
|
SwapExchangeButton {
|
2024-06-18 17:24:07 +00:00
|
|
|
id: swapExchangeButton
|
|
|
|
objectName: "swapExchangeButton"
|
2024-06-13 00:45:33 +00:00
|
|
|
anchors.centerIn: parent
|
2024-06-18 17:24:07 +00:00
|
|
|
onClicked: {
|
|
|
|
const tempPayToken = root.swapInputParamsForm.fromTokensKey
|
|
|
|
const tempPayAmount = root.swapInputParamsForm.fromTokenAmount
|
|
|
|
root.swapInputParamsForm.fromTokensKey = root.swapInputParamsForm.toTokenKey
|
|
|
|
root.swapInputParamsForm.fromTokenAmount = !!root.swapAdaptor.swapOutputData.toTokenAmount ? root.swapAdaptor.swapOutputData.toTokenAmount : root.swapInputParamsForm.toTokenAmount
|
|
|
|
root.swapInputParamsForm.toTokenKey = tempPayToken
|
|
|
|
root.swapInputParamsForm.toTokenAmount = tempPayAmount
|
|
|
|
}
|
2024-06-13 00:45:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: remove! Needed only till sign after approval is implemented under
|
|
|
|
https://github.com/status-im/status-desktop/issues/14833 */
|
|
|
|
StatusButton {
|
|
|
|
text: "Final Swap after Approval"
|
|
|
|
visible: root.swapAdaptor.validSwapProposalReceived && root.swapAdaptor.swapOutputData.approvalNeeded
|
|
|
|
onClicked: {
|
|
|
|
swapAdaptor.sendSwapTx()
|
|
|
|
close()
|
2024-06-10 12:51:33 +00:00
|
|
|
}
|
|
|
|
}
|
2024-06-04 11:58:37 +00:00
|
|
|
|
|
|
|
EditSlippagePanel {
|
|
|
|
id: editSlippagePanel
|
|
|
|
objectName: "editSlippagePanel"
|
|
|
|
Layout.fillWidth: true
|
|
|
|
Layout.topMargin: Style.current.padding
|
|
|
|
visible: editSlippageButton.checked
|
|
|
|
selectedToToken: root.swapAdaptor.toToken
|
2024-06-06 14:05:31 +00:00
|
|
|
toTokenAmount: root.swapAdaptor.swapOutputData.toTokenAmount
|
2024-06-04 11:58:37 +00:00
|
|
|
loading: root.swapAdaptor.swapProposalLoading
|
|
|
|
onSlippageValueChanged: {
|
|
|
|
root.swapInputParamsForm.selectedSlippage = slippageValue
|
|
|
|
}
|
|
|
|
}
|
2024-06-06 14:05:31 +00:00
|
|
|
|
|
|
|
ErrorTag {
|
|
|
|
objectName: "errorTag"
|
2024-06-13 00:45:33 +00:00
|
|
|
visible: root.swapAdaptor.swapOutputData.hasError || payPanel.amountEnteredGreaterThanBalance
|
2024-06-06 14:05:31 +00:00
|
|
|
Layout.alignment: Qt.AlignHCenter
|
|
|
|
Layout.topMargin: Style.current.smallPadding
|
2024-06-13 00:45:33 +00:00
|
|
|
text: {
|
|
|
|
if (payPanel.amountEnteredGreaterThanBalance) {
|
|
|
|
return qsTr("Insufficient funds for swap")
|
|
|
|
}
|
|
|
|
return qsTr("An error has occured, please try again")
|
|
|
|
}
|
2024-06-14 11:34:20 +00:00
|
|
|
buttonText: qsTr("Buy crypto")
|
|
|
|
buttonVisible: payPanel.amountEnteredGreaterThanBalance
|
2024-06-13 00:45:33 +00:00
|
|
|
onButtonClicked: Global.openBuyCryptoModalRequested()
|
2024-06-06 14:05:31 +00:00
|
|
|
}
|
2024-06-04 11:58:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
footer: StatusDialogFooter {
|
|
|
|
color: Theme.palette.baseColor3
|
|
|
|
dropShadowEnabled: true
|
|
|
|
leftButtons: ObjectModel {
|
|
|
|
ColumnLayout {
|
|
|
|
Layout.leftMargin: Style.current.padding
|
|
|
|
spacing: 0
|
|
|
|
StatusBaseText {
|
|
|
|
objectName: "maxSlippageText"
|
|
|
|
text: qsTr("Max slippage:")
|
|
|
|
color: Theme.palette.directColor5
|
|
|
|
font.pixelSize: 15
|
|
|
|
font.weight: Font.Medium
|
|
|
|
}
|
|
|
|
RowLayout {
|
|
|
|
StatusBaseText {
|
|
|
|
objectName: "maxSlippageValue"
|
|
|
|
text: "%1%".arg(LocaleUtils.numberToLocaleString(root.swapInputParamsForm.selectedSlippage))
|
|
|
|
color: Theme.palette.directColor4
|
|
|
|
font.pixelSize: 15
|
|
|
|
font.weight: Font.Medium
|
|
|
|
}
|
|
|
|
StatusFlatButton {
|
|
|
|
id: editSlippageButton
|
|
|
|
objectName: "editSlippageButton"
|
|
|
|
checkable: true
|
|
|
|
checked: false
|
|
|
|
icon.name: "edit_pencil"
|
|
|
|
textColor: editSlippageButton.hovered ? Theme.palette.directColor1 : Theme.palette.directColor5
|
|
|
|
size: StatusBaseButton.Size.Tiny
|
|
|
|
hoverColor: Theme.palette.transparent
|
|
|
|
visible: !checked
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
rightButtons: ObjectModel {
|
|
|
|
RowLayout {
|
|
|
|
Layout.rightMargin: Style.current.padding
|
|
|
|
spacing: Style.current.bigPadding
|
|
|
|
ColumnLayout {
|
|
|
|
StatusBaseText {
|
2024-06-06 14:05:31 +00:00
|
|
|
objectName: "maxFeesText"
|
|
|
|
text: qsTr("Max fees:")
|
2024-06-04 11:58:37 +00:00
|
|
|
color: Theme.palette.directColor5
|
|
|
|
font.weight: Font.Medium
|
|
|
|
}
|
|
|
|
StatusTextWithLoadingState {
|
2024-06-06 14:05:31 +00:00
|
|
|
objectName: "maxFeesValue"
|
|
|
|
text: loading ? Constants.dummyText :
|
|
|
|
root.swapAdaptor.validSwapProposalReceived ?
|
|
|
|
root.swapAdaptor.currencyStore.formatCurrencyAmount(
|
|
|
|
root.swapAdaptor.swapOutputData.totalFees,
|
|
|
|
root.swapAdaptor.currencyStore.currentCurrency) :
|
|
|
|
"--"
|
2024-06-04 11:58:37 +00:00
|
|
|
customColor: Theme.palette.directColor4
|
|
|
|
font.weight: Font.Medium
|
|
|
|
loading: root.swapAdaptor.swapProposalLoading
|
|
|
|
}
|
|
|
|
}
|
|
|
|
StatusButton {
|
|
|
|
objectName: "signButton"
|
|
|
|
icon.name: "password"
|
2024-06-06 14:05:31 +00:00
|
|
|
/* TODO: Handling the next step agter approval of spending cap TBD under
|
|
|
|
https://github.com/status-im/status-desktop/issues/14833 */
|
|
|
|
text: root.swapAdaptor.validSwapProposalReceived &&
|
|
|
|
root.swapAdaptor.swapOutputData.approvalNeeded ?
|
|
|
|
qsTr("Approve %1").arg(!!root.swapAdaptor.fromToken ? root.swapAdaptor.fromToken.symbol: "") :
|
|
|
|
qsTr("Swap")
|
2024-06-04 11:58:37 +00:00
|
|
|
disabledColor: Theme.palette.directColor8
|
2024-06-13 00:45:33 +00:00
|
|
|
enabled: root.swapAdaptor.validSwapProposalReceived &&
|
|
|
|
editSlippagePanel.valid &&
|
|
|
|
!payPanel.amountEnteredGreaterThanBalance
|
2024-06-06 14:05:31 +00:00
|
|
|
onClicked: {
|
|
|
|
if (root.swapAdaptor.validSwapProposalReceived ){
|
|
|
|
if(root.swapAdaptor.swapOutputData.approvalNeeded) {
|
|
|
|
swapAdaptor.sendApproveTx()
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
swapAdaptor.sendSwapTx()
|
2024-06-13 00:45:33 +00:00
|
|
|
close()
|
2024-06-06 14:05:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-06-04 11:58:37 +00:00
|
|
|
}
|
|
|
|
}
|
2024-06-06 09:49:13 +00:00
|
|
|
}
|
2024-05-13 17:23:01 +00:00
|
|
|
}
|
|
|
|
}
|