fix(WalletConnect): Fixing disconnect notifications

The disconnect notifications were operating on `WalletConnectService.currentSessionProposal`. This object stores the current session object on connect, but it's not necessarily the same session the user wants to disconnect.
To fix this I'm getting the active sessions from status-go when the disconnect request is received (from Status or dapp). If the topic matches to any connection topic owned by the users accounts we'll show a notification.

(cherry picked from commit fea4e8ed76)
This commit is contained in:
Alex Jbanca 2024-08-02 14:18:58 +03:00 committed by Alex Jbanca
parent c0e5111fe5
commit 833848188c
5 changed files with 91 additions and 14 deletions

View File

@ -1,5 +1,5 @@
import NimQml import NimQml
import chronicles import chronicles, times, json
import app_service/service/wallet_connect/service as wallet_connect_service import app_service/service/wallet_connect/service as wallet_connect_service
import app_service/service/wallet_account/service as wallet_account_service import app_service/service/wallet_account/service as wallet_account_service
@ -47,6 +47,20 @@ QtObject:
self.dappsListReceived(res) self.dappsListReceived(res)
return true return true
proc activeSessionsReceived(self: Controller, activeSessionsJson: string) {.signal.}
# Emits signal activeSessionsReceived with the list of active sessions
# TODO: make it async
proc getActiveSessions(self: Controller): bool {.slot.} =
let validAtTimestamp = now().toTime().toUnix()
let res = self.service.getActiveSessions(validAtTimestamp)
if res.isNil:
return false
else:
let resultStr = $res
self.activeSessionsReceived(resultStr)
return true
proc userAuthenticationResult*(self: Controller, topic: string, id: string, error: bool, password: string, pin: string, payload: string) {.signal.} proc userAuthenticationResult*(self: Controller, topic: string, id: string, error: bool, password: string, pin: string, payload: string) {.signal.}
# Beware, it will fail if an authentication is already in progress # Beware, it will fail if an authentication is already in progress

View File

@ -115,6 +115,11 @@ QtObject:
# TODO #14588: call it async # TODO #14588: call it async
return status_go.getDapps(validAtEpoch, testChains) return status_go.getDapps(validAtEpoch, testChains)
proc getActiveSessions*(self: Service, validAtTimestamp: int64): JsonNode =
# TODO #14588: call it async
return status_go.getActiveSessions(validAtTimestamp)
# Will fail if another authentication is in progress # Will fail if another authentication is in progress
proc authenticateUser*(self: Service, keyUid: string, callback: AuthenticationResponseFn): bool = proc authenticateUser*(self: Service, keyUid: string, callback: AuthenticationResponseFn): bool =
if self.authenticationCallback != nil: if self.authenticationCallback != nil:

View File

@ -18,7 +18,7 @@ rpc(disconnectWalletConnectSession, "wallet"):
topic: string topic: string
rpc(getWalletConnectActiveSessions, "wallet"): rpc(getWalletConnectActiveSessions, "wallet"):
validAtTimestamp: int validAtTimestamp: int64
rpc(hashMessageEIP191, "wallet"): rpc(hashMessageEIP191, "wallet"):
message: string message: string
@ -50,13 +50,14 @@ proc disconnectSession*(topic: string): bool =
return false return false
# returns nil if error # returns nil if error
proc getActiveSessions*(validAtTimestamp: int): JsonNode = proc getActiveSessions*(validAtTimestamp: int64): JsonNode =
try: try:
let rpcRes = getWalletConnectActiveSessions(validAtTimestamp) let rpcRes = getWalletConnectActiveSessions(validAtTimestamp)
if(not isSuccessResponse(rpcRes)): if(not isSuccessResponse(rpcRes)):
return nil return nil
let jsonResultStr = rpcRes.result.getStr() let jsonResultStr = $rpcRes.result
if jsonResultStr == "null" or jsonResultStr == "": if jsonResultStr == "null" or jsonResultStr == "":
return newJArray() return newJArray()

View File

@ -236,16 +236,7 @@ QObject {
} }
function onSessionDelete(topic, err) { function onSessionDelete(topic, err) {
store.deactivateWalletConnectSession(topic) d.disconnectSessionRequested(topic, err)
dappsProvider.updateDapps()
const app_url = d.currentSessionProposal ? d.currentSessionProposal.params.proposer.metadata.url : "-"
const app_domain = StringUtils.extractDomainFromLink(app_url)
if(err) {
root.displayToastMessage(qsTr("Failed to disconnect from %1").arg(app_domain), true)
} else {
root.displayToastMessage(qsTr("Disconnected from %1").arg(app_domain), false)
}
} }
} }
@ -267,6 +258,54 @@ QObject {
timeoutTimer.stop() timeoutTimer.stop()
root.pairingValidated(state) root.pairingValidated(state)
} }
function disconnectSessionRequested(topic, err) {
// Get all sessions and filter the active ones for known accounts
// Act on the first matching session with the same topic
const activeSessionsCallback = (allSessions, success) => {
store.activeSessionsReceived.disconnect(activeSessionsCallback)
if (!success) {
// TODO #14754: implement custom dApp notification
d.notifyDappDisconnect("-", true)
return
}
// Convert to original format
const webSdkSessions = allSessions.map((session) => {
return JSON.parse(session.sessionJson)
})
const sessions = DAppsHelpers.filterActiveSessionsForKnownAccounts(webSdkSessions, root.validAccounts)
for (const sessionID in sessions) {
const session = sessions[sessionID]
if (session.topic === topic) {
store.deactivateWalletConnectSession(topic)
dappsProvider.updateDapps()
const dappUrl = session.peer.metadata.url ?? "-"
d.notifyDappDisconnect(dappUrl, err)
break
}
}
}
store.activeSessionsReceived.connect(activeSessionsCallback)
if (!store.getActiveSessions()) {
store.activeSessionsReceived.disconnect(activeSessionsCallback)
// TODO #14754: implement custom dApp notification
}
}
function notifyDappDisconnect(dappUrl, err) {
const appDomain = StringUtils.extractDomainFromLink(dappUrl)
if(err) {
root.displayToastMessage(qsTr("Failed to disconnect from %1").arg(appDomain), true)
} else {
root.displayToastMessage(qsTr("Disconnected from %1").arg(appDomain), false)
}
}
} }
Component.onCompleted: { Component.onCompleted: {

View File

@ -8,6 +8,7 @@ QObject {
required property var controller required property var controller
/// \c dappsJson serialized from status-go.wallet.GetDapps /// \c dappsJson serialized from status-go.wallet.GetDapps
signal dappsListReceived(string dappsJson) signal dappsListReceived(string dappsJson)
signal activeSessionsReceived(var activeSessionsJsonObj, bool success)
signal userAuthenticated(string topic, string id, string password, string pin, string payload) signal userAuthenticated(string topic, string id, string password, string pin, string payload)
signal userAuthenticationFailed(string topic, string id) signal userAuthenticationFailed(string topic, string id)
@ -107,6 +108,12 @@ QObject {
return controller.getDapps() return controller.getDapps()
} }
/// \c getActiveSessions triggers an async response to \c activeSessionsReceived
/// \returns true if the request was sent successfully
function getActiveSessions() {
return controller.getActiveSessions()
}
function hexToDec(hex) { function hexToDec(hex) {
return controller.hexToDecBigString(hex) return controller.hexToDecBigString(hex)
} }
@ -124,6 +131,17 @@ QObject {
root.dappsListReceived(dappsJson) root.dappsListReceived(dappsJson)
} }
function onActiveSessionsReceived(activeSessionsJson) {
try {
const jsonObj = JSON.parse(activeSessionsJson)
root.activeSessionsReceived(jsonObj, true)
} catch (e) {
console.error("Failed to parse activeSessionsJson", e)
root.activeSessionsReceived({}, false)
return
}
}
function onUserAuthenticationResult(topic, id, success, password, pin, payload) { function onUserAuthenticationResult(topic, id, success, password, pin, payload) {
if (success) { if (success) {
root.userAuthenticated(topic, id, password, pin, payload) root.userAuthenticated(topic, id, password, pin, payload)