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
This commit is contained in:
Stefan 2024-07-23 17:26:55 +03:00 committed by Stefan Dunca
parent 35b7f0583d
commit 7fb241b964
4 changed files with 62 additions and 9 deletions

View File

@ -552,7 +552,9 @@ Item {
dappsListReceivedJsonStr: dappsListReceivedJsonStr dappsListReceivedJsonStr: dappsListReceivedJsonStr
}) })
verify(!!store) 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) verify(!!provider)
} }
@ -576,7 +578,8 @@ Item {
sdk.sdkReady = true sdk.sdkReady = true
compare(sdk.getActiveSessionsCallbacks.length, 1, "expected a call to sdk.getActiveSessions when SDK becomes ready") compare(sdk.getActiveSessionsCallbacks.length, 1, "expected a call to sdk.getActiveSessions when SDK becomes ready")
let callback = sdk.getActiveSessionsCallbacks[0].callback 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}) callback({"b536a": session, "b537b": session})
compare(provider.dappsModel.count, 1, "expected dappsModel have the SDK's reported dapps") compare(provider.dappsModel.count, 1, "expected dappsModel have the SDK's reported dapps")
compare(provider.dappsModel.get(0).iconUrl, "", "expected iconUrl to be missing") compare(provider.dappsModel.get(0).iconUrl, "", "expected iconUrl to be missing")
@ -665,6 +668,28 @@ Item {
verify(eip155.hasOwnProperty("events")) verify(eip155.hasOwnProperty("events"))
compare(eip155.events.length, 2) 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 { Component {

View File

@ -2,6 +2,8 @@ import QtQuick 2.15
import StatusQ.Core.Utils 0.1 import StatusQ.Core.Utils 0.1
import AppLayouts.Wallet.services.dapps 1.0
import shared.stores 1.0 import shared.stores 1.0
import utils 1.0 import utils 1.0
@ -11,6 +13,7 @@ QObject {
required property WalletConnectSDKBase sdk required property WalletConnectSDKBase sdk
required property DAppsStore store required property DAppsStore store
required property var supportedAccountsModel
readonly property alias dappsModel: d.dappsModel readonly property alias dappsModel: d.dappsModel
@ -46,11 +49,12 @@ QObject {
} }
getActiveSessionsFn = () => { getActiveSessionsFn = () => {
sdk.getActiveSessions((sessions) => { sdk.getActiveSessions((allSessions) => {
root.store.dappsListReceived.disconnect(dappsListReceivedFn); root.store.dappsListReceived.disconnect(dappsListReceivedFn);
let tmpMap = {} let tmpMap = {}
var topics = [] var topics = []
const sessions = Helpers.filterActiveSessionsForKnownAccounts(allSessions, root.supportedAccountsModel)
for (let key in sessions) { for (let key in sessions) {
let dapp = sessions[key].peer.metadata let dapp = sessions[key].peer.metadata
if (!!dapp.icons && dapp.icons.length > 0) { if (!!dapp.icons && dapp.icons.length > 0) {
@ -61,7 +65,7 @@ QObject {
tmpMap[dapp.url] = dapp; tmpMap[dapp.url] = dapp;
topics.push(key) 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(); dapps.clear();
// Iterate tmpMap and fill dapps // Iterate tmpMap and fill dapps
for (let key in tmpMap) { for (let key in tmpMap) {

View File

@ -36,7 +36,7 @@ QObject {
readonly property alias requestHandler: requestHandler readonly property alias requestHandler: requestHandler
readonly property var validAccounts: SortFilterProxyModel { readonly property var validAccounts: SortFilterProxyModel {
sourceModel: root.walletRootStore.nonWatchAccounts sourceModel: d.supportedAccountsModel
proxyRoles: [ proxyRoles: [
FastExpressionRole { FastExpressionRole {
name: "colorizedChainPrefixes" name: "colorizedChainPrefixes"
@ -108,10 +108,11 @@ QObject {
} }
function disconnectDapp(url) { function disconnectDapp(url) {
wcSDK.getActiveSessions((sessions) => { wcSDK.getActiveSessions((allSessions) => {
for (let key in sessions) { const sessions = Helpers.filterActiveSessionsForKnownAccounts(allSessions, d.supportedAccountsModel)
let dapp = sessions[key].peer.metadata for (let session in sessions) {
let topic = sessions[key].topic let dapp = session.peer.metadata
let topic = session.topic
if (dapp.url == url) { if (dapp.url == url) {
wcSDK.disconnectSession(topic) wcSDK.disconnectSession(topic)
} }
@ -218,6 +219,8 @@ QObject {
QObject { QObject {
id: d id: d
readonly property var supportedAccountsModel: root.walletRootStore.nonWatchAccounts
property var currentSessionProposal: null property var currentSessionProposal: null
property var acceptedSessionProposal: null property var acceptedSessionProposal: null
@ -255,6 +258,7 @@ QObject {
sdk: root.wcSDK sdk: root.wcSDK
store: root.store 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 // 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

View File

@ -100,3 +100,23 @@ function extractInfoFromPairUri(uri) {
} }
return { topic, expiry } 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
}