feat(@desktop/wallet): Implementation of Sign Swap

fixes #14835
This commit is contained in:
Khushboo Mehta 2024-07-02 00:35:10 +02:00 committed by Khushboo-dev-cpp
parent c934ba74ef
commit 0e458842d2
7 changed files with 193 additions and 31 deletions

View File

@ -48,17 +48,21 @@ SplitView {
modal: false
closePolicy: Popup.CloseOnEscape
destroyOnClose: true
title: qsTr("Approve spending cap")
loading: loadingCheckBox.checked
swapSignApproveInputForm: SwapSignApproveInputForm {
selectedAccountAddress: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240"
selectedNetworkChainId: 11155111
tokensKey: "DAI"
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 {
@ -77,6 +81,7 @@ SplitView {
currencyStore: CurrenciesStore {}
inputFormData: modal.swapSignApproveInputForm
}
txType: isApprovalTx.checked ? SwapSignApprovePopup.TxType.Approve : SwapSignApprovePopup.TxType.Swap
}
}
}
@ -95,6 +100,12 @@ SplitView {
text: "loading"
checked: false
}
CheckBox {
id: isApprovalTx
text: "approve tx"
checked: false
}
}
}
}

View File

@ -20,7 +20,8 @@ ListModel {
{ chainId: 10, address: "0x0000000000000000000000000000000000000000"},
{ chainId: 420, address: "0x0000000000000000000000000000000000000000"},
{ chainId: 42161, address: "0x0000000000000000000000000000000000000000"},
{ chainId: 421613, address: "0x0000000000000000000000000000000000000000"}
{ chainId: 421613, address: "0x0000000000000000000000000000000000000000"},
{ chainId: 11155111, address: "0x0000000000000000000000000000000000000000"},
],
decimals: 18,
type: 1,

View File

@ -331,10 +331,10 @@ StatusDialog {
if(root.swapAdaptor.validSwapProposalReceived) {
if(root.swapAdaptor.swapOutputData.approvalNeeded) {
let approvalGasFeesFiat = root.swapAdaptor.currencyStore.getFiatValue(root.swapAdaptor.swapOutputData.approvalGasFees, Constants.ethToken)
return root.swapAdaptor.currencyStore.formatCurrencyAmount(
root.swapAdaptor.swapOutputData.approvalGasFees,
approvalGasFeesFiat,
root.swapAdaptor.currencyStore.currentCurrency)
} else {
return root.swapAdaptor.currencyStore.formatCurrencyAmount(
root.swapAdaptor.swapOutputData.totalFees,
@ -375,14 +375,9 @@ StatusDialog {
!payPanel.amountEnteredGreaterThanBalance &&
!root.swapAdaptor.approvalPending
onClicked: {
if (root.swapAdaptor.validSwapProposalReceived ){
if(root.swapAdaptor.swapOutputData.approvalNeeded) {
Global.openPopup(swapSignApprovePopup)
}
else {
swapAdaptor.sendSwapTx()
close()
}
if (root.swapAdaptor.validSwapProposalReceived) {
let txType = root.swapAdaptor.swapOutputData.approvalNeeded ? SwapSignApprovePopup.TxType.Approve : SwapSignApprovePopup.TxType.Swap
Global.openPopup(swapSignApprovePopup, {"txType": txType})
}
}
}
@ -401,12 +396,17 @@ StatusDialog {
swapSignApproveInputForm: SwapSignApproveInputForm {
selectedAccountAddress: root.swapInputParamsForm.selectedAccountAddress
selectedNetworkChainId: root.swapInputParamsForm.selectedNetworkChainId
tokensKey: root.swapInputParamsForm.fromTokensKey
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
}
adaptor: SwapSignApproveAdaptor {
swapStore: root.swapAdaptor.swapStore
@ -415,8 +415,14 @@ StatusDialog {
inputFormData: approvePopup.swapSignApproveInputForm
}
onSign: {
root.swapAdaptor.sendApproveTx()
close()
if(txType === SwapSignApprovePopup.TxType.Approve) {
root.swapAdaptor.sendApproveTx()
close()
} else {
root.swapAdaptor.sendSwapTx()
close()
root.close()
}
}
onReject: close()
}

View File

@ -9,6 +9,7 @@ QtObject {
property string fromTokenAmount: ""
property string toTokenAmount: ""
// TODO: this should be string but backend gas_estimate_item.nim passes this as float
property real totalFees: 0
property var bestRoutes: []
property bool hasError

View File

@ -16,14 +16,17 @@ 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 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.tokensKey
value: root.inputFormData.fromTokensKey
}
ModelEntry {
@ -39,4 +42,27 @@ QObject {
key: "chainId"
value: root.inputFormData.selectedNetworkChainId
}
ModelEntry {
id: toTokenEntry
sourceModel: root.walletAssetsStore.walletTokensStore.plainTokensBySymbolModel
key: "key"
value: root.inputFormData.toTokensKey
}
ModelEntry {
id: fromTokenContractAddress
sourceModel: !!root.fromToken ?
root.fromToken.addressPerChain ?? null : null
key: "chainId"
value: root.inputFormData.selectedNetworkChainId
}
ModelEntry {
id: toTokenContractAddress
sourceModel: !!root.toToken ?
root.toToken.addressPerChain ?? null : null
key: "chainId"
value: root.inputFormData.selectedNetworkChainId
}
}

View File

@ -7,11 +7,20 @@ QtObject {
required property string selectedAccountAddress
required property int selectedNetworkChainId
required property string tokensKey
required property string fromTokensKey
required property string fromTokensAmount
required property string toTokensKey
required property string toTokensAmount
required property double selectedSlippage
// TODO: this should be string but backend gas_estimate_item.nim passes this as float
required property double swapFees
// need to check how this is done in new router, right now it is Enum type
required property int estimatedTime
required property string swapProviderName
required property string approvalGasFees
required property string approvalAmountRequired
required property string approvalContractAddress
}

View File

@ -24,18 +24,30 @@ StatusDialog {
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: qsTr("Approve spending cap")
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: Constants.swap.paraswapUrl
subtitle: d.isApproveTx ? Constants.swap.paraswapUrl : qsTr("%1 to %2").arg(payToken.title).arg(receiveToken.title)
contentItem: StatusScrollView {
id: scrollView
@ -55,9 +67,12 @@ StatusDialog {
}
StatusListItem {
width: parent.width
title: SQUtils.AmountsArithmetic.div(
SQUtils.AmountsArithmetic.fromString(swapSignApproveInputForm.approvalAmountRequired),
SQUtils.AmountsArithmetic.fromNumber(1, !!root.adaptor.fromToken ? root.adaptor.fromToken.decimals: 18)).toString()
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: [
@ -74,6 +89,7 @@ StatusDialog {
}
]
}
visible: d.isApproveTx
}
Column {
@ -81,7 +97,73 @@ StatusDialog {
spacing: Style.current.padding
StatusBaseText {
width: parent.width
text: qsTr("Account")
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
@ -117,9 +199,9 @@ StatusDialog {
asset.isImage: true
title: !!root.adaptor.fromToken ?
root.adaptor.fromToken.symbol ?? "" : ""
subTitle: SQUtils.Utils.elideText(contractAddressOnSelectedNetwork.item.address, 6, 4)
subTitle: SQUtils.Utils.elideText(payContractAddressOnSelectedNetwork.item.address, 6, 4)
ModelEntry {
id: contractAddressOnSelectedNetwork
id: payContractAddressOnSelectedNetwork
sourceModel: !!root.adaptor.fromToken ?
root.adaptor.fromToken.addressPerChain ?? null : null
key: "chainId"
@ -135,6 +217,7 @@ StatusDialog {
}
]
}
visible: d.isApproveTx
}
Column {
@ -166,6 +249,7 @@ StatusDialog {
}
]
}
visible: d.isApproveTx
}
Column {
@ -213,14 +297,26 @@ StatusDialog {
anchors.right: parent.right
loading: root.loading
text: {
let feesInFoat = root.adaptor.currencyStore.getFiatValue(root.swapSignApproveInputForm.approvalGasFees, Constants.ethToken)
return root.adaptor.currencyStore.formatCurrencyAmount(feesInFoat, root.adaptor.currencyStore.currentCurrency)
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: root.adaptor.currencyStore.formatCurrencyAmount(root.swapSignApproveInputForm.approvalGasFees, Constants.ethToken)
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)
}
}
}
}
]
@ -235,8 +331,12 @@ StatusDialog {
SwapModalFooterInfoComponent {
titleText: qsTr("Max fees:")
infoText: {
let feesInFoat = root.adaptor.currencyStore.getFiatValue(root.swapSignApproveInputForm.approvalGasFees, Constants.ethToken)
return root.adaptor.currencyStore.formatCurrencyAmount(feesInFoat, root.adaptor.currencyStore.currentCurrency)
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
}
@ -245,6 +345,14 @@ StatusDialog {
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))
loading: root.loading
visible: !d.isApproveTx
}
}