status-desktop/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectService.qml

194 lines
6.3 KiB
QML

import QtQuick 2.15
import StatusQ 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Core.Utils 0.1
import AppLayouts.Wallet 1.0
import AppLayouts.Wallet.services.dapps 1.0
import AppLayouts.Wallet.services.dapps.types 1.0
import AppLayouts.Profile.stores 1.0
import shared.stores 1.0
import shared.popups.walletconnect 1.0
import SortFilterProxyModel 0.2
import utils 1.0
import "types"
QObject {
id: root
required property WalletConnectSDKBase wcSDK
required property DAppsStore store
required property WalletStore walletStore
readonly property alias dappsModel: dappsProvider.dappsModel
readonly property alias requestHandler: requestHandler
readonly property var validAccounts: SortFilterProxyModel {
sourceModel: root.walletStore ? root.walletStore.accounts : null
filters: ValueFilter {
roleName: "walletType"
value: Constants.watchWalletType
inverted: true
}
proxyRoles: [
FastExpressionRole {
name: "colorizedChainPrefixes"
function getChainShortNames(chainIds) {
const chainShortNames = root.walletStore.getNetworkShortNames(chainIds)
return WalletUtils.colorizedChainPrefix(chainShortNames)
}
expression: getChainShortNames(model.preferredSharingChainIds)
expectedRoles: ["preferredSharingChainIds"]
}
]
}
readonly property var flatNetworks: root.walletStore ? root.walletStore.flatNetworks : null
function pair(uri) {
d.acceptedSessionProposal = null
wcSDK.pair(uri)
}
function approvePairSession(sessionProposal, approvedChainIds, approvedAccount) {
d.acceptedSessionProposal = sessionProposal
let approvedNamespaces = JSON.parse(
Helpers.buildSupportedNamespaces(approvedChainIds,
[approvedAccount.address],
SessionRequest.getSupportedMethods())
)
wcSDK.buildApprovedNamespaces(sessionProposal.params, approvedNamespaces)
}
function rejectPairSession(id) {
wcSDK.rejectSession(id)
}
function disconnectDapp(sessionTopic) {
wcSDK.disconnectSession(sessionTopic)
}
signal connectDApp(var dappChains, var sessionProposal, var approvedNamespaces)
signal approveSessionResult(var session, var error)
signal sessionRequest(SessionRequestResolved request)
signal displayToastMessage(string message, bool error)
readonly property Connections sdkConnections: Connections {
target: wcSDK
function onSessionProposal(sessionProposal) {
d.currentSessionProposal = sessionProposal
let supportedNamespacesStr = Helpers.buildSupportedNamespacesFromModels(
root.flatNetworks, root.validAccounts, SessionRequest.getSupportedMethods())
wcSDK.buildApprovedNamespaces(sessionProposal.params, JSON.parse(supportedNamespacesStr))
}
function onBuildApprovedNamespacesResult(approvedNamespaces, error) {
if(error) {
// TODO: error reporting
return
}
if (d.acceptedSessionProposal) {
wcSDK.approveSession(d.acceptedSessionProposal, approvedNamespaces)
} else {
let res = Helpers.extractChainsAndAccountsFromApprovedNamespaces(approvedNamespaces)
root.connectDApp(res.chains, d.currentSessionProposal, approvedNamespaces)
}
}
function onApproveSessionResult(session, err) {
if (err) {
// TODO #14676: handle the error
console.error("Failed to approve session", err)
return
}
// TODO #14754: implement custom dApp notification
let app_url = d.currentSessionProposal ? d.currentSessionProposal.params.proposer.metadata.url : "-"
root.displayToastMessage(qsTr("Connected to %1 via WalletConnect").arg(app_url), false)
// Persist session
if(!store.addWalletConnectSession(JSON.stringify(session))) {
console.error("Failed to persist session")
}
// Notify client
root.approveSessionResult(session, err)
dappsProvider.updateDapps()
}
function onRejectSessionResult(err) {
let app_url = d.currentSessionProposal ? d.currentSessionProposal.params.proposer.metadata.url : "-"
if(err) {
root.displayToastMessage(qsTr("Failed to reject connection request for %1").arg(app_url), true)
} else {
root.displayToastMessage(qsTr("Connection request for %1 was rejected").arg(app_url), false)
}
}
function onSessionDelete(topic, err) {
let app_url = d.currentSessionProposal ? d.currentSessionProposal.params.proposer.metadata.url : "-"
if(err) {
root.displayToastMessage(qsTr("Failed to disconnect from %1").arg(app_url), true)
} else {
root.displayToastMessage(qsTr("Disconnected from %1").arg(app_url), false)
}
}
}
QObject {
id: d
property var currentSessionProposal: null
property var acceptedSessionProposal: null
// TODO #14676: use it to check if already paired
function getPairingTopicFromPairingUrl(url)
{
if (!url.startsWith("wc:"))
{
return null;
}
const atIndex = url.indexOf("@");
if (atIndex < 0)
{
return null;
}
return url.slice(3, atIndex);
}
}
Component.onCompleted: {
dappsProvider.updateDapps()
}
DAppsRequestHandler {
id: requestHandler
sdk: root.wcSDK
store: root.store
walletStore: root.walletStore
onSessionRequest: (request) => {
root.sessionRequest(request)
}
onDisplayToastMessage: (message, error) => {
root.displayToastMessage(message, error)
}
}
DAppsListProvider {
id: dappsProvider
sdk: root.wcSDK
store: root.store
}
}