status-desktop/ui/app/AppLayouts/Wallet/popups/swap/SwapModalAdaptor.qml

292 lines
11 KiB
QML
Raw Normal View History

import QtQml 2.15
import SortFilterProxyModel 0.2
import StatusQ 0.1
import StatusQ.Core.Utils 0.1
import utils 1.0
import shared.stores 1.0
import AppLayouts.Wallet 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 SwapInputParamsForm swapFormData
required property SwapOutputData swapOutputData
// the below 2 properties holds the state of finding a swap proposal
property bool validSwapProposalReceived: false
property bool swapProposalLoading: false
// the below 2 properties holds the state of finding a swap proposal
property bool approvalPending: false
property bool approvalSuccessful: false
// 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 string uuid: d.uuid
readonly property var nonWatchAccounts: SortFilterProxyModel {
sourceModel: root.swapStore.accounts
filters: ValueFilter {
roleName: "walletType"
value: Constants.watchWalletType
inverted: true
}
sorters: [
RoleSorter { roleName: "currencyBalanceDouble"; sortOrder: Qt.DescendingOrder },
RoleSorter { roleName: "position"; sortOrder: Qt.AscendingOrder }
]
proxyRoles: [
FastExpressionRole {
name: "accountBalance"
expression: d.processAccountBalance(model.address)
expectedRoles: ["address"]
},
FastExpressionRole {
name: "currencyBalanceDouble"
expression: model.currencyBalance.amount
expectedRoles: ["currencyBalance"]
},
FastExpressionRole {
name: "fromToken"
expression: root.fromToken
},
FastExpressionRole {
name: "colorizedChainPrefixes"
function getChainShortNames(chainIds) {
const chainShortNames = root.getNetworkShortNames(chainIds)
return WalletUtils.colorizedChainPrefix(chainShortNames)
}
expression: getChainShortNames(model.preferredSharingChainIds)
expectedRoles: ["preferredSharingChainIds"]
}
]
}
readonly property SortFilterProxyModel filteredFlatNetworksModel: SortFilterProxyModel {
sourceModel: root.swapStore.flatNetworks
filters: ValueFilter { roleName: "isTest"; value: root.swapStore.areTestNetworksEnabled }
}
signal suggestedRoutesReady()
QtObject {
id: d
property string uuid
// storing txHash to verify against tx completed event
property string txHash
readonly property SubmodelProxyModel filteredBalancesModel: SubmodelProxyModel {
sourceModel: root.walletAssetsStore.baseGroupedAccountAssetModel
submodelRoleName: "balances"
delegateModel: SortFilterProxyModel {
sourceModel: joinModel
filters: ValueFilter {
roleName: "chainId"
value: root.swapFormData.selectedNetworkChainId
}
readonly property LeftJoinModel joinModel: LeftJoinModel {
leftModel: submodel
rightModel: root.swapStore.flatNetworks
joinRole: "chainId"
}
}
}
function processAccountBalance(address) {
if (!root.swapFormData.fromTokensKey || !root.fromToken) {
return null
}
let network = ModelUtils.getByKey(root.filteredFlatNetworksModel, "chainId", root.swapFormData.selectedNetworkChainId)
if (!network) {
return null
}
let balancesModel = ModelUtils.getByKey(filteredBalancesModel, "tokensKey", root.swapFormData.fromTokensKey, "balances")
let accountBalance = ModelUtils.getByKey(balancesModel, "account", address)
if(accountBalance && accountBalance.balance !== "0") {
accountBalance.formattedBalance = root.formatCurrencyAmountFromBigInt(accountBalance.balance, root.fromToken.symbol, root.fromToken.decimals)
return accountBalance
}
return {
balance: "0",
iconUrl: network.iconUrl,
chainColor: network.chainColor,
formattedBalance: "0 %1".arg(root.fromToken.symbol)
}
}
}
ModelEntry {
id: fromTokenEntry
sourceModel: root.walletAssetsStore.walletTokensStore.plainTokensBySymbolModel
key: "key"
value: root.swapFormData.fromTokensKey
}
ModelEntry {
id: toTokenEntry
sourceModel: root.walletAssetsStore.walletTokensStore.plainTokensBySymbolModel
key: "key"
value: root.swapFormData.toTokenKey
}
ModelEntry {
id: selectedAccountEntry
sourceModel: root.nonWatchAccounts
key: "address"
value: root.swapFormData.selectedAccountAddress
}
Connections {
target: root.swapStore
function onSuggestedRoutesReady(txRoutes) {
if (txRoutes.uuid !== d.uuid) {
// Suggested routes for a different fetch, ignore
return
}
root.swapOutputData.reset()
root.validSwapProposalReceived = false
root.swapProposalLoading = false
root.swapOutputData.rawPaths = txRoutes.rawPaths
// if valid route was found
if(txRoutes.suggestedRoutes.count === 1) {
root.validSwapProposalReceived = true
root.swapOutputData.toTokenAmount = AmountsArithmetic.div(AmountsArithmetic.fromString(txRoutes.amountToReceive), AmountsArithmetic.fromNumber(1, root.toToken.decimals)).toString()
let gasTimeEstimate = txRoutes.gasTimeEstimate
let totalTokenFeesInFiat = 0
if (!!root.fromToken && !!root.fromToken.marketDetails && !!root.fromToken.marketDetails.currencyPrice)
totalTokenFeesInFiat = gasTimeEstimate.totalTokenFees * root.fromToken.marketDetails.currencyPrice.amount
root.swapOutputData.totalFees = root.currencyStore.getFiatValue(gasTimeEstimate.totalFeesInEth, Constants.ethToken) + totalTokenFeesInFiat
let bestPath = ModelUtils.get(txRoutes.suggestedRoutes, 0, "route")
root.swapOutputData.approvalNeeded = !!bestPath ? bestPath.approvalRequired: false
root.swapOutputData.approvalGasFees = !!bestPath ? bestPath.approvalGasFees.toString() : ""
root.swapOutputData.approvalAmountRequired = !!bestPath ? bestPath.approvalAmountRequired: ""
root.swapOutputData.approvalContractAddress = !!bestPath ? bestPath.approvalContractAddress: ""
root.swapOutputData.estimatedTime = !!bestPath ? bestPath.estimatedTime: Constants.TransactionEstimatedTime.Unknown
root.swapOutputData.txProviderName = !!bestPath ? bestPath.bridgeName: ""
}
else {
root.swapOutputData.hasError = true
}
root.suggestedRoutesReady()
}
function onTransactionSent(chainId, txHash, uuid, error) {
if(root.swapOutputData.approvalNeeded) {
if (uuid !== d.uuid || !!error) {
root.approvalPending = false
root.approvalSuccessful = false
return
}
root.approvalPending = true
d.txHash = txHash
}
}
function onTransactionSendingComplete(txHash, success) {
if(d.txHash === txHash && root.swapOutputData.approvalNeeded && root.approvalPending) {
root.approvalPending = false
root.approvalSuccessful = success
d.txHash = ""
}
}
}
function reset() {
root.swapFormData.resetFormData()
root.swapOutputData.reset()
root.validSwapProposalReceived = false
root.swapProposalLoading = false
root.approvalPending = false
root.approvalSuccessful = false
d.txHash = ""
}
function getNetworkShortNames(chainIds) {
var networkString = ""
let chainIdsArray = chainIds.split(":")
for (let i = 0; i< chainIdsArray.length; i++) {
let nwShortName = ModelUtils.getByKey(root.filteredFlatNetworksModel, "chainId", Number(chainIdsArray[i]), "shortName")
if(!!nwShortName) {
networkString = networkString + nwShortName + ':'
}
}
return networkString
}
function formatCurrencyAmount(balance, symbol, options = null, locale = null) {
return root.currencyStore.formatCurrencyAmount(balance, symbol, options, locale)
}
function formatCurrencyAmountFromBigInt(balance, symbol, decimals, options = null) {
return root.currencyStore.formatCurrencyAmountFromBigInt(balance, symbol, decimals, options)
}
function getDisabledChainIds(enabledChainId) {
let disabledChainIds = []
let chainIds = ModelUtils.modelToFlatArray(root.filteredFlatNetworksModel, "chainId")
for (let i = 0; i < chainIds.length; i++) {
if (chainIds[i] !== enabledChainId) {
disabledChainIds.push(chainIds[i])
}
}
return disabledChainIds.join(":")
}
function fetchSuggestedRoutes(cryptoValueInRaw) {
root.swapFormData.toTokenAmount = ""
if (root.swapFormData.isFormFilledCorrectly() && !!cryptoValueInRaw) {
// Identify new swap with a different uuid
d.uuid = Utils.uuid()
root.swapProposalLoading = true
let account = selectedAccountEntry.item
let accountAddress = account.address
let disabledChainIds = getDisabledChainIds(root.swapFormData.selectedNetworkChainId)
root.swapStore.fetchSuggestedRoutes(d.uuid, accountAddress, accountAddress,
cryptoValueInRaw, "0", root.swapFormData.fromTokensKey, root.swapFormData.toTokenKey,
disabledChainIds, disabledChainIds, Constants.SendType.Swap, "")
} else {
root.swapProposalLoading = false
root.swapOutputData.reset()
}
}
function sendApproveTx() {
root.approvalPending = true
let account = selectedAccountEntry.item
let accountAddress = account.address
root.swapStore.authenticateAndTransfer(d.uuid, accountAddress, accountAddress,
root.swapFormData.fromTokensKey, root.swapFormData.toTokenKey,
Constants.SendType.Approve, "", false, root.swapOutputData.rawPaths, "")
}
function sendSwapTx() {
let account = selectedAccountEntry.item
let accountAddress = account.address
root.swapStore.authenticateAndTransfer(d.uuid, accountAddress, accountAddress,
root.swapFormData.fromTokensKey, root.swapFormData.toTokenKey,
Constants.SendType.Swap, "", false, root.swapOutputData.rawPaths, root.swapFormData.selectedSlippage)
}
}