parent
9cf4c59ba0
commit
e2949ad6e7
|
@ -76,6 +76,10 @@ proc init*(self: Controller) =
|
|||
var data = WalletSignal(e)
|
||||
self.delegate.prepareSignaturesForTransactions(data.txHashes)
|
||||
|
||||
self.events.on(SIGNAL_TRANSACTION_SENDING_COMPLETE) do(e:Args):
|
||||
let args = TransactionMinedArgs(e)
|
||||
self.delegate.transactionSendingComplete(args.transactionHash, args.success)
|
||||
|
||||
proc getWalletAccounts*(self: Controller): seq[wallet_account_service.WalletAccountDto] =
|
||||
return self.walletAccountService.getWalletAccounts()
|
||||
|
||||
|
|
|
@ -92,3 +92,6 @@ method onTransactionSigned*(self: AccessInterface, keycardFlowType: string, keyc
|
|||
|
||||
method hasGas*(self: AccessInterface, accountAddress: string, chainId: int, nativeGasSymbol: string, requiredGas: float): bool {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method transactionSendingComplete*(self: AccessInterface, txHash: string, success: bool) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
|
|
@ -171,7 +171,9 @@ proc convertTransactionPathDtoToSuggestedRouteItem(self: Module, path: Transacti
|
|||
isFirstSimpleTx = path.isFirstSimpleTx,
|
||||
isFirstBridgeTx = path.isFirstBridgeTx,
|
||||
approvalRequired = path.approvalRequired,
|
||||
approvalGasFees = path.approvalGasFees
|
||||
approvalGasFees = path.approvalGasFees,
|
||||
approvalAmountRequired = $path.approvalAmountRequired,
|
||||
approvalContractAddress = path.approvalContractAddress
|
||||
)
|
||||
|
||||
method refreshWalletAccounts*(self: Module) =
|
||||
|
@ -445,3 +447,6 @@ method splitAndFormatAddressPrefix*(self: Module, text : string, updateInStore:
|
|||
|
||||
method hasGas*(self: Module, accountAddress: string, chainId: int, nativeGasSymbol: string, requiredGas: float): bool =
|
||||
return self.controller.hasGas(accountAddress, chainId, nativeGasSymbol, requiredGas)
|
||||
|
||||
method transactionSendingComplete*(self: Module, txHash: string, success: bool) =
|
||||
self.view.sendtransactionSendingCompleteSignal(txHash, success)
|
||||
|
|
|
@ -20,6 +20,8 @@ QtObject:
|
|||
isFirstBridgeTx: bool
|
||||
approvalRequired: bool
|
||||
approvalGasFees: float
|
||||
approvalAmountRequired: string
|
||||
approvalContractAddress: string
|
||||
|
||||
proc setup*(self: SuggestedRouteItem,
|
||||
bridgeName: string,
|
||||
|
@ -37,7 +39,9 @@ QtObject:
|
|||
isFirstSimpleTx: bool,
|
||||
isFirstBridgeTx: bool,
|
||||
approvalRequired: bool,
|
||||
approvalGasFees: float
|
||||
approvalGasFees: float,
|
||||
approvalAmountRequired: string,
|
||||
approvalContractAddress: string,
|
||||
) =
|
||||
self.QObject.setup
|
||||
self.bridgeName = bridgeName
|
||||
|
@ -56,6 +60,8 @@ QtObject:
|
|||
self.isFirstBridgeTx = isFirstBridgeTx
|
||||
self.approvalRequired = approvalRequired
|
||||
self.approvalGasFees = approvalGasFees
|
||||
self.approvalAmountRequired = approvalAmountRequired
|
||||
self.approvalContractAddress = approvalContractAddress
|
||||
|
||||
proc delete*(self: SuggestedRouteItem) =
|
||||
self.QObject.delete
|
||||
|
@ -76,11 +82,14 @@ QtObject:
|
|||
isFirstSimpleTx: bool = false,
|
||||
isFirstBridgeTx: bool = false,
|
||||
approvalRequired: bool = false,
|
||||
approvalGasFees: float = 0
|
||||
approvalGasFees: float = 0,
|
||||
approvalAmountRequired: string = "",
|
||||
approvalContractAddress: string = ""
|
||||
): SuggestedRouteItem =
|
||||
new(result, delete)
|
||||
result.setup(bridgeName, fromNetwork, toNetwork, maxAmountIn, amountIn, amountOut, gasAmount, gasFees, tokenFees,
|
||||
cost, estimatedTime, amountInLocked, isFirstSimpleTx, isFirstBridgeTx, approvalRequired, approvalGasFees)
|
||||
cost, estimatedTime, amountInLocked, isFirstSimpleTx, isFirstBridgeTx, approvalRequired, approvalGasFees,
|
||||
approvalAmountRequired, approvalContractAddress)
|
||||
|
||||
proc `$`*(self: SuggestedRouteItem): string =
|
||||
result = "SuggestedRouteItem("
|
||||
|
@ -100,6 +109,8 @@ QtObject:
|
|||
result = result & "\nisFirstBridgeTx: " & $self.isFirstBridgeTx
|
||||
result = result & "\napprovalRequired: " & $self.approvalRequired
|
||||
result = result & "\napprovalGasFees: " & $self.approvalGasFees
|
||||
result = result & "\napprovalAmountRequired: " & $self.approvalAmountRequired
|
||||
result = result & "\napprovalContractAddress: " & $self.approvalContractAddress
|
||||
result = result & ")"
|
||||
|
||||
proc bridgeNameChanged*(self: SuggestedRouteItem) {.signal.}
|
||||
|
@ -213,3 +224,17 @@ QtObject:
|
|||
QtProperty[float] approvalGasFees:
|
||||
read = getApprovalGasFees
|
||||
notify = approvalGasFeesChanged
|
||||
|
||||
proc approvalAmountRequiredChanged*(self: SuggestedRouteItem) {.signal.}
|
||||
proc getApprovalAmountRequired*(self: SuggestedRouteItem): string {.slot.} =
|
||||
return self.approvalAmountRequired
|
||||
QtProperty[string] approvalAmountRequired:
|
||||
read = getApprovalAmountRequired
|
||||
notify = approvalAmountRequiredChanged
|
||||
|
||||
proc approvalContractAddressChanged*(self: SuggestedRouteItem) {.signal.}
|
||||
proc getApprovalContractAddress*(self: SuggestedRouteItem): string {.slot.} =
|
||||
return self.approvalContractAddress
|
||||
QtProperty[string] approvalContractAddress:
|
||||
read = getApprovalContractAddress
|
||||
notify = approvalContractAddressChanged
|
||||
|
|
|
@ -388,3 +388,7 @@ QtObject:
|
|||
|
||||
self.delegate.authenticateAndTransferWithPaths(accountFrom, accountTo, token,
|
||||
toToken, uuid, sendType, tokenName, tokenIsOwnerToken, rawPaths, slippagePercentage)
|
||||
|
||||
proc transactionSendingComplete*(self: View, txHash: string, success: bool) {.signal.}
|
||||
proc sendtransactionSendingCompleteSignal*(self: View, txHash: string, success: bool) =
|
||||
self.transactionSendingComplete(txHash, success)
|
||||
|
|
|
@ -45,6 +45,7 @@ const SIGNAL_HISTORY_ERROR* = "historyError"
|
|||
const SIGNAL_CRYPTO_SERVICES_READY* = "cryptoServicesReady"
|
||||
const SIGNAL_TRANSACTION_DECODED* = "transactionDecoded"
|
||||
const SIGNAL_OWNER_TOKEN_SENT* = "ownerTokenSent"
|
||||
const SIGNAL_TRANSACTION_SENDING_COMPLETE* = "transactionSendingComplete"
|
||||
|
||||
const SIMPLE_TX_BRIDGE_NAME = "Transfer"
|
||||
const HOP_TX_BRIDGE_NAME = "Hop"
|
||||
|
@ -170,6 +171,7 @@ QtObject:
|
|||
if tokenMetadata.isOwnerToken:
|
||||
let status = if receivedData.success: ContractTransactionStatus.Completed else: ContractTransactionStatus.Failed
|
||||
self.events.emit(SIGNAL_OWNER_TOKEN_SENT, OwnerTokenSentArgs(chainId: receivedData.chainId, txHash: receivedData.transactionHash, tokenName: tokenMetadata.tokenName, status: status))
|
||||
self.events.emit(SIGNAL_TRANSACTION_SENDING_COMPLETE, receivedData)
|
||||
except Exception as e:
|
||||
debug "Not the owner token transfer", msg=e.msg
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ import QtQuick 2.15
|
|||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import SortFilterProxyModel 0.2
|
||||
import QtTest 1.15
|
||||
|
||||
import StatusQ 0.1
|
||||
import StatusQ.Core 0.1
|
||||
|
@ -32,6 +33,16 @@ SplitView {
|
|||
}
|
||||
|
||||
readonly property SwapTransactionRoutes dummySwapTransactionRoutes: SwapTransactionRoutes{}
|
||||
|
||||
property string uuid
|
||||
|
||||
function resetValues() {
|
||||
accountComboBox.currentIndex = 0
|
||||
fromTokenComboBox.currentIndex = 0
|
||||
swapInput.text = ""
|
||||
fetchSuggestedRoutesSpy.clear()
|
||||
authenticateAndTransferSpy.clear()
|
||||
}
|
||||
}
|
||||
|
||||
PopupBackground {
|
||||
|
@ -54,6 +65,9 @@ SplitView {
|
|||
SwapStore {
|
||||
id: dSwapStore
|
||||
signal suggestedRoutesReady(var txRoutes)
|
||||
signal transactionSent(var chainId,var txHash, var uuid, var error)
|
||||
signal transactionSendingComplete(var txHash, var success)
|
||||
|
||||
readonly property var accounts: WalletAccountsModel {}
|
||||
readonly property var flatNetworks: NetworksModel.flatNetworks
|
||||
readonly property bool areTestNetworksEnabled: areTestNetworksEnabledCheckbox.checked
|
||||
|
@ -64,16 +78,35 @@ SplitView {
|
|||
accountTo, "amount = ", amount, " tokenFrom = ", tokenFrom, " tokenTo = ", tokenTo,
|
||||
" disabledFromChainIDs = ", disabledFromChainIDs, " disabledToChainIDs = ", disabledToChainIDs,
|
||||
" preferredChainIDs = ", preferredChainIDs, " sendType =", sendType, " lockedInAmounts = ", lockedInAmounts)
|
||||
fetchSuggestedRoutesSignal()
|
||||
}
|
||||
function authenticateAndTransfer(uuid, accountFrom, accountTo, tokenFrom,
|
||||
tokenTo, sendType, tokenName, tokenIsOwnerToken, paths) {
|
||||
console.debug("authenticateAndTransfer called >> uuid ", uuid, " accountFrom = ",accountFrom, " accountTo =",
|
||||
accountTo, "tokenFrom = ",tokenFrom, " tokenTo = ",tokenTo, " sendType = ", sendType,
|
||||
" tokenName = ", tokenName, " tokenIsOwnerToken = ", tokenIsOwnerToken, " paths = ", paths)
|
||||
d.uuid = uuid
|
||||
authenticateAndTransferSignal()
|
||||
}
|
||||
function getWei2Eth(wei, decimals) {
|
||||
return wei/(10**decimals)
|
||||
}
|
||||
|
||||
// only for testing
|
||||
signal fetchSuggestedRoutesSignal()
|
||||
signal authenticateAndTransferSignal()
|
||||
}
|
||||
|
||||
SignalSpy {
|
||||
id: fetchSuggestedRoutesSpy
|
||||
target: dSwapStore
|
||||
signalName: "fetchSuggestedRoutesSignal"
|
||||
}
|
||||
|
||||
SignalSpy {
|
||||
id: authenticateAndTransferSpy
|
||||
target: dSwapStore
|
||||
signalName: "authenticateAndTransferSignal"
|
||||
}
|
||||
|
||||
TokensStore {
|
||||
|
@ -99,7 +132,7 @@ SplitView {
|
|||
accountComboBox.currentIndex = accountComboBox.indexOfValue(selectedAccountAddress)
|
||||
}
|
||||
}
|
||||
swapOutputData: SwapOutputData{}
|
||||
swapOutputData: SwapOutputData {}
|
||||
}
|
||||
|
||||
Component {
|
||||
|
@ -166,11 +199,33 @@ SplitView {
|
|||
property: "fromTokenAmount"
|
||||
value: swapInput.text
|
||||
}
|
||||
Binding {
|
||||
target: swapInputParamsForm
|
||||
property: "selectedNetworkChainId"
|
||||
value: networksComboBox.currentValue ?? -1
|
||||
}
|
||||
Binding {
|
||||
target: swapInputParamsForm
|
||||
property: "selectedAccountAddress"
|
||||
value: accountComboBox.currentValue ?? ""
|
||||
}
|
||||
Binding {
|
||||
target: swapInputParamsForm
|
||||
property: "fromTokenAmount"
|
||||
value: swapInput.text
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: approveTxButton
|
||||
function onClicked() {
|
||||
modal.swapAdaptor.sendApproveTx()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Pane {
|
||||
ScrollView {
|
||||
id: rightPanel
|
||||
SplitView.minimumWidth: 300
|
||||
SplitView.preferredWidth: 300
|
||||
|
@ -237,11 +292,93 @@ SplitView {
|
|||
currentIndex: 1
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "simulate happy path no approval needed"
|
||||
onClicked: {
|
||||
d.resetValues()
|
||||
fromTokenComboBox.currentIndex = 0
|
||||
swapInput.text = "0.2"
|
||||
fetchSuggestedRoutesSpy.wait()
|
||||
Backpressure.debounce(this, 250, () => {
|
||||
dSwapStore.suggestedRoutesReady(d.dummySwapTransactionRoutes.txHasRouteNoApproval)
|
||||
})()
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "simulate happy path with approval needed"
|
||||
onClicked: {
|
||||
d.resetValues()
|
||||
accountComboBox.currentIndex = 2
|
||||
fromTokenComboBox.currentIndex = 2
|
||||
swapInput.text = "0.1"
|
||||
fetchSuggestedRoutesSpy.wait()
|
||||
Backpressure.debounce(this, 1000, () => {
|
||||
dSwapStore.suggestedRoutesReady(d.dummySwapTransactionRoutes.txHasRoutesApprovalNeeded)
|
||||
})()
|
||||
Backpressure.debounce(this, 1500, () => {approveTxButton.clicked()})()
|
||||
authenticateAndTransferSpy.wait()
|
||||
Backpressure.debounce(this, 1000, () => {
|
||||
dSwapStore.transactionSent(networksComboBox.currentValue, "0x877ffe47fc29340312611d4e833ab189fe4f4152b01cc9a05bb4125b81b2a89a", d.uuid, "")
|
||||
})()
|
||||
Backpressure.debounce(this, 2000, () => {
|
||||
dSwapStore.transactionSendingComplete("0x877ffe47fc29340312611d4e833ab189fe4f4152b01cc9a05bb4125b81b2a89a", true)
|
||||
})()
|
||||
fetchSuggestedRoutesSpy.wait()
|
||||
Backpressure.debounce(this, 1000, () => {
|
||||
dSwapStore.suggestedRoutesReady(d.dummySwapTransactionRoutes.txHasRouteNoApproval)
|
||||
})()
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "simulate fetching proposal error"
|
||||
onClicked: {
|
||||
d.resetValues()
|
||||
fromTokenComboBox.currentIndex = 0
|
||||
swapInput.text = "0.2"
|
||||
fetchSuggestedRoutesSpy.wait()
|
||||
Backpressure.debounce(this, 250, () => {
|
||||
dSwapStore.suggestedRoutesReady(d.dummySwapTransactionRoutes.txNoRoutes)
|
||||
|
||||
})()
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "simulate approval failed"
|
||||
onClicked: {
|
||||
d.resetValues()
|
||||
accountComboBox.currentIndex = 2
|
||||
fromTokenComboBox.currentIndex = 2
|
||||
swapInput.text = "0.1"
|
||||
fetchSuggestedRoutesSpy.wait()
|
||||
Backpressure.debounce(this, 1000, () => {
|
||||
dSwapStore.suggestedRoutesReady(d.dummySwapTransactionRoutes.txHasRoutesApprovalNeeded)
|
||||
})()
|
||||
Backpressure.debounce(this, 1500, () => {approveTxButton.clicked()})()
|
||||
authenticateAndTransferSpy.wait()
|
||||
Backpressure.debounce(this, 1000, () => {
|
||||
dSwapStore.transactionSent(networksComboBox.currentValue, "0x877ffe47fc29340312611d4e833ab189fe4f4152b01cc9a05bb4125b81b2a89a", d.uuid, "")
|
||||
})()
|
||||
Backpressure.debounce(this, 2000, () => {
|
||||
dSwapStore.transactionSendingComplete("0x877ffe47fc29340312611d4e833ab189fe4f4152b01cc9a05bb4125b81b2a89a", false)
|
||||
})()
|
||||
}
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
id: advancedSignalsCheckBox
|
||||
text: "show advanced signals for testing"
|
||||
checked: false
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "emit no routes found event"
|
||||
onClicked: {
|
||||
dSwapStore.suggestedRoutesReady(d.dummySwapTransactionRoutes.txNoRoutes)
|
||||
}
|
||||
visible: advancedSignalsCheckBox.checked
|
||||
}
|
||||
|
||||
Button {
|
||||
|
@ -249,6 +386,7 @@ SplitView {
|
|||
onClicked: {
|
||||
dSwapStore.suggestedRoutesReady(d.dummySwapTransactionRoutes.txHasRouteNoApproval)
|
||||
}
|
||||
visible: advancedSignalsCheckBox.checked
|
||||
}
|
||||
|
||||
Button {
|
||||
|
@ -256,6 +394,45 @@ SplitView {
|
|||
onClicked: {
|
||||
dSwapStore.suggestedRoutesReady(d.dummySwapTransactionRoutes.txHasRoutesApprovalNeeded)
|
||||
}
|
||||
visible: advancedSignalsCheckBox.checked
|
||||
}
|
||||
|
||||
Button {
|
||||
id: approveTxButton
|
||||
text: "call approveTX"
|
||||
visible: advancedSignalsCheckBox.checked
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "emit transactionSent successful"
|
||||
onClicked: {
|
||||
dSwapStore.transactionSent(networksComboBox.currentValue, "0x877ffe47fc29340312611d4e833ab189fe4f4152b01cc9a05bb4125b81b2a89a", d.uuid, "")
|
||||
}
|
||||
visible: advancedSignalsCheckBox.checked
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "emit transactionSent failure"
|
||||
onClicked: {
|
||||
dSwapStore.transactionSent(networksComboBox.currentValue, "", d.uuid, "no password given")
|
||||
}
|
||||
visible: advancedSignalsCheckBox.checked
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "emit approval completed successfully"
|
||||
onClicked: {
|
||||
dSwapStore.transactionSendingComplete("0x877ffe47fc29340312611d4e833ab189fe4f4152b01cc9a05bb4125b81b2a89a", true)
|
||||
}
|
||||
visible: advancedSignalsCheckBox.checked
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "emit approval completed with failure"
|
||||
onClicked: {
|
||||
dSwapStore.transactionSendingComplete("0x877ffe47fc29340312611d4e833ab189fe4f4152b01cc9a05bb4125b81b2a89a", false)
|
||||
}
|
||||
visible: advancedSignalsCheckBox.checked
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
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
|
||||
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"
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Pane {
|
||||
id: rightPanel
|
||||
SplitView.minimumWidth: 300
|
||||
SplitView.preferredWidth: 300
|
||||
SplitView.minimumHeight: 300
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 10
|
||||
|
||||
CheckBox {
|
||||
id: loadingCheckBox
|
||||
text: "loading"
|
||||
checked: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// category: Popups
|
|
@ -413,7 +413,7 @@ Item {
|
|||
mouseClick(delToTest)
|
||||
|
||||
// check input value and state
|
||||
waitForItemPolished(controlUnderTest)
|
||||
waitForRendering(controlUnderTest)
|
||||
|
||||
compare(amountToSendInput.input.text, "5.42")
|
||||
const marketPrice = !!amountToSendInput.selectedHolding ? amountToSendInput.selectedHolding.marketDetails.currencyPrice.amount : 0
|
||||
|
|
|
@ -28,6 +28,9 @@ Item {
|
|||
|
||||
readonly property var swapStore: SwapStore {
|
||||
signal suggestedRoutesReady(var txRoutes)
|
||||
signal transactionSent(var chainId,var txHash, var uuid, var error)
|
||||
signal transactionSendingComplete(var txHash, var success)
|
||||
|
||||
readonly property var accounts: WalletAccountsModel {}
|
||||
readonly property var flatNetworks: NetworksModel.flatNetworks
|
||||
readonly property bool areTestNetworksEnabled: true
|
||||
|
@ -35,7 +38,13 @@ Item {
|
|||
return wei/(10**decimals)
|
||||
}
|
||||
function fetchSuggestedRoutes(uuid, accountFrom, accountTo, amount, tokenFrom, tokenTo,
|
||||
disabledFromChainIDs, disabledToChainIDs, preferredChainIDs, sendType, lockedInAmounts) {}
|
||||
disabledFromChainIDs, disabledToChainIDs, preferredChainIDs, sendType, lockedInAmounts) {
|
||||
swapStore.fetchSuggestedRoutesCalled()
|
||||
}
|
||||
function authenticateAndTransfer(uuid, accountFrom, accountTo, tokenFrom,
|
||||
tokenTo, sendType, tokenName, tokenIsOwnerToken, paths) {}
|
||||
// local signals for testing function calls
|
||||
signal fetchSuggestedRoutesCalled()
|
||||
}
|
||||
|
||||
readonly property var swapAdaptor: SwapModalAdaptor {
|
||||
|
@ -81,6 +90,12 @@ Item {
|
|||
signalName: "formValuesChanged"
|
||||
}
|
||||
|
||||
SignalSpy {
|
||||
id: fetchSuggestedRoutesCalled
|
||||
target: swapStore
|
||||
signalName: "fetchSuggestedRoutesCalled"
|
||||
}
|
||||
|
||||
TestCase {
|
||||
name: "SwapModal"
|
||||
when: windowShown
|
||||
|
@ -547,17 +562,17 @@ Item {
|
|||
|
||||
// set input values in the form correctly
|
||||
root.swapFormData.fromTokensKey = root.swapAdaptor.walletAssetsStore.walletTokensStore.plainTokensBySymbolModel.get(0).key
|
||||
compare(formValuesChanged.count, 1)
|
||||
formValuesChanged.wait()
|
||||
root.swapFormData.toTokenKey = root.swapAdaptor.walletAssetsStore.walletTokensStore.plainTokensBySymbolModel.get(1).key
|
||||
root.swapFormData.fromTokenAmount = "0.001"
|
||||
compare(formValuesChanged.count, 2)
|
||||
formValuesChanged.wait()
|
||||
root.swapFormData.selectedNetworkChainId = root.swapAdaptor.filteredFlatNetworksModel.get(0).chainId
|
||||
compare(formValuesChanged.count, 3)
|
||||
formValuesChanged.wait()
|
||||
root.swapFormData.selectedAccountAddress = root.swapAdaptor.nonWatchAccounts.get(0).address
|
||||
compare(formValuesChanged.count, 4)
|
||||
formValuesChanged.wait()
|
||||
|
||||
// wait for fetchSuggestedRoutes function to be called
|
||||
wait(1000)
|
||||
fetchSuggestedRoutesCalled.wait()
|
||||
|
||||
// verify loading state was set and no errors currently
|
||||
verifyLoadingAndNoErrorsState(payPanel, receivePanel)
|
||||
|
@ -591,10 +606,10 @@ Item {
|
|||
|
||||
// edit some params to retry swap
|
||||
root.swapFormData.fromTokenAmount = "0.00011"
|
||||
compare(formValuesChanged.count, 5)
|
||||
formValuesChanged.wait()
|
||||
|
||||
// wait for fetchSuggestedRoutes function to be called
|
||||
wait(1000)
|
||||
fetchSuggestedRoutesCalled.wait()
|
||||
|
||||
// verify loading state was set and no errors currently
|
||||
verifyLoadingAndNoErrorsState(payPanel, receivePanel)
|
||||
|
@ -640,10 +655,10 @@ Item {
|
|||
|
||||
// edit some params to retry swap
|
||||
root.swapFormData.fromTokenAmount = "0.012"
|
||||
compare(formValuesChanged.count, 6)
|
||||
formValuesChanged.wait()
|
||||
|
||||
// wait for fetchSuggestedRoutes function to be called
|
||||
wait(1000)
|
||||
fetchSuggestedRoutesCalled.wait()
|
||||
|
||||
// verify loading state was set and no errors currently
|
||||
verifyLoadingAndNoErrorsState(payPanel, receivePanel)
|
||||
|
@ -1007,7 +1022,7 @@ Item {
|
|||
root.swapFormData.fromTokenAmount = valueToExchangeString
|
||||
root.swapFormData.toTokenKey = "STT"
|
||||
|
||||
compare(formValuesChanged.count, 3)
|
||||
formValuesChanged.wait()
|
||||
|
||||
// Launch popup
|
||||
launchAndVerfyModal()
|
||||
|
@ -1065,7 +1080,7 @@ Item {
|
|||
root.swapFormData.fromTokensKey = "ETH"
|
||||
root.swapFormData.toTokenKey = "STT"
|
||||
|
||||
compare(formValuesChanged.count, 3)
|
||||
formValuesChanged.wait()
|
||||
|
||||
const payPanel = findChild(controlUnderTest, "payPanel")
|
||||
verify(!!payPanel)
|
||||
|
@ -1095,7 +1110,7 @@ Item {
|
|||
maxTagButton.clicked()
|
||||
waitForItemPolished(payPanel)
|
||||
|
||||
tryCompare(formValuesChanged, "count", 3)
|
||||
formValuesChanged.wait()
|
||||
|
||||
verify(amountToSendInput.interactive)
|
||||
verify(amountToSendInput.input.input.edit.cursorVisible)
|
||||
|
@ -1287,5 +1302,158 @@ Item {
|
|||
closeAndVerfyModal()
|
||||
}
|
||||
}
|
||||
|
||||
function test_approval_flow_button_states() {
|
||||
root.swapAdaptor.reset()
|
||||
|
||||
// Launch popup
|
||||
launchAndVerfyModal()
|
||||
|
||||
const maxFeesValue = findChild(controlUnderTest, "maxFeesValue")
|
||||
verify(!!maxFeesValue)
|
||||
const signButton = findChild(controlUnderTest, "signButton")
|
||||
verify(!!signButton)
|
||||
const errorTag = findChild(controlUnderTest, "errorTag")
|
||||
verify(!!errorTag)
|
||||
const payPanel = findChild(controlUnderTest, "payPanel")
|
||||
verify(!!payPanel)
|
||||
const receivePanel = findChild(controlUnderTest, "receivePanel")
|
||||
verify(!!receivePanel)
|
||||
|
||||
// Check max fees values and sign button state when nothing is set
|
||||
compare(maxFeesValue.text, "--")
|
||||
verify(!signButton.enabled)
|
||||
verify(!errorTag.visible)
|
||||
|
||||
// set input values in the form correctly
|
||||
root.swapFormData.fromTokensKey = root.swapAdaptor.walletAssetsStore.walletTokensStore.plainTokensBySymbolModel.get(0).key
|
||||
formValuesChanged.wait()
|
||||
root.swapFormData.toTokenKey = root.swapAdaptor.walletAssetsStore.walletTokensStore.plainTokensBySymbolModel.get(1).key
|
||||
root.swapFormData.fromTokenAmount = "0.001"
|
||||
formValuesChanged.wait()
|
||||
root.swapFormData.selectedNetworkChainId = root.swapAdaptor.filteredFlatNetworksModel.get(0).chainId
|
||||
formValuesChanged.wait()
|
||||
root.swapFormData.selectedAccountAddress = "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240"
|
||||
formValuesChanged.wait()
|
||||
|
||||
// wait for fetchSuggestedRoutes function to be called
|
||||
fetchSuggestedRoutesCalled.wait()
|
||||
|
||||
// verify loading state was set and no errors currently
|
||||
verifyLoadingAndNoErrorsState(payPanel, receivePanel)
|
||||
|
||||
// emit event with route that needs no approval
|
||||
let txRoutes = root.dummySwapTransactionRoutes.txHasRoutesApprovalNeeded
|
||||
txRoutes.uuid = root.swapAdaptor.uuid
|
||||
root.swapStore.suggestedRoutesReady(txRoutes)
|
||||
|
||||
// calculation needed for total fees
|
||||
let gasTimeEstimate = txRoutes.gasTimeEstimate
|
||||
let totalTokenFeesInFiat = gasTimeEstimate.totalTokenFees * root.swapAdaptor.fromToken.marketDetails.currencyPrice.amount
|
||||
let totalFees = root.swapAdaptor.currencyStore.getFiatValue(gasTimeEstimate.totalFeesInEth, Constants.ethToken) + totalTokenFeesInFiat
|
||||
let bestPath = SQUtils.ModelUtils.get(txRoutes.suggestedRoutes, 0, "route")
|
||||
|
||||
// verify loading state removed and data is displayed as expected on the Modal
|
||||
verify(root.swapAdaptor.validSwapProposalReceived)
|
||||
verify(!root.swapAdaptor.swapProposalLoading)
|
||||
compare(root.swapAdaptor.swapOutputData.fromTokenAmount, "")
|
||||
compare(root.swapAdaptor.swapOutputData.toTokenAmount, SQUtils.AmountsArithmetic.div(
|
||||
SQUtils.AmountsArithmetic.fromString(txRoutes.amountToReceive),
|
||||
SQUtils.AmountsArithmetic.fromNumber(1, root.swapAdaptor.toToken.decimals)).toString())
|
||||
compare(root.swapAdaptor.swapOutputData.totalFees, totalFees)
|
||||
compare(root.swapAdaptor.swapOutputData.bestRoutes, txRoutes.suggestedRoutes)
|
||||
compare(root.swapAdaptor.swapOutputData.hasError, false)
|
||||
compare(root.swapAdaptor.swapOutputData.estimatedTime, bestPath.estimatedTime)
|
||||
compare(root.swapAdaptor.swapOutputData.txProviderName, bestPath.bridgeName)
|
||||
compare(root.swapAdaptor.swapOutputData.approvalNeeded, true)
|
||||
compare(root.swapAdaptor.swapOutputData.approvalGasFees, bestPath.approvalGasFees.toString())
|
||||
compare(root.swapAdaptor.swapOutputData.approvalAmountRequired, bestPath.approvalAmountRequired)
|
||||
compare(root.swapAdaptor.swapOutputData.approvalContractAddress, bestPath.approvalContractAddress)
|
||||
|
||||
verify(!errorTag.visible)
|
||||
verify(signButton.enabled)
|
||||
verify(!signButton.loading)
|
||||
compare(signButton.text, qsTr("Approve %1").arg(root.swapAdaptor.fromToken.symbol))
|
||||
// TODO: note that there is a loss of precision as the approvalGasFees is currently passes as float from the backend and not string.
|
||||
compare(maxFeesValue.text, root.swapAdaptor.currencyStore.formatCurrencyAmount(
|
||||
root.swapAdaptor.swapOutputData.approvalGasFees,
|
||||
root.swapAdaptor.currencyStore.currentCurrency))
|
||||
|
||||
// simulate user click on approve button and approval failed
|
||||
root.swapStore.transactionSent(root.swapFormData.selectedNetworkChainId, "0x877ffe47fc29340312611d4e833ab189fe4f4152b01cc9a05bb4125b81b2a89a", root.swapAdaptor.uuid, "")
|
||||
|
||||
verify(root.swapAdaptor.approvalPending)
|
||||
verify(!root.swapAdaptor.approvalSuccessful)
|
||||
verify(!errorTag.visible)
|
||||
verify(!signButton.enabled)
|
||||
verify(signButton.loading)
|
||||
compare(signButton.text, qsTr("Approving %1").arg(root.swapAdaptor.fromToken.symbol))
|
||||
// TODO: note that there is a loss of precision as the approvalGasFees is currently passes as float from the backend and not string.
|
||||
compare(maxFeesValue.text, root.swapAdaptor.currencyStore.formatCurrencyAmount(
|
||||
root.swapAdaptor.swapOutputData.approvalGasFees,
|
||||
root.swapAdaptor.currencyStore.currentCurrency))
|
||||
|
||||
// simulate approval tx was unsuccessful
|
||||
root.swapStore.transactionSendingComplete("0x877ffe47fc29340312611d4e833ab189fe4f4152b01cc9a05bb4125b81b2a89a", false)
|
||||
|
||||
verify(!root.swapAdaptor.approvalPending)
|
||||
verify(!root.swapAdaptor.approvalSuccessful)
|
||||
verify(!errorTag.visible)
|
||||
verify(signButton.enabled)
|
||||
verify(!signButton.loading)
|
||||
compare(signButton.text, qsTr("Approve %1").arg(root.swapAdaptor.fromToken.symbol))
|
||||
// TODO: note that there is a loss of precision as the approvalGasFees is currently passes as float from the backend and not string.
|
||||
compare(maxFeesValue.text, root.swapAdaptor.currencyStore.formatCurrencyAmount(
|
||||
root.swapAdaptor.swapOutputData.approvalGasFees,
|
||||
root.swapAdaptor.currencyStore.currentCurrency))
|
||||
|
||||
// simulate user click on approve button and successful approval tx made
|
||||
signButton.clicked()
|
||||
root.swapStore.transactionSent(root.swapFormData.selectedNetworkChainId, "0x877ffe47fc29340312611d4e833ab189fe4f4152b01cc9a05bb4125b81b2a89a", root.swapAdaptor.uuid, "")
|
||||
|
||||
verify(root.swapAdaptor.approvalPending)
|
||||
verify(!root.swapAdaptor.approvalSuccessful)
|
||||
verify(!errorTag.visible)
|
||||
verify(!signButton.enabled)
|
||||
verify(signButton.loading)
|
||||
compare(signButton.text, qsTr("Approving %1").arg(root.swapAdaptor.fromToken.symbol))
|
||||
// TODO: note that there is a loss of precision as the approvalGasFees is currently passes as float from the backend and not string.
|
||||
compare(maxFeesValue.text, root.swapAdaptor.currencyStore.formatCurrencyAmount(
|
||||
root.swapAdaptor.swapOutputData.approvalGasFees,
|
||||
root.swapAdaptor.currencyStore.currentCurrency))
|
||||
|
||||
// simulate approval tx was successful
|
||||
signButton.clicked()
|
||||
root.swapStore.transactionSendingComplete("0x877ffe47fc29340312611d4e833ab189fe4f4152b01cc9a05bb4125b81b2a89a", true)
|
||||
|
||||
// check if fetchSuggestedRoutes called
|
||||
fetchSuggestedRoutesCalled.wait()
|
||||
|
||||
// verify loading state was set and no errors currently
|
||||
verifyLoadingAndNoErrorsState(payPanel, receivePanel)
|
||||
|
||||
verify(!root.swapAdaptor.approvalPending)
|
||||
verify(!root.swapAdaptor.approvalSuccessful)
|
||||
verify(!errorTag.visible)
|
||||
verify(!signButton.enabled)
|
||||
verify(!signButton.loading)
|
||||
compare(signButton.text, qsTr("Swap"))
|
||||
compare(maxFeesValue.text, Constants.dummyText)
|
||||
|
||||
let txHasRouteNoApproval = root.dummySwapTransactionRoutes.txHasRouteNoApproval
|
||||
txHasRouteNoApproval.uuid = root.swapAdaptor.uuid
|
||||
root.swapStore.suggestedRoutesReady(root.dummySwapTransactionRoutes.txHasRouteNoApproval)
|
||||
|
||||
verify(!root.swapAdaptor.approvalPending)
|
||||
verify(!root.swapAdaptor.approvalSuccessful)
|
||||
verify(!errorTag.visible)
|
||||
verify(signButton.enabled)
|
||||
verify(!signButton.loading)
|
||||
compare(signButton.text, qsTr("Swap"))
|
||||
compare(maxFeesValue.text, root.swapAdaptor.currencyStore.formatCurrencyAmount(
|
||||
root.swapAdaptor.swapOutputData.totalFees,
|
||||
root.swapAdaptor.currencyStore.currentCurrency))
|
||||
closeAndVerfyModal()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -128,7 +128,7 @@ QtObject {
|
|||
isFirstSimpleTx:true,
|
||||
isFirstBridgeTx:true,
|
||||
approvalRequired:true,
|
||||
approvalGasFees:0.0,
|
||||
approvalGasFees:0.100000000000000007,
|
||||
approvalAmountRequired:"0",
|
||||
approvalContractAddress:"0x216b4b4ba9f3e719726886d34a177484278bfcae"
|
||||
}
|
||||
|
|
|
@ -75,7 +75,8 @@ ListModel {
|
|||
{ chainId: 1, address: "0x6b175474e89094c44da98b954eedeac495271d0f"},
|
||||
{ chainId: 10, address: "0xda10009cbd5d07dd0cecc66161fc93d7c9000da1"},
|
||||
{ chainId: 42161, address: "0xda10009cbd5d07dd0cecc66161fc93d7c9000da1"},
|
||||
{ chainId: 5, address: "0xf2edf1c091f683e3fb452497d9a98a49cba84666"}
|
||||
{ chainId: 5, address: "0xf2edf1c091f683e3fb452497d9a98a49cba84666"},
|
||||
{ chainId: 11155111, address: "0xda10009cbd5d07dd0cecc66161fc93d7c9000da1"},
|
||||
],
|
||||
decimals: 18,
|
||||
type: 1,
|
||||
|
|
|
@ -232,29 +232,6 @@ QtObject {
|
|||
root.suggestedRoutesCalled = true
|
||||
}
|
||||
|
||||
enum EstimatedTime {
|
||||
Unknown = 0,
|
||||
LessThanOneMin,
|
||||
LessThanThreeMins,
|
||||
LessThanFiveMins,
|
||||
MoreThanFiveMins
|
||||
}
|
||||
|
||||
function getLabelForEstimatedTxTime(estimatedFlag) {
|
||||
switch(estimatedFlag) {
|
||||
case TransactionStore.EstimatedTime.Unknown:
|
||||
return qsTr("~ Unknown")
|
||||
case TransactionStore.EstimatedTime.LessThanOneMin :
|
||||
return qsTr("< 1 minute")
|
||||
case TransactionStore.EstimatedTime.LessThanThreeMins :
|
||||
return qsTr("< 3 minutes")
|
||||
case TransactionStore.EstimatedTime.LessThanFiveMins:
|
||||
return qsTr("< 5 minutes")
|
||||
default:
|
||||
return qsTr("> 5 minutes")
|
||||
}
|
||||
}
|
||||
|
||||
function resetStoredProperties() {
|
||||
root.amountToSend = ""
|
||||
root.sendType = Constants.SendType.Transfer
|
||||
|
|
|
@ -40,7 +40,7 @@ Item {
|
|||
target: walletSection
|
||||
|
||||
function onFilterChanged(address) {
|
||||
RootStore.selectedAddress = address == "" ? "" : address
|
||||
RootStore.selectedAddress = address === "" ? "" : address
|
||||
}
|
||||
|
||||
function onDisplayKeypairImportPopup() {
|
||||
|
@ -214,7 +214,9 @@ Item {
|
|||
hasFloatingButtons: true
|
||||
})
|
||||
onLaunchSwapModal: {
|
||||
d.swapFormData.selectedAccountAddress = RootStore.selectedAddress
|
||||
d.swapFormData.selectedAccountAddress = !!RootStore.selectedAddress ?
|
||||
RootStore.selectedAddress :
|
||||
StatusQUtils.ModelUtils.get(RootStore.nonWatchAccounts,0, "address")
|
||||
d.swapFormData.selectedNetworkChainId = StatusQUtils.ModelUtils.getByKey(RootStore.filteredFlatModel, "layer", 1, "chainId")
|
||||
d.swapFormData.fromTokensKey = tokensKey
|
||||
d.swapFormData.defaultToTokenKey = RootStore.areTestNetworksEnabled ? Constants.swap.testStatusTokenKey : Constants.swap.mainnetStatusTokenKey
|
||||
|
@ -333,7 +335,9 @@ Item {
|
|||
}
|
||||
onLaunchSwapModal: {
|
||||
d.swapFormData.fromTokensKey = ""
|
||||
d.swapFormData.selectedAccountAddress = RootStore.selectedAddress
|
||||
d.swapFormData.selectedAccountAddress = !!RootStore.selectedAddress ?
|
||||
RootStore.selectedAddress :
|
||||
StatusQUtils.ModelUtils.get(RootStore.nonWatchAccounts,0, "address")
|
||||
d.swapFormData.selectedNetworkChainId = StatusQUtils.ModelUtils.getByKey(RootStore.filteredFlatModel, "layer", 1, "chainId")
|
||||
if(!!walletStore.currentViewedHoldingTokensKey && walletStore.currentViewedHoldingType === Constants.TokenType.ERC20) {
|
||||
d.swapFormData.fromTokensKey = walletStore.currentViewedHoldingTokensKey
|
||||
|
|
|
@ -73,4 +73,19 @@ QtObject {
|
|||
|
||||
return value - Math.max(0.0001, Math.min(0.01, value * 0.1))
|
||||
}
|
||||
|
||||
function getLabelForEstimatedTxTime(estimatedFlag) {
|
||||
switch(estimatedFlag) {
|
||||
case Constants.TransactionEstimatedTime.Unknown:
|
||||
return qsTr("~ Unknown")
|
||||
case Constants.TransactionEstimatedTime.LessThanOneMin :
|
||||
return qsTr("< 1 minute")
|
||||
case Constants.TransactionEstimatedTime.LessThanThreeMins :
|
||||
return qsTr("< 3 minutes")
|
||||
case Constants.TransactionEstimatedTime.LessThanFiveMins:
|
||||
return qsTr("< 5 minutes")
|
||||
default:
|
||||
return qsTr("> 5 minutes")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
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
|
||||
}
|
||||
}
|
|
@ -19,3 +19,4 @@ CollectibleLinksTags 1.0 CollectibleLinksTags.qml
|
|||
SwapExchangeButton 1.0 SwapExchangeButton.qml
|
||||
EditSlippagePanel 1.0 EditSlippagePanel.qml
|
||||
TokenSelector 1.0 TokenSelector.qml
|
||||
SwapModalFooterInfoComponent 1.0 SwapModalFooterInfoComponent.qml
|
||||
|
|
|
@ -42,6 +42,8 @@ StatusDialog {
|
|||
if (payPanel.valueValid && root.swapInputParamsForm.isFormFilledCorrectly()) {
|
||||
root.swapAdaptor.validSwapProposalReceived = false
|
||||
root.swapAdaptor.swapProposalLoading = true
|
||||
root.swapAdaptor.approvalPending = false
|
||||
root.swapAdaptor.approvalSuccessful = false
|
||||
root.swapAdaptor.swapOutputData.resetAllButReceivedTokenValuesForSwap()
|
||||
debounceFetchSuggestedRoutes()
|
||||
}
|
||||
|
@ -63,6 +65,16 @@ StatusDialog {
|
|||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.swapAdaptor
|
||||
function onApprovalSuccessfulChanged() {
|
||||
// perform a recalculation to make sure expected outcome shown is accurate
|
||||
if(root.swapAdaptor.approvalSuccessful) {
|
||||
d.fetchSuggestedRoutes()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on implicitHeight {
|
||||
NumberAnimation { duration: 1000; easing.type: Easing.OutExpo; alwaysRunToEnd: true}
|
||||
}
|
||||
|
@ -233,17 +245,6 @@ StatusDialog {
|
|||
}
|
||||
}
|
||||
|
||||
/* 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()
|
||||
}
|
||||
}
|
||||
|
||||
EditSlippagePanel {
|
||||
id: editSlippagePanel
|
||||
objectName: "editSlippagePanel"
|
||||
|
@ -264,7 +265,7 @@ StatusDialog {
|
|||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.topMargin: Style.current.smallPadding
|
||||
text: {
|
||||
if (payPanel.amountEnteredGreaterThanBalance) {
|
||||
if (payPanel.amountEnteredGreaterThanBalance) {
|
||||
return qsTr("Insufficient funds for swap")
|
||||
}
|
||||
return qsTr("An error has occured, please try again")
|
||||
|
@ -323,34 +324,60 @@ StatusDialog {
|
|||
}
|
||||
StatusTextWithLoadingState {
|
||||
objectName: "maxFeesValue"
|
||||
text: loading ? Constants.dummyText :
|
||||
root.swapAdaptor.validSwapProposalReceived ?
|
||||
root.swapAdaptor.currencyStore.formatCurrencyAmount(
|
||||
text: {
|
||||
if(root.swapAdaptor.swapProposalLoading) {
|
||||
return Constants.dummyText
|
||||
}
|
||||
|
||||
if(root.swapAdaptor.validSwapProposalReceived) {
|
||||
if(root.swapAdaptor.swapOutputData.approvalNeeded) {
|
||||
return root.swapAdaptor.currencyStore.formatCurrencyAmount(
|
||||
root.swapAdaptor.swapOutputData.approvalGasFees,
|
||||
root.swapAdaptor.currencyStore.currentCurrency)
|
||||
|
||||
} else {
|
||||
return root.swapAdaptor.currencyStore.formatCurrencyAmount(
|
||||
root.swapAdaptor.swapOutputData.totalFees,
|
||||
root.swapAdaptor.currencyStore.currentCurrency) :
|
||||
"--"
|
||||
root.swapAdaptor.currencyStore.currentCurrency)
|
||||
}
|
||||
}
|
||||
|
||||
return "--"
|
||||
}
|
||||
customColor: Theme.palette.directColor4
|
||||
font.weight: Font.Medium
|
||||
loading: root.swapAdaptor.swapProposalLoading
|
||||
}
|
||||
}
|
||||
/* TODO: https://github.com/status-im/status-desktop/issues/15313
|
||||
will introduce having loading button and showing text on the side*/
|
||||
StatusButton {
|
||||
objectName: "signButton"
|
||||
readonly property string fromTokenSymbol: !!root.swapAdaptor.fromToken ? root.swapAdaptor.fromToken.symbol ?? "" : ""
|
||||
loading: root.swapAdaptor.approvalPending
|
||||
icon.name: "password"
|
||||
/* 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")
|
||||
text: {
|
||||
if(root.swapAdaptor.validSwapProposalReceived) {
|
||||
if (root.swapAdaptor.approvalPending) {
|
||||
return qsTr("Approving %1").arg(fromTokenSymbol)
|
||||
} else if(root.swapAdaptor.swapOutputData.approvalNeeded) {
|
||||
return qsTr("Approve %1").arg(fromTokenSymbol)
|
||||
}
|
||||
}
|
||||
return qsTr("Swap")
|
||||
}
|
||||
tooltip.text: root.swapAdaptor.validSwapProposalReceived &&
|
||||
root.swapAdaptor.swapOutputData.approvalNeeded ?
|
||||
qsTr("Approve %1 spending cap to Swap").arg(fromTokenSymbol) : ""
|
||||
disabledColor: Theme.palette.directColor8
|
||||
enabled: root.swapAdaptor.validSwapProposalReceived &&
|
||||
editSlippagePanel.valid &&
|
||||
!payPanel.amountEnteredGreaterThanBalance
|
||||
!payPanel.amountEnteredGreaterThanBalance &&
|
||||
!root.swapAdaptor.approvalPending
|
||||
onClicked: {
|
||||
if (root.swapAdaptor.validSwapProposalReceived ){
|
||||
if(root.swapAdaptor.swapOutputData.approvalNeeded) {
|
||||
swapAdaptor.sendApproveTx()
|
||||
Global.openPopup(swapSignApprovePopup)
|
||||
}
|
||||
else {
|
||||
swapAdaptor.sendSwapTx()
|
||||
|
@ -362,4 +389,36 @@ 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
|
||||
destroyOnClose: true
|
||||
loading: root.swapAdaptor.swapProposalLoading
|
||||
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
|
||||
}
|
||||
adaptor: SwapSignApproveAdaptor {
|
||||
swapStore: root.swapAdaptor.swapStore
|
||||
walletAssetsStore: root.swapAdaptor.walletAssetsStore
|
||||
currencyStore: root.swapAdaptor.currencyStore
|
||||
inputFormData: approvePopup.swapSignApproveInputForm
|
||||
}
|
||||
onSign: {
|
||||
root.swapAdaptor.sendApproveTx()
|
||||
close()
|
||||
}
|
||||
onReject: close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,10 +23,16 @@ QObject {
|
|||
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 {
|
||||
|
@ -70,31 +76,12 @@ QObject {
|
|||
filters: ValueFilter { roleName: "isTest"; value: root.swapStore.areTestNetworksEnabled }
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -141,6 +128,27 @@ QObject {
|
|||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -163,12 +171,38 @@ QObject {
|
|||
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
|
||||
root.swapOutputData.approvalNeeded = ModelUtils.get(root.swapOutputData.bestRoutes, 0, "route").approvalRequired
|
||||
let bestPath = ModelUtils.get(root.swapOutputData.bestRoutes, 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
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
|
@ -176,6 +210,9 @@ QObject {
|
|||
root.swapOutputData.reset()
|
||||
root.validSwapProposalReceived = false
|
||||
root.swapProposalLoading = false
|
||||
root.approvalPending = false
|
||||
root.approvalSuccessful = false
|
||||
d.txHash = ""
|
||||
}
|
||||
|
||||
function getNetworkShortNames(chainIds) {
|
||||
|
@ -230,6 +267,8 @@ QObject {
|
|||
}
|
||||
|
||||
function sendApproveTx() {
|
||||
root.approvalPending = true
|
||||
|
||||
let account = selectedAccountEntry.item
|
||||
let accountAddress = account.address
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import QtQuick 2.15
|
||||
|
||||
import utils 1.0
|
||||
|
||||
/* This is so that all the data from the response
|
||||
to the swap request can be placed here at one place. */
|
||||
QtObject {
|
||||
|
@ -9,9 +11,15 @@ QtObject {
|
|||
property string toTokenAmount: ""
|
||||
property real totalFees: 0
|
||||
property var bestRoutes: []
|
||||
property bool approvalNeeded
|
||||
property bool hasError
|
||||
property var rawPaths: []
|
||||
// need to check how this is done in new router v2, right now it is Enum type
|
||||
property int estimatedTime
|
||||
property string txProviderName
|
||||
property bool approvalNeeded
|
||||
property string approvalGasFees
|
||||
property string approvalAmountRequired
|
||||
property string approvalContractAddress
|
||||
|
||||
function reset() {
|
||||
root.fromTokenAmount = ""
|
||||
|
@ -25,6 +33,12 @@ QtObject {
|
|||
root.approvalNeeded = false
|
||||
root.hasError = false
|
||||
root.rawPaths = []
|
||||
root.estimatedTime = Constants.TransactionEstimatedTime.Unknown
|
||||
txProviderName = ""
|
||||
approvalNeeded = false
|
||||
approvalGasFees = ""
|
||||
approvalAmountRequired = ""
|
||||
approvalContractAddress = ""
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
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 selectedAccount: selectedAccountEntry.item
|
||||
readonly property var selectedNetwork: selectedNetworkEntry.item
|
||||
|
||||
ModelEntry {
|
||||
id: fromTokenEntry
|
||||
sourceModel: root.walletAssetsStore.walletTokensStore.plainTokensBySymbolModel
|
||||
key: "key"
|
||||
value: root.inputFormData.tokensKey
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
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 tokensKey
|
||||
// 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
|
||||
}
|
|
@ -0,0 +1,268 @@
|
|||
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
|
||||
|
||||
signal sign()
|
||||
signal reject()
|
||||
|
||||
objectName: "swapSignApproveModal"
|
||||
implicitWidth: 480
|
||||
padding: 20
|
||||
title: qsTr("Approve spending cap")
|
||||
/* 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
|
||||
|
||||
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: SQUtils.AmountsArithmetic.div(
|
||||
SQUtils.AmountsArithmetic.fromString(swapSignApproveInputForm.approvalAmountRequired),
|
||||
SQUtils.AmountsArithmetic.fromNumber(1, !!root.adaptor.fromToken ? root.adaptor.fromToken.decimals: 18)).toString()
|
||||
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 ?? "" : ""
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
width: scrollView.availableWidth
|
||||
spacing: Style.current.padding
|
||||
StatusBaseText {
|
||||
width: parent.width
|
||||
text: qsTr("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(contractAddressOnSelectedNetwork.item.address, 6, 4)
|
||||
ModelEntry {
|
||||
id: contractAddressOnSelectedNetwork
|
||||
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: {}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
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: {}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
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: {
|
||||
let feesInFoat = root.adaptor.currencyStore.getFiatValue(root.swapSignApproveInputForm.approvalGasFees, Constants.ethToken)
|
||||
return root.adaptor.currencyStore.formatCurrencyAmount(feesInFoat, root.adaptor.currencyStore.currentCurrency)
|
||||
}
|
||||
}
|
||||
StatusTextWithLoadingState {
|
||||
anchors.right: parent.right
|
||||
loading: root.loading
|
||||
text: root.adaptor.currencyStore.formatCurrencyAmount(root.swapSignApproveInputForm.approvalGasFees, Constants.ethToken)
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
footer: StatusDialogFooter {
|
||||
spacing: Style.current.xlPadding
|
||||
leftButtons: ObjectModel {
|
||||
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)
|
||||
}
|
||||
loading: root.loading
|
||||
}
|
||||
SwapModalFooterInfoComponent {
|
||||
Layout.maximumWidth: 60
|
||||
titleText: qsTr("Est. time:")
|
||||
infoText: WalletUtils.getLabelForEstimatedTxTime(root.swapSignApproveInputForm.estimatedTime)
|
||||
loading: root.loading
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,3 +2,6 @@ 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
|
||||
|
|
|
@ -17,12 +17,20 @@ QtObject {
|
|||
readonly property var walletSectionSendInst: walletSectionSend
|
||||
|
||||
signal suggestedRoutesReady(var txRoutes)
|
||||
signal transactionSent(var chainId, var txHash, var uuid, var error)
|
||||
signal transactionSendingComplete(var txHash, var success)
|
||||
|
||||
readonly property Connections walletSectionSendConnections: Connections {
|
||||
target: root.walletSectionSendInst
|
||||
function onSuggestedRoutesReady(txRoutes) {
|
||||
root.suggestedRoutesReady(txRoutes)
|
||||
}
|
||||
function onTransactionSent(chainId, txHash, uuid, error) {
|
||||
root.transactionSent(chainId, txHash, uuid, error)
|
||||
}
|
||||
function onTransactionSendingComplete(txHash, success) {
|
||||
root.transactionSendingComplete(txHash, success)
|
||||
}
|
||||
}
|
||||
|
||||
function fetchSuggestedRoutes(uuid, accountFrom, accountTo, amountIn, amountOut, tokenFrom, tokenTo,
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
|
@ -490,7 +490,7 @@ StatusDialog {
|
|||
function onSuggestedRoutesReady(txRoutes) {
|
||||
popup.bestRoutes = txRoutes.suggestedRoutes
|
||||
let gasTimeEstimate = txRoutes.gasTimeEstimate
|
||||
d.totalTimeEstimate = popup.store.getLabelForEstimatedTxTime(gasTimeEstimate.totalTime)
|
||||
d.totalTimeEstimate = WalletUtils.getLabelForEstimatedTxTime(gasTimeEstimate.totalTime)
|
||||
let totalTokenFeesInFiat = 0
|
||||
if (!!d.selectedHolding && !!d.selectedHolding.marketDetails && !!d.selectedHolding.marketDetails.currencyPrice)
|
||||
totalTokenFeesInFiat = gasTimeEstimate.totalTokenFees * d.selectedHolding.marketDetails.currencyPrice.amount
|
||||
|
|
|
@ -84,29 +84,6 @@ QtObject {
|
|||
return globalUtils.plainText(text)
|
||||
}
|
||||
|
||||
enum EstimatedTime {
|
||||
Unknown = 0,
|
||||
LessThanOneMin,
|
||||
LessThanThreeMins,
|
||||
LessThanFiveMins,
|
||||
MoreThanFiveMins
|
||||
}
|
||||
|
||||
function getLabelForEstimatedTxTime(estimatedFlag) {
|
||||
switch(estimatedFlag) {
|
||||
case TransactionStore.EstimatedTime.Unknown:
|
||||
return qsTr("~ Unknown")
|
||||
case TransactionStore.EstimatedTime.LessThanOneMin :
|
||||
return qsTr("< 1 minute")
|
||||
case TransactionStore.EstimatedTime.LessThanThreeMins :
|
||||
return qsTr("< 3 minutes")
|
||||
case TransactionStore.EstimatedTime.LessThanFiveMins:
|
||||
return qsTr("< 5 minutes")
|
||||
default:
|
||||
return qsTr("> 5 minutes")
|
||||
}
|
||||
}
|
||||
|
||||
function getAsset(assetsList, symbol) {
|
||||
for(var i=0; i< assetsList.rowCount();i++) {
|
||||
let asset = assetsList.get(i)
|
||||
|
|
|
@ -1323,5 +1323,18 @@ QtObject {
|
|||
but in case the logic for keys changes in the backend, it should be updated here as well */
|
||||
readonly property string testStatusTokenKey: "STT"
|
||||
readonly property string mainnetStatusTokenKey: "SNT"
|
||||
/* 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 */
|
||||
readonly property string paraswapIcon: "paraswap"
|
||||
readonly property string paraswapUrl: "app.paraswap.io"
|
||||
}
|
||||
|
||||
enum TransactionEstimatedTime {
|
||||
Unknown = 0,
|
||||
LessThanOneMin,
|
||||
LessThanThreeMins,
|
||||
LessThanFiveMins,
|
||||
MoreThanFiveMins
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue