From 6eeb917cd7e188f904198792881b676aed776c13 Mon Sep 17 00:00:00 2001 From: Godfrain Jacques Date: Fri, 19 Jul 2024 12:21:36 -0700 Subject: [PATCH] feat(connector)_: create connection between connector service and status-desktop (#15565) fix #15179 that triggers connection request from client - Setup the initial architecture for connecting Status-go and UI screens - Implement the request Connection popup for Accept or Rejecting connection --- .../core/signals/remote_signals/connector.nim | 17 +++ .../signals/remote_signals/signal_type.nim | 1 + src/app/core/signals/signals_manager.nim | 1 + src/app/core/signals/types.nim | 4 +- .../modules/main/wallet_section/module.nim | 9 +- src/app/modules/main/wallet_section/view.nim | 15 +- .../shared_modules/connector/controller.nim | 65 +++++++++ src/app_service/service/connector/service.nim | 82 +++++++++++ src/backend/connector.nim | 31 ++++ .../services/dapps/DappsConnectorSDK.qml | 133 ++++++++++++++++++ .../AppLayouts/Wallet/services/dapps/qmldir | 1 + ui/app/AppLayouts/Wallet/stores/RootStore.qml | 1 + .../Wallet/views/NetworkSelectorView.qml | 4 +- ui/app/mainui/AppMain.qml | 49 ++++++- .../popups/walletconnect/ConnectDAppModal.qml | 3 + .../walletconnect/private/ContextCard.qml | 3 +- 16 files changed, 406 insertions(+), 13 deletions(-) create mode 100644 src/app/core/signals/remote_signals/connector.nim create mode 100644 src/app/modules/shared_modules/connector/controller.nim create mode 100644 src/app_service/service/connector/service.nim create mode 100644 src/backend/connector.nim create mode 100644 ui/app/AppLayouts/Wallet/services/dapps/DappsConnectorSDK.qml diff --git a/src/app/core/signals/remote_signals/connector.nim b/src/app/core/signals/remote_signals/connector.nim new file mode 100644 index 0000000000..58c14dde6d --- /dev/null +++ b/src/app/core/signals/remote_signals/connector.nim @@ -0,0 +1,17 @@ +import json, tables, chronicles +import base + +include app_service/common/json_utils + +type ConnectorSendRequestAccountsSignal* = ref object of Signal + url*: string + name*: string + iconUrl*: string + requestId*: string + +proc fromEvent*(T: type ConnectorSendRequestAccountsSignal, event: JsonNode): ConnectorSendRequestAccountsSignal = + result = ConnectorSendRequestAccountsSignal() + result.url = event["event"]{"url"}.getStr() + result.name = event["event"]{"name"}.getStr() + result.iconUrl = event["event"]{"iconUrl"}.getStr() + result.requestId = event["event"]{"requestId"}.getStr() \ No newline at end of file diff --git a/src/app/core/signals/remote_signals/signal_type.nim b/src/app/core/signals/remote_signals/signal_type.nim index da35daad41..54efc22c31 100644 --- a/src/app/core/signals/remote_signals/signal_type.nim +++ b/src/app/core/signals/remote_signals/signal_type.nim @@ -66,6 +66,7 @@ type SignalType* {.pure.} = enum DBReEncryptionFinished = "db.reEncryption.finished" CommunityTokenTransactionStatusChanged = "communityToken.communityTokenTransactionStatusChanged" CommunityTokenAction = "communityToken.communityTokenAction" + ConnectorSendRequestAccounts = "connector.sendRequestAccounts" Unknown proc event*(self:SignalType):string = diff --git a/src/app/core/signals/signals_manager.nim b/src/app/core/signals/signals_manager.nim index ee0577b48c..1eb1095571 100644 --- a/src/app/core/signals/signals_manager.nim +++ b/src/app/core/signals/signals_manager.nim @@ -135,6 +135,7 @@ QtObject: of SignalType.LocalPairing: LocalPairingSignal.fromEvent(jsonSignal) of SignalType.CommunityTokenTransactionStatusChanged: CommunityTokenTransactionStatusChangedSignal.fromEvent(jsonSignal) of SignalType.CommunityTokenAction: CommunityTokenActionSignal.fromEvent(jsonSignal) + of SignalType.ConnectorSendRequestAccounts: ConnectorSendRequestAccountsSignal.fromEvent(jsonSignal) else: Signal() result.signalType = signalType diff --git a/src/app/core/signals/types.nim b/src/app/core/signals/types.nim index d481aa7e8a..6c3c6fed2c 100644 --- a/src/app/core/signals/types.nim +++ b/src/app/core/signals/types.nim @@ -1,11 +1,11 @@ {.used.} -import ./remote_signals/[base, chronicles_logs, community, discovery_summary, envelope, expired, mailserver, messages, +import ./remote_signals/[base, chronicles_logs, community, connector, discovery_summary, envelope, expired, mailserver, messages, peerstats, signal_type, stats, wallet, whisper_filter, update_available, status_updates, waku_backed_up_profile, waku_backed_up_settings, waku_backed_up_keypair, waku_backed_up_watch_only_account, waku_fetching_backup_progress, pairing, node] -export base, chronicles_logs, community, discovery_summary, envelope, expired, mailserver, messages, peerstats, +export base, chronicles_logs, community, connector, discovery_summary, envelope, expired, mailserver, messages, peerstats, signal_type, stats, wallet, whisper_filter, update_available, status_updates, waku_backed_up_profile, waku_backed_up_settings, waku_backed_up_keypair, waku_backed_up_watch_only_account, waku_fetching_backup_progress, pairing, node \ No newline at end of file diff --git a/src/app/modules/main/wallet_section/module.nim b/src/app/modules/main/wallet_section/module.nim index 9e9b959514..1040b12357 100644 --- a/src/app/modules/main/wallet_section/module.nim +++ b/src/app/modules/main/wallet_section/module.nim @@ -19,6 +19,7 @@ import ./activity/details_controller as activity_detailsc import app/modules/shared_modules/collectible_details/controller as collectible_detailsc import app/modules/shared_modules/wallet_connect/controller as wc_controller +import app/modules/shared_modules/connector/controller as connector_controller import app/global/global_singleton import app/core/eventemitter @@ -39,6 +40,7 @@ import app_service/service/network_connection/service as network_connection_serv import app_service/service/devices/service as devices_service import app_service/service/community_tokens/service as community_tokens_service import app_service/service/wallet_connect/service as wc_service +import app_service/service/connector/service as connector_service import backend/collectibles as backend_collectibles @@ -81,6 +83,8 @@ type devicesService: devices_service.Service walletConnectService: wc_service.Service walletConnectController: wc_controller.Controller + dappsConnectorService: connector_service.Service + dappsConnectorController: connector_controller.Controller activityController: activityc.Controller collectibleDetailsController: collectible_detailsc.Controller @@ -171,7 +175,9 @@ proc newModule*( result.walletConnectService = wc_service.newService(result.events, result.threadpool, settingsService, transactionService) result.walletConnectController = wc_controller.newController(result.walletConnectService, walletAccountService) - result.view = newView(result, result.activityController, result.tmpActivityControllers, result.activityDetailsController, result.collectibleDetailsController, result.walletConnectController) + result.dappsConnectorService = connector_service.newService(result.events) + result.dappsConnectorController = connector_controller.newController(result.dappsConnectorService, result.events) + result.view = newView(result, result.activityController, result.tmpActivityControllers, result.activityDetailsController, result.collectibleDetailsController, result.walletConnectController, result.dappsConnectorController) result.viewVariant = newQVariant(result.view) method delete*(self: Module) = @@ -348,6 +354,7 @@ method load*(self: Module) = self.sendModule.load() self.networksModule.load() self.walletConnectService.init() + self.dappsConnectorService.init() method isLoaded*(self: Module): bool = return self.moduleLoaded diff --git a/src/app/modules/main/wallet_section/view.nim b/src/app/modules/main/wallet_section/view.nim index 19a2fcfb13..637eda9c94 100644 --- a/src/app/modules/main/wallet_section/view.nim +++ b/src/app/modules/main/wallet_section/view.nim @@ -6,6 +6,7 @@ import app/modules/shared_modules/collectible_details/controller as collectible_ import ./io_interface import app/modules/shared_models/currency_amount import app/modules/shared_modules/wallet_connect/controller as wc_controller +import app/modules/shared_modules/connector/controller as connector_controller type ActivityControllerArray* = array[2, activityc.Controller] @@ -26,6 +27,7 @@ QtObject: isNonArchivalNode: bool keypairOperabilityForObservedAccount: string wcController: QVariant + dappsConnectorController: QVariant walletReady: bool addressFilters: string currentCurrency: string @@ -37,6 +39,7 @@ QtObject: proc delete*(self: View) = self.wcController.delete + self.dappsConnectorController.delete self.QObject.delete @@ -45,7 +48,8 @@ QtObject: tmpActivityControllers: ActivityControllerArray, activityDetailsController: activity_detailsc.Controller, collectibleDetailsController: collectible_detailsc.Controller, - wcController: wc_controller.Controller): View = + wcController: wc_controller.Controller, + dappsConnectorController: connector_controller.Controller): View = new(result, delete) result.delegate = delegate result.activityController = activityController @@ -53,6 +57,7 @@ QtObject: result.activityDetailsController = activityDetailsController result.collectibleDetailsController = collectibleDetailsController result.wcController = newQVariant(wcController) + result.dappsConnectorController = newQVariant(dappsConnectorController) result.setup() @@ -248,6 +253,14 @@ QtObject: QtProperty[QVariant] walletConnectController: read = getWalletConnectController + proc getDappsConnectorController(self: View): QVariant {.slot.} = + if self.dappsConnectorController == nil: + return newQVariant() + return self.dappsConnectorController + + QtProperty[QVariant] dappsConnectorController: + read = getDappsConnectorController + proc walletReadyChanged*(self: View) {.signal.} proc getWalletReady*(self: View): bool {.slot.} = diff --git a/src/app/modules/shared_modules/connector/controller.nim b/src/app/modules/shared_modules/connector/controller.nim new file mode 100644 index 0000000000..2f66b20b9a --- /dev/null +++ b/src/app/modules/shared_modules/connector/controller.nim @@ -0,0 +1,65 @@ +import NimQml +import json, strutils +import chronicles +import app/core/eventemitter + +import app/core/signals/types + +import app_service/service/connector/service as connector_service + +const SIGNAL_CONNECTOR_SEND_REQUEST_ACCOUNTS* = "ConnectorSendRequestAccounts" + +logScope: + topics = "connector-controller" + +QtObject: + type + Controller* = ref object of QObject + service: connector_service.Service + events: EventEmitter + + proc delete*(self: Controller) = + self.QObject.delete + + proc dappRequestsToConnect*(self: Controller, requestId: string, payload: string) {.signal.} + + proc newController*(service: connector_service.Service, events: EventEmitter): Controller = + new(result, delete) + + result.events = events + result.service = service + + let controller = result # Capture result in a local variable + + service.registerEventsHandler(proc (event: connector_service.Event, payload: string) = + discard + ) + + result.events.on(SIGNAL_CONNECTOR_SEND_REQUEST_ACCOUNTS) do(e: Args): + let params = ConnectorSendRequestAccountsSignal(e) + let dappInfo = %*{ + "icon": params.iconUrl, + "name": params.name, + "url": params.url, + } + + controller.dappRequestsToConnect(params.requestId, dappInfo.toJson()) + + result.QObject.setup + + proc parseSingleUInt(chainIDsString: string): uint = + try: + let chainIds = parseJson(chainIDsString) + if chainIds.kind == JArray and chainIds.len == 1 and chainIds[0].kind == JInt: + return uint(chainIds[0].getInt()) + else: + raise newException(ValueError, "Invalid JSON array format") + except JsonParsingError: + raise newException(ValueError, "Failed to parse JSON") + + proc approveDappConnectRequest*(self: Controller, requestId: string, account: string, chainIDString: string): bool {.slot.} = + let chainId = parseSingleUInt(chainIDString) + return self.service.approveDappConnect(requestId, account, chainId) + + proc rejectDappConnectRequest*(self: Controller, requestId: string): bool {.slot.} = + return self.service.rejectDappConnect(requestId) \ No newline at end of file diff --git a/src/app_service/service/connector/service.nim b/src/app_service/service/connector/service.nim new file mode 100644 index 0000000000..882ba03db3 --- /dev/null +++ b/src/app_service/service/connector/service.nim @@ -0,0 +1,82 @@ +import NimQml, chronicles, json + +import backend/connector as status_go + +import app/global/global_singleton + +import app/core/eventemitter +import app/core/signals/types + +import strutils + +logScope: + topics = "connector-service" + +const SIGNAL_CONNECTOR_SEND_REQUEST_ACCOUNTS* = "ConnectorSendRequestAccounts" + +# Enum with events +type Event* = enum + DappConnect + +# Event handler function +type EventHandlerFn* = proc(event: Event, payload: string) + +# This can be ditched for now and process everything in the controller; +# However, it would be good to have the DB based calls async and this might be needed +QtObject: + type Service* = ref object of QObject + events: EventEmitter + eventHandler: EventHandlerFn + + proc delete*(self: Service) = + self.QObject.delete + + proc newService*( + events: EventEmitter + ): Service = + new(result, delete) + result.QObject.setup + + result.events = events + + proc init*(self: Service) = + self.events.on(SignalType.ConnectorSendRequestAccounts.event, proc(e: Args) = + if self.eventHandler == nil: + return + + var data = ConnectorSendRequestAccountsSignal(e) + + if not data.requestId.len() == 0: + error "ConnectorSendRequestAccountsSignal failed, requestId is empty" + return + + self.events.emit(SIGNAL_CONNECTOR_SEND_REQUEST_ACCOUNTS, data) + ) + + proc registerEventsHandler*(self: Service, handler: EventHandlerFn) = + self.eventHandler = handler + + proc approveDappConnect*(self: Service, requestId: string, account: string, chainID: uint): bool = + try: + var args = RequestAccountsAcceptedArgs() + + args.requestId = requestId + args.account = account + args.chainId = chainId + + return status_go.requestAccountsAcceptedFinishedRpc(args) + + except Exception as e: + error "requestAccountsAcceptedFinishedRpc failed: ", err=e.msg + return false + + proc rejectDappConnect*(self: Service, requestId: string): bool = + try: + var args = RejectedArgs() + args.requestId = requestId + + return status_go.requestAccountsRejectedFinishedRpc(args) + + except Exception as e: + error "requestAccountsRejectedFinishedRpc failed: ", err=e.msg + return false \ No newline at end of file diff --git a/src/backend/connector.nim b/src/backend/connector.nim new file mode 100644 index 0000000000..374ec58f6b --- /dev/null +++ b/src/backend/connector.nim @@ -0,0 +1,31 @@ +import options +import json, json_serialization +import core, response_type + +from gen import rpc + +const + EventConnectorSendRequestAccounts* = "connector.sendRequestAccounts" + +type RequestAccountsAcceptedArgs* = ref object of RootObj + requestId* {.serializedFieldName("requestId").}: string + account* {.serializedFieldName("account").}: string + chainId* {.serializedFieldName("chainId").}: uint + +type RejectedArgs* = ref object of RootObj + requestId* {.serializedFieldName("requestId").}: string + +rpc(requestAccountsAccepted, "connector"): + args: RequestAccountsAcceptedArgs + +rpc(requestAccountsRejected, "connector"): + args: RejectedArgs + +proc isSuccessResponse(rpcResponse: RpcResponse[JsonNode]): bool = + return rpcResponse.error.isNil + +proc requestAccountsAcceptedFinishedRpc*(args: RequestAccountsAcceptedArgs): bool = + return isSuccessResponse(requestAccountsAccepted(args)) + +proc requestAccountsRejectedFinishedRpc*(args: RejectedArgs): bool = + return isSuccessResponse(requestAccountsRejected(args)) \ No newline at end of file diff --git a/ui/app/AppLayouts/Wallet/services/dapps/DappsConnectorSDK.qml b/ui/app/AppLayouts/Wallet/services/dapps/DappsConnectorSDK.qml new file mode 100644 index 0000000000..951dae02ee --- /dev/null +++ b/ui/app/AppLayouts/Wallet/services/dapps/DappsConnectorSDK.qml @@ -0,0 +1,133 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 + +import QtWebEngine 1.10 +import QtWebChannel 1.15 + +import StatusQ.Core.Utils 0.1 as SQUtils +import StatusQ.Components 0.1 + +import StatusQ 0.1 +import SortFilterProxyModel 0.2 +import AppLayouts.Wallet.controls 1.0 +import shared.popups.walletconnect 1.0 +import AppLayouts.Wallet.services.dapps 1.0 +import AppLayouts.Wallet.services.dapps.types 1.0 + +import shared.stores 1.0 +import utils 1.0 + +import "types" + +// Act as another layer of abstraction to the WalletConnectSDKBase +// Quick hack until the WalletConnectSDKBase could be refactored to a more generic DappProviderBase with API to match +// the UX requirements +WalletConnectSDKBase { + id: root + + // Nim connector.controller instance + property var controller + property bool sdkReady: true + property bool active: true + required property WalletConnectService wcService + required property var walletStore + property string requestId: "" + + projectId: "" + + implicitWidth: 1 + implicitHeight: 1 + + Loader { + id: connectDappLoader + + active: false + + property var dappChains: [] + property var sessionProposal: null + property var availableNamespaces: null + property var sessionTopic: null + readonly property var proposalMedatada: !!sessionProposal + ? sessionProposal.params.proposer.metadata + : { name: "", url: "", icons: [] } + + sourceComponent: ConnectDAppModal { + visible: true + + onClosed: { + rejectSession(root.requestId) + connectDappLoader.active = false + } + accounts: root.wcService.validAccounts + flatNetworks: root.walletStore.filteredFlatModel + selectedAccountAddress: root.wcService.selectedAccountAddress + + dAppUrl: proposalMedatada.url + dAppName: proposalMedatada.name + dAppIconUrl: !!proposalMedatada.icons && proposalMedatada.icons.length > 0 ? proposalMedatada.icons[0] : "" + multipleChainSelection: false + + onConnect: { + connectDappLoader.active = false + approveSession(root.requestId, selectedAccount.address, selectedChains) + } + + onDecline: { + connectDappLoader.active = false + rejectSession(root.requestId) + } + } + } + + Connections { + target: controller + + onDappRequestsToConnect: function(requestId, dappInfoString) { + var dappInfo = JSON.parse(dappInfoString) + + let sessionProposal = { + "params": { + "optionalNamespaces": {}, + "proposer": { + "metadata": { + "description": "-", + "icons": [ + dappInfo.icon + ], + "name": dappInfo.name, + "url": dappInfo.url + } + }, + "requiredNamespaces": { + "eip155": { + "chains": [ + `eip155:${dappInfo.chainId}` + ], + "events": [], + "methods": ["eth_sendTransaction"] + } + } + } + }; + + connectDappLoader.sessionProposal = sessionProposal + connectDappLoader.active = true + root.requestId = requestId + } + } + + approveSession: function(requestId, account, selectedChains) { + controller.approveDappConnectRequest(requestId, account, JSON.stringify(selectedChains)) + } + + rejectSession: function(requestId) { + controller.rejectDappConnectRequest(requestId) + } + + // We don't expect requests for these. They are here only to spot errors + pair: function(pairLink) { console.error("ConnectorSDK.pair: not implemented") } + getPairings: function(callback) { console.error("ConnectorSDK.getPairings: not implemented") } + disconnectPairing: function(topic) { console.error("ConnectorSDK.disconnectPairing: not implemented") } + buildApprovedNamespaces: function(params, supportedNamespaces) { console.error("ConnectorSDK.buildApprovedNamespaces: not implemented") } +} diff --git a/ui/app/AppLayouts/Wallet/services/dapps/qmldir b/ui/app/AppLayouts/Wallet/services/dapps/qmldir index d3f89ee7d1..34a25042c4 100644 --- a/ui/app/AppLayouts/Wallet/services/dapps/qmldir +++ b/ui/app/AppLayouts/Wallet/services/dapps/qmldir @@ -1,6 +1,7 @@ WalletConnectSDKBase 1.0 WalletConnectSDKBase.qml WalletConnectSDK 1.0 WalletConnectSDK.qml WalletConnectService 1.0 WalletConnectService.qml +DappsConnectorSDK 1.0 DappsConnectorSDK.qml DAppsListProvider 1.0 DAppsListProvider.qml DAppsRequestHandler 1.0 DAppsRequestHandler.qml diff --git a/ui/app/AppLayouts/Wallet/stores/RootStore.qml b/ui/app/AppLayouts/Wallet/stores/RootStore.qml index 9bfeb07bc8..9869d41693 100644 --- a/ui/app/AppLayouts/Wallet/stores/RootStore.qml +++ b/ui/app/AppLayouts/Wallet/stores/RootStore.qml @@ -105,6 +105,7 @@ QtObject { readonly property var tmpActivityController1: walletSectionInst.tmpActivityController1 readonly property var activityDetailsController: walletSectionInst.activityDetailsController readonly property var walletConnectController: walletSectionInst.walletConnectController + readonly property var dappsConnectorController: walletSectionInst.dappsConnectorController readonly property bool isAccountTokensReloading: walletSectionInst.isAccountTokensReloading readonly property double lastReloadTimestamp: walletSectionInst.lastReloadTimestamp diff --git a/ui/app/AppLayouts/Wallet/views/NetworkSelectorView.qml b/ui/app/AppLayouts/Wallet/views/NetworkSelectorView.qml index 70b9a9b929..12c50872a8 100644 --- a/ui/app/AppLayouts/Wallet/views/NetworkSelectorView.qml +++ b/ui/app/AppLayouts/Wallet/views/NetworkSelectorView.qml @@ -40,8 +40,8 @@ StatusListView { onSelectionChanged: { if (!root.multiSelection && selection.length > 1) { - console.warn("Warning: Multi-selection is disabled, but multiple items are selected. Automatically selecting the last inserted item.") - selection = [selection[selection.length - 1]] + console.warn("Warning: Multi-selection is disabled, but multiple items are selected. Automatically selecting the first inserted item.") + selection = [selection[0]] } } diff --git a/ui/app/mainui/AppMain.qml b/ui/app/mainui/AppMain.qml index 527b2fad9e..bf7dd0f504 100644 --- a/ui/app/mainui/AppMain.qml +++ b/ui/app/mainui/AppMain.qml @@ -2046,19 +2046,56 @@ Item { } } + Component { + id: walletConnectSDK + + WalletConnectSDK { + active: WalletStore.RootStore.walletSectionInst.walletReady + projectId: WalletStore.RootStore.appSettings.walletConnectProjectID + } + } + + Component { + id: dappsConnectorSDK + + DappsConnectorSDK { + active: WalletStore.RootStore.walletSectionInst.walletReady + controller: WalletStore.RootStore.dappsConnectorController + wcService: Global.walletConnectService + walletStore: WalletStore.RootStore + } + } + + Loader { + id: walletConnectSDKLoader + active: Global.featureFlags.dappsEnabled + sourceComponent: walletConnectSDK + } + + Loader { + id: dappsConnectorSDKLoader + active: Global.featureFlags.connectorEnabled + sourceComponent: dappsConnectorSDK + } + + Loader { + id: sdkLoader + + onLoaded: { + walletConnectSDKLoader.active = Global.featureFlags.dappsEnabled + dappsConnectorSDKLoader.active = Global.featureFlags.connectorEnabled + } + } + Loader { id: walletConnectServiceLoader - active: Global.featureFlags.dappsEnabled + active: Global.featureFlags.dappsEnabled || Global.featureFlags.connectorEnabled sourceComponent: WalletConnectService { id: walletConnectService - wcSDK: WalletConnectSDK { - active: WalletStore.RootStore.walletSectionInst.walletReady - - projectId: WalletStore.RootStore.appSettings.walletConnectProjectID - } + wcSDK: sdkLoader.item store: DAppsStore { controller: WalletStore.RootStore.walletConnectController } diff --git a/ui/imports/shared/popups/walletconnect/ConnectDAppModal.qml b/ui/imports/shared/popups/walletconnect/ConnectDAppModal.qml index c0aa69645e..d4f6371341 100644 --- a/ui/imports/shared/popups/walletconnect/ConnectDAppModal.qml +++ b/ui/imports/shared/popups/walletconnect/ConnectDAppModal.qml @@ -72,6 +72,8 @@ StatusDialog { */ property string selectedAccountAddress: contextCard.selectedAccount.address ?? "" + property bool multipleChainSelection: true + readonly property alias selectedAccount: contextCard.selectedAccount readonly property alias selectedChains: d.selectedChains @@ -118,6 +120,7 @@ StatusDialog { Layout.maximumWidth: root.availableWidth Layout.fillWidth: true + multipleChainSelection: root.multipleChainSelection selectedAccountAddress: root.selectedAccountAddress connectionAttempted: d.connectionAttempted accountsModel: d.accountsProxy diff --git a/ui/imports/shared/popups/walletconnect/private/ContextCard.qml b/ui/imports/shared/popups/walletconnect/private/ContextCard.qml index 5d5845659b..c11baeeb43 100644 --- a/ui/imports/shared/popups/walletconnect/private/ContextCard.qml +++ b/ui/imports/shared/popups/walletconnect/private/ContextCard.qml @@ -16,6 +16,7 @@ Rectangle { property var accountsModel property var chainsModel property alias chainSelection: networkFilter.selection + property bool multipleChainSelection: true readonly property alias selectedAccount: accountsDropdown.currentAccount @@ -79,7 +80,7 @@ Rectangle { flatNetworks: root.chainsModel showTitle: true - multiSelection: true + multiSelection: root.multipleChainSelection showAllSelectedText: false selectionAllowed: !root.connectionAttempted && root.chainsModel.ModelCount.count > 1 }