From 7fb241b96466e1ada90d89946205fc63ec0686f0 Mon Sep 17 00:00:00 2001 From: Stefan Date: Tue, 23 Jul 2024 17:26:55 +0300 Subject: [PATCH] fix(dapps): filter out dApps connected on other profiles Add helper function to filter out dApps with sessions that don't have any supported account in the available accounts list. Use helper function to filter out dApps connected on other profiles when listing dApps in the dApps list. When disconnecting an app only disconnect sessions for the current profile. Closes: #14888, #15707 --- .../qmlTests/tests/tst_DAppsWorkflow.qml | 29 +++++++++++++++++-- .../services/dapps/DAppsListProvider.qml | 8 +++-- .../services/dapps/WalletConnectService.qml | 14 +++++---- .../Wallet/services/dapps/helpers.js | 20 +++++++++++++ 4 files changed, 62 insertions(+), 9 deletions(-) diff --git a/storybook/qmlTests/tests/tst_DAppsWorkflow.qml b/storybook/qmlTests/tests/tst_DAppsWorkflow.qml index a44d99e7d0..830353a383 100644 --- a/storybook/qmlTests/tests/tst_DAppsWorkflow.qml +++ b/storybook/qmlTests/tests/tst_DAppsWorkflow.qml @@ -552,7 +552,9 @@ Item { dappsListReceivedJsonStr: dappsListReceivedJsonStr }) verify(!!store) - provider = createTemporaryObject(dappsListProviderComponent, root, {sdk: sdk, store: store}) + const walletStore = createTemporaryObject(walletStoreComponent, root) + verify(!!walletStore) + provider = createTemporaryObject(dappsListProviderComponent, root, {sdk: sdk, store: store, supportedAccountsModel: walletStore.nonWatchAccounts}) verify(!!provider) } @@ -576,7 +578,8 @@ Item { sdk.sdkReady = true compare(sdk.getActiveSessionsCallbacks.length, 1, "expected a call to sdk.getActiveSessions when SDK becomes ready") let callback = sdk.getActiveSessionsCallbacks[0].callback - let session = JSON.parse(Testing.formatApproveSessionResponse([1, 2], ["0x1"], {dappMetadataJsonString: Testing.noIconsDappMetadataJsonString})) + const address = ModelUtils.get(provider.supportedAccountsModel, 0, "address") + let session = JSON.parse(Testing.formatApproveSessionResponse([1, 2], [address], {dappMetadataJsonString: Testing.noIconsDappMetadataJsonString})) callback({"b536a": session, "b537b": session}) compare(provider.dappsModel.count, 1, "expected dappsModel have the SDK's reported dapps") compare(provider.dappsModel.get(0).iconUrl, "", "expected iconUrl to be missing") @@ -665,6 +668,28 @@ Item { verify(eip155.hasOwnProperty("events")) compare(eip155.events.length, 2) } + + function test_filterActiveSessionsForKnownAccounts() { + const account1 = accountsModel.get(0) + const account2 = accountsModel.get(1) + const chainIds = [chainsModel.get(0).chainId, chainsModel.get(1).chainId] + const knownSession = JSON.parse(Testing.formatApproveSessionResponse(chainIds, [account2.address])) + // Allow the unlikely unknown accounts to cover for the deleted accounts case + const unknownSessionWithKnownAccount = JSON.parse(Testing.formatApproveSessionResponse(chainIds, ['0x03acc', account1.address])) + const unknownSession1 = JSON.parse(Testing.formatApproveSessionResponse(chainIds, ['0x83acc'])) + const unknownSession2 = JSON.parse(Testing.formatApproveSessionResponse(chainIds, ['0x12acc'])) + let testSessions = { + "b536a": knownSession, + "b537b": unknownSession1, + "b538c": unknownSession2, + "b539d": unknownSessionWithKnownAccount + } + let res = Helpers.filterActiveSessionsForKnownAccounts(testSessions, accountsModel) + compare(Object.keys(res).length, 2, "expected two sessions to be returned") + // Also test that order is stable + compare(res["b536a"], knownSession, "expected the known session to be returned") + compare(res["b539d"], unknownSessionWithKnownAccount, "expected the known session to be returned") + } } Component { diff --git a/ui/app/AppLayouts/Wallet/services/dapps/DAppsListProvider.qml b/ui/app/AppLayouts/Wallet/services/dapps/DAppsListProvider.qml index 2dfc42da45..a4989f52be 100644 --- a/ui/app/AppLayouts/Wallet/services/dapps/DAppsListProvider.qml +++ b/ui/app/AppLayouts/Wallet/services/dapps/DAppsListProvider.qml @@ -2,6 +2,8 @@ import QtQuick 2.15 import StatusQ.Core.Utils 0.1 +import AppLayouts.Wallet.services.dapps 1.0 + import shared.stores 1.0 import utils 1.0 @@ -11,6 +13,7 @@ QObject { required property WalletConnectSDKBase sdk required property DAppsStore store + required property var supportedAccountsModel readonly property alias dappsModel: d.dappsModel @@ -46,11 +49,12 @@ QObject { } getActiveSessionsFn = () => { - sdk.getActiveSessions((sessions) => { + sdk.getActiveSessions((allSessions) => { root.store.dappsListReceived.disconnect(dappsListReceivedFn); let tmpMap = {} var topics = [] + const sessions = Helpers.filterActiveSessionsForKnownAccounts(allSessions, root.supportedAccountsModel) for (let key in sessions) { let dapp = sessions[key].peer.metadata if (!!dapp.icons && dapp.icons.length > 0) { @@ -61,7 +65,7 @@ QObject { tmpMap[dapp.url] = dapp; topics.push(key) } - // TODO #14755: on SDK dApps refresh update the model that has data source from persistence instead of using reset + // TODO #15075: on SDK dApps refresh update the model that has data source from persistence instead of using reset dapps.clear(); // Iterate tmpMap and fill dapps for (let key in tmpMap) { diff --git a/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectService.qml b/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectService.qml index 22cd1c2d9e..e68d1562a4 100644 --- a/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectService.qml +++ b/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectService.qml @@ -36,7 +36,7 @@ QObject { readonly property alias requestHandler: requestHandler readonly property var validAccounts: SortFilterProxyModel { - sourceModel: root.walletRootStore.nonWatchAccounts + sourceModel: d.supportedAccountsModel proxyRoles: [ FastExpressionRole { name: "colorizedChainPrefixes" @@ -108,10 +108,11 @@ QObject { } function disconnectDapp(url) { - wcSDK.getActiveSessions((sessions) => { - for (let key in sessions) { - let dapp = sessions[key].peer.metadata - let topic = sessions[key].topic + wcSDK.getActiveSessions((allSessions) => { + const sessions = Helpers.filterActiveSessionsForKnownAccounts(allSessions, d.supportedAccountsModel) + for (let session in sessions) { + let dapp = session.peer.metadata + let topic = session.topic if (dapp.url == url) { wcSDK.disconnectSession(topic) } @@ -218,6 +219,8 @@ QObject { QObject { id: d + readonly property var supportedAccountsModel: root.walletRootStore.nonWatchAccounts + property var currentSessionProposal: null property var acceptedSessionProposal: null @@ -255,6 +258,7 @@ QObject { sdk: root.wcSDK store: root.store + supportedAccountsModel: d.supportedAccountsModel } // Timeout for the corner case where the URL was already dismissed and the SDK doesn't respond with an error nor advances with the proposal diff --git a/ui/app/AppLayouts/Wallet/services/dapps/helpers.js b/ui/app/AppLayouts/Wallet/services/dapps/helpers.js index a7c2db0590..bdb62aa661 100644 --- a/ui/app/AppLayouts/Wallet/services/dapps/helpers.js +++ b/ui/app/AppLayouts/Wallet/services/dapps/helpers.js @@ -100,3 +100,23 @@ function extractInfoFromPairUri(uri) { } return { topic, expiry } } + +function filterActiveSessionsForKnownAccounts(sessions, accountsModel) { + let knownSessions = ({}) + Object.keys(sessions).forEach((topic) => { + const session = sessions[topic] + const eip155Addresses = session.namespaces.eip155.accounts + const accountSet = new Set( + eip155Addresses.map(eip155Address => eip155Address.split(':').pop().trim()) + ); + const uniqueAddresses = Array.from(accountSet); + const firstAccount = SQUtils.ModelUtils.getFirstModelEntryIf(accountsModel, (account) => { + return uniqueAddresses.includes(account.address) + }) + if (!firstAccount) { + return + } + knownSessions[topic] = session + }) + return knownSessions +} \ No newline at end of file