chore(walletconnect): improvements identifying pairings and sessions

This commit is contained in:
Sale Djenic 2023-12-08 19:18:58 +01:00 committed by saledjenic
parent f1c090a765
commit d587552c40
7 changed files with 571 additions and 298 deletions

View File

@ -80,17 +80,23 @@ QtObject:
error "Unknown identifier" error "Unknown identifier"
# supportedNamespaces is a Namespace as defined in status-go: services/wallet/walletconnect/walletconnect.go # supportedNamespaces is a Namespace as defined in status-go: services/wallet/walletconnect/walletconnect.go
proc proposeUserPair*(self: Controller, sessionProposalJson: string, supportedNamespacesJson: string) {.signal.} proc respondSessionProposal*(self: Controller, sessionProposalJson: string, supportedNamespacesJson: string, error: string) {.signal.}
proc pairSessionProposal(self: Controller, sessionProposalJson: string) {.slot.} = proc sessionProposal(self: Controller, sessionProposalJson: string) {.slot.} =
var res: JsonNode var
let err = backend_wallet_connect.pair(res, sessionProposalJson) supportedNamespacesJson: string
if err.len > 0: error: string
error "Failed to pair session" try:
return var res: JsonNode
let sessionProposalJson = if res.hasKey("sessionProposal"): $res["sessionProposal"] else: "" let err = backend_wallet_connect.pair(res, sessionProposalJson)
let supportedNamespacesJson = if res.hasKey("supportedNamespaces"): $res["supportedNamespaces"] else: "" if err.len > 0:
self.proposeUserPair(sessionProposalJson, supportedNamespacesJson) raise newException(CatchableError, err)
supportedNamespacesJson = if res.hasKey("supportedNamespaces"): $res["supportedNamespaces"] else: ""
except Exception as e:
error = e.msg
error "pairing", msg=error
self.respondSessionProposal(sessionProposalJson, supportedNamespacesJson, error)
proc recordSuccessfulPairing(self: Controller, sessionProposalJson: string) {.slot.} = proc recordSuccessfulPairing(self: Controller, sessionProposalJson: string) {.slot.} =
if backend_wallet_connect.recordSuccessfulPairing(sessionProposalJson): if backend_wallet_connect.recordSuccessfulPairing(sessionProposalJson):

View File

@ -11,6 +11,8 @@ ListView {
signal disconnect(string topic) signal disconnect(string topic)
spacing: 32
delegate: Item { delegate: Item {
implicitWidth: delegateLayout.implicitWidth implicitWidth: delegateLayout.implicitWidth
implicitHeight: delegateLayout.implicitHeight implicitHeight: delegateLayout.implicitHeight
@ -19,10 +21,16 @@ ListView {
id: delegateLayout id: delegateLayout
width: root.width width: root.width
StatusIcon {
icon: model.peerMetadata.icons.length > 0? model.peerMetadata.icons[0] : ""
visible: !!icon
}
StatusBaseText { StatusBaseText {
text: `${SQUtils.Utils.elideText(model.topic, 6, 6)}\n${new Date(model.expiry * 1000).toLocaleString()}` text: `${model.peerMetadata.name}\n${model.peerMetadata.url}\nTopic: ${SQUtils.Utils.elideText(model.topic, 6, 6)}\nExpire: ${new Date(model.expiry * 1000).toLocaleString()}`
color: model.active ? "green" : "orange" color: model.active ? "green" : "orange"
} }
StatusButton { StatusButton {
text: "Disconnect" text: "Disconnect"
@ -34,4 +42,4 @@ ListView {
} }
} }
} }
} }

View File

@ -0,0 +1,155 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import StatusQ.Controls 0.1
import StatusQ.Core 0.1
import StatusQ.Core.Utils 0.1 as SQUtils
ListView {
id: root
signal disconnect(string topic)
signal ping(string topic)
spacing: 48
delegate: Item {
implicitWidth: delegateLayout.implicitWidth
implicitHeight: delegateLayout.implicitHeight
ListModel {
id: namespacesListModel
}
Component.onCompleted: {
for (var key of Object.keys(model.namespaces)) {
let namespace = model.namespaces[key]
let obj = {
"eip": "",
"chain": "",
"methods": namespace.methods.join(", "),
"events": namespace.events.join(", ")
}
if (namespace.chains.length > 0) {
let data = namespace.chains[0].split(":")
if (data.length === 2) {
obj["eip"] = data[0]
obj["chain"] = data[1]
}
}
namespacesListModel.append(obj)
}
}
ColumnLayout {
id: delegateLayout
width: root.width
spacing: 8
StatusIcon {
icon: model.peer.metadata.icons.length > 0? model.peer.metadata.icons[0] : ""
visible: !!icon
}
StatusBaseText {
text: `Pairing topic:${SQUtils.Utils.elideText(model.pairingTopic, 6, 6)}\n${model.peer.metadata.name}\n${model.peer.metadata.url}`
}
StatusBaseText {
text: `Session topic:${SQUtils.Utils.elideText(model.topic, 6, 6)}\nExpire:${new Date(model.expiry * 1000).toLocaleString()}`
}
Rectangle {
color: "transparent"
border.color: "grey"
border.width: 1
Layout.fillWidth: true
Layout.preferredHeight: allNamespaces.implicitHeight
ColumnLayout {
id: allNamespaces
Repeater {
model: namespacesListModel
delegate: Rectangle {
id: namespaceDelegateRoot
property bool expanded: false
color: "transparent"
border.color: "grey"
border.width: 1
Layout.fillWidth: true
Layout.preferredHeight: namespace.implicitHeight
ColumnLayout {
id: namespace
spacing: 8
RowLayout {
StatusBaseText {
text: `Review ${model.eip} permissions`
}
StatusIcon {
icon: namespaceDelegateRoot.expanded? "chevron-up" : "chevron-down"
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onClicked: {
namespaceDelegateRoot.expanded = !namespaceDelegateRoot.expanded
}
}
}
}
StatusBaseText {
Layout.fillWidth: true
visible: namespaceDelegateRoot.expanded
text: `Chain ${model.chain}`
}
StatusBaseText {
Layout.fillWidth: true
visible: namespaceDelegateRoot.expanded
text: `Methods: ${model.methods}\nEvents: ${model.events}`
}
}
}
}
}
}
RowLayout {
StatusButton {
text: "Disconnect"
onClicked: {
root.disconnect(model.topic)
}
}
StatusButton {
text: "Ping"
onClicked: {
root.ping(model.topic)
}
}
}
}
}
}

View File

@ -45,104 +45,198 @@ Popup {
ColumnLayout { ColumnLayout {
id: mainLayout id: mainLayout
spacing: 8
StatusBaseText { StatusBaseText {
text: qsTr("Debugging UX until design is ready") text: qsTr("Debugging UX until design is ready")
font.bold: true
} }
StatusSwitch { StatusTabBar {
id: testAuthentication id: tabBar
checkable: true
text: qsTr("Test Authentication")
}
StatusInput {
id: pairLinkInput
Layout.fillWidth: true Layout.fillWidth: true
placeholderText: "Insert pair link" StatusTabButton {
width: implicitWidth
text: qsTr("WalletConnect")
}
StatusTabButton {
width: implicitWidth
text: qsTr("Sessions")
}
StatusTabButton {
width: implicitWidth
text: qsTr("Pairings")
}
} }
RowLayout { StackLayout {
Layout.fillWidth: true Layout.fillWidth: true
currentIndex: tabBar.currentIndex
StatusButton { ColumnLayout {
text: testAuthentication.checked? "Authentication" : "Pair"
onClicked: { StatusSwitch {
if (testAuthentication.checked) { id: testAuthentication
d.setStatusText("") checkable: true
d.setDetailsText("") text: qsTr("Test Authentication")
d.state = "" }
accountsModel.clear()
statusText.text = "Authenticating..." StatusInput {
root.sdk.auth(pairLinkInput.text) id: pairLinkInput
return
Layout.fillWidth: true
placeholderText: "Insert pair link"
}
RowLayout {
Layout.fillWidth: true
StatusButton {
text: testAuthentication.checked? "Authentication" : "Pair"
onClicked: {
d.setStatusText("")
d.setDetailsText("")
d.state = ""
accountsModel.clear()
if (testAuthentication.checked) {
d.setStatusText("Authenticating...")
root.sdk.auth(pairLinkInput.text)
return
}
d.setStatusText("Pairing...")
root.sdk.pair(pairLinkInput.text)
}
enabled: pairLinkInput.text.length > 0 && root.sdk.sdkReady
} }
statusText.text = "Pairing..." StatusButton {
root.sdk.pair(pairLinkInput.text) text: "Accept"
onClicked: {
root.sdk.approveSession(d.observedData, d.supportedNamespaces)
}
visible: d.state === d.waitingPairState
}
StatusButton {
text: "Reject"
onClicked: {
root.sdk.rejectSession(d.observedData.id)
}
visible: d.state === d.waitingPairState
}
}
ButtonGroup {
id: selAccBtnGroup
}
SelectAccount {
Layout.fillWidth: true
Layout.preferredHeight: contentHeight
model: accountsModel
buttonGroup: selAccBtnGroup
onAccountSelected: {
root.sdk.formatAuthMessage(d.observedData.params.cacaoPayload, address)
}
}
RowLayout {
StatusButton {
text: "Accept"
onClicked: {
if (testAuthentication.checked) {
root.controller.authRequest(d.selectedAddress, d.authMessage, passwordInput.text)
return
}
root.controller.sessionRequest(JSON.stringify(d.sessionRequest), passwordInput.text)
}
visible: d.state === d.waitingUserResponseToSessionRequest ||
d.state === d.waitingUserResponseToAuthRequest
}
StatusButton {
text: "Reject"
onClicked: {
if (testAuthentication.checked) {
root.sdk.authReject(d.observedData.id, d.selectedAddress)
return
}
root.sdk.rejectSessionRequest(d.sessionRequest.topic, d.sessionRequest.id, false)
}
visible: d.state === d.waitingUserResponseToSessionRequest ||
d.state === d.waitingUserResponseToAuthRequest
}
StatusInput {
id: passwordInput
text: "1234567890"
placeholderText: "Insert account password"
visible: d.state === d.waitingUserResponseToSessionRequest ||
d.state === d.waitingUserResponseToAuthRequest
}
} }
enabled: pairLinkInput.text.length > 0 && root.sdk.sdkReady
} }
StatusButton { ColumnLayout {
text: "Accept" Layout.fillWidth: true
onClicked: {
root.sdk.approvePairSession(d.observedData, d.supportedNamespaces) Sessions {
Layout.fillWidth: true
Layout.preferredHeight: contentHeight
model: root.sdk.sessionsModel
onDisconnect: function (topic) {
root.sdk.disconnectTopic(topic)
}
onPing: function (topic) {
root.sdk.ping(topic)
}
} }
visible: d.state === d.waitingPairState
} }
StatusButton {
text: "Reject" ColumnLayout {
onClicked: { Layout.fillWidth: true
root.sdk.rejectPairSession(d.observedData.id)
Pairings {
Layout.fillWidth: true
Layout.preferredHeight: contentHeight
model: root.sdk.pairingsModel
onDisconnect: function (topic) {
root.sdk.disconnectTopic(topic)
}
} }
visible: d.state === d.waitingPairState
} }
} }
Item {
Layout.fillWidth: true
Layout.preferredHeight: 32
}
ColumnLayout { ColumnLayout {
StatusBaseText {
text: qsTr("Tracking details...")
font.bold: true
}
StatusBaseText { StatusBaseText {
id: statusText id: statusText
text: "-" text: "-"
} font.bold: true
StatusBaseText {
text: "Pairings"
visible: root.sdk.pairingsModel.count > 0
}
Pairings {
Layout.fillWidth: true
Layout.minimumWidth: count > 0 ? 400 : 0
Layout.preferredHeight: contentHeight
Layout.maximumHeight: 300
model: root.sdk.pairingsModel
onDisconnect: function (topic) {
root.sdk.disconnectPairing(topic)
}
}
ButtonGroup {
id: selAccBtnGroup
}
SelectAccount {
Layout.fillWidth: true
Layout.minimumWidth: count > 0 ? 400 : 0
Layout.preferredHeight: contentHeight
Layout.maximumHeight: 300
model: accountsModel
buttonGroup: selAccBtnGroup
onAccountSelected: {
root.sdk.formatAuthMessage(d.observedData.params.cacaoPayload, address)
}
} }
StatusBaseText { StatusBaseText {
@ -152,49 +246,7 @@ Popup {
color: "#FF00FF" color: "#FF00FF"
} }
RowLayout {
StatusButton {
text: "Accept"
onClicked: {
if (testAuthentication.checked) {
root.controller.authRequest(d.selectedAddress, d.authMessage, passwordInput.text)
return
}
root.controller.sessionRequest(JSON.stringify(d.sessionRequest), passwordInput.text)
}
visible: d.state === d.waitingUserResponseToSessionRequest ||
d.state === d.waitingUserResponseToAuthRequest
}
StatusButton {
text: "Reject"
onClicked: {
if (testAuthentication.checked) {
root.sdk.authReject(d.observedData.id, d.selectedAddress)
return
}
root.sdk.rejectSessionRequest(d.sessionRequest.topic, d.sessionRequest.id, false)
}
visible: d.state === d.waitingUserResponseToSessionRequest ||
d.state === d.waitingUserResponseToAuthRequest
}
StatusInput {
id: passwordInput
text: "1234567890"
placeholderText: "Insert account password"
visible: d.state === d.waitingUserResponseToSessionRequest ||
d.state === d.waitingUserResponseToAuthRequest
}
}
ColumnLayout { /* spacer */ }
} }
// Separator
ColumnLayout {}
} }
ScrollBar.vertical: ScrollBar {} ScrollBar.vertical: ScrollBar {}
@ -216,15 +268,15 @@ Popup {
} }
} }
function onPairSessionProposal(sessionProposal) { function onSessionProposal(sessionProposal) {
d.setDetailsText(sessionProposal) d.setDetailsText(sessionProposal)
d.setStatusText("Pair ID: " + sessionProposal.id + "; Topic: " + sessionProposal.params.pairingTopic) d.setStatusText("Pair ID: " + sessionProposal.id + "; Topic: " + sessionProposal.params.pairingTopic)
root.controller.pairSessionProposal(JSON.stringify(sessionProposal)) root.controller.sessionProposal(JSON.stringify(sessionProposal))
} }
function onPairAcceptedResult(sessionProposal, success, result) { function onApproveSessionResult(sessionProposal, error) {
d.setDetailsText(result) d.setDetailsText("")
if (success) { if (!error) {
d.setStatusText("Pairing OK") d.setStatusText("Pairing OK")
d.state = d.pairedState d.state = d.pairedState
root.controller.recordSuccessfulPairing(JSON.stringify(sessionProposal)) root.controller.recordSuccessfulPairing(JSON.stringify(sessionProposal))
@ -234,10 +286,10 @@ Popup {
} }
} }
function onPairRejectedResult(success, result) { function onRejectSessionResult(error) {
d.setDetailsText(result) d.setDetailsText("")
d.state = d.sdkReadyState d.state = d.sdkReadyState
if (success) { if (!error) {
d.setStatusText("Pairing rejected") d.setStatusText("Pairing rejected")
} else { } else {
d.setStatusText("Rejecting pairing error", "red") d.setStatusText("Rejecting pairing error", "red")
@ -257,12 +309,12 @@ Popup {
} }
} }
function onPairSessionProposalExpired() { function onSessionProposalExpired() {
d.setStatusText(`Timeout waiting for response. Reusing URI?`, "red") d.setStatusText(`Timeout waiting for response. Reusing URI?`, "red")
} }
function onStatusChanged(message) { function onStatusChanged(message) {
statusText.text = message d.setStatusText(message)
} }
function onAuthRequest(request) { function onAuthRequest(request) {
@ -352,6 +404,7 @@ Popup {
} }
statusText.color = textColor statusText.color = textColor
} }
function setDetailsText(message) { function setDetailsText(message) {
if (message === undefined) { if (message === undefined) {
message = "undefined" message = "undefined"
@ -369,7 +422,12 @@ Popup {
Connections { Connections {
target: root.controller target: root.controller
function onProposeUserPair(sessionProposalJson, supportedNamespacesJson) { function onRespondSessionProposal(sessionProposalJson, supportedNamespacesJson, error) {
if (error) {
d.setStatusText(`Error: ${error}`, "red")
d.setDetailsText("")
return
}
d.setStatusText("Waiting user accept") d.setStatusText("Waiting user accept")
d.observedData = JSON.parse(sessionProposalJson) d.observedData = JSON.parse(sessionProposalJson)

View File

@ -8,12 +8,15 @@ import QtWebChannel 1.15
import StatusQ.Core.Utils 0.1 as SQUtils import StatusQ.Core.Utils 0.1 as SQUtils
import StatusQ.Components 0.1 import StatusQ.Components 0.1
import utils 1.0
Item { Item {
id: root id: root
required property string projectId required property string projectId
readonly property alias sdkReady: d.sdkReady readonly property alias sdkReady: d.sdkReady
readonly property alias pairingsModel: d.pairingsModel readonly property alias pairingsModel: d.pairingsModel
readonly property alias sessionsModel: d.sessionsModel
readonly property alias webEngineLoader: loader readonly property alias webEngineLoader: loader
property alias active: loader.active property alias active: loader.active
@ -24,10 +27,10 @@ Item {
signal statusChanged(string message) signal statusChanged(string message)
signal sdkInit(bool success, var result) signal sdkInit(bool success, var result)
signal pairSessionProposal(var sessionProposal) signal sessionProposal(var sessionProposal)
signal pairSessionProposalExpired() signal sessionProposalExpired()
signal pairAcceptedResult(var sessionProposal, bool success, var sessionType) signal approveSessionResult(var sessionProposal, string error)
signal pairRejectedResult(bool success, var result) signal rejectSessionResult(string error)
signal sessionRequestEvent(var sessionRequest) signal sessionRequestEvent(var sessionRequest)
signal sessionRequestUserAnswerResult(bool accept, string error) signal sessionRequestUserAnswerResult(bool accept, string error)
@ -41,16 +44,20 @@ Item {
wcCalls.pair(pairLink) wcCalls.pair(pairLink)
} }
function disconnectPairing(topic) { function disconnectTopic(topic) {
wcCalls.disconnectPairing(topic) wcCalls.disconnectTopic(topic)
} }
function approvePairSession(sessionProposal, supportedNamespaces) { function ping(topic) {
wcCalls.approvePairSession(sessionProposal, supportedNamespaces) wcCalls.ping(topic)
} }
function rejectPairSession(id) { function approveSession(sessionProposal, supportedNamespaces) {
wcCalls.rejectPairSession(id) wcCalls.approveSession(sessionProposal, supportedNamespaces)
}
function rejectSession(id) {
wcCalls.rejectSession(id)
} }
function acceptSessionRequest(topic, id, signature) { function acceptSessionRequest(topic, id, signature) {
@ -82,6 +89,7 @@ Item {
property bool sdkReady: false property bool sdkReady: false
property ListModel pairingsModel: pairings property ListModel pairingsModel: pairings
property ListModel sessionsModel: sessions
property WebEngineView engine: loader.instance property WebEngineView engine: loader.instance
@ -89,6 +97,7 @@ Item {
if (sdkReady) if (sdkReady)
{ {
d.resetPairingsModel() d.resetPairingsModel()
d.resetSessionsModel()
} }
} }
@ -96,18 +105,33 @@ Item {
{ {
pairings.clear(); pairings.clear();
wcCalls.getPairings((pairList) => { // We have to postpone `getPairings` call, cause otherwise:
for (let i = 0; i < pairList.length; i++) { // - the last made pairing will always have `active` prop set to false
pairings.append({ // - expiration date won't be the correct one, but one used in session proposal
active: pairList[i].active, // - the list of pairings will display succesfully made pairing as inactive
topic: pairList[i].topic, Backpressure.debounce(this, 250, () => {
expiry: pairList[i].expiry wcCalls.getPairings((pairList) => {
}); for (let i = 0; i < pairList.length; i++) {
if (entryCallback) { pairings.append(pairList[i]);
entryCallback(pairList[i])
} if (entryCallback) {
} entryCallback(pairList[i])
}) }
}
});
})();
}
function resetSessionsModel() {
sessions.clear();
Backpressure.debounce(this, 250, () => {
wcCalls.getActiveSessions((sessionList) => {
for (var topic of Object.keys(sessionList)) {
sessions.append(sessionList[topic]);
}
});
})();
} }
function getPairingTopicFromPairingUrl(url) function getPairingTopicFromPairingUrl(url)
@ -116,11 +140,13 @@ Item {
{ {
return null; return null;
} }
const atIndex = url.indexOf("@"); const atIndex = url.indexOf("@");
if (atIndex < 0) if (atIndex < 0)
{ {
return null; return null;
} }
return url.slice(3, atIndex); return url.slice(3, atIndex);
} }
} }
@ -147,17 +173,19 @@ Item {
d.engine.runJavaScript(`wc.getPairings()`, function(result) { d.engine.runJavaScript(`wc.getPairings()`, function(result) {
console.debug(`WC WalletConnectSDK.wcCall.getPairings; response: ${JSON.stringify(result, null, 2)}`) if (callback && result) {
callback(result)
}
})
}
if (result) function getActiveSessions(callback) {
{ console.debug(`WC WalletConnectSDK.wcCall.getActiveSessions;`)
if (!!result.error) {
console.error("getPairings: ", result.error)
return
}
callback(result.result) d.engine.runJavaScript(`wc.getActiveSessions()`, function(result) {
return
if (callback && result) {
callback(result)
} }
}) })
} }
@ -165,75 +193,46 @@ Item {
function pair(pairLink) { function pair(pairLink) {
console.debug(`WC WalletConnectSDK.wcCall.pair; pairLink: ${pairLink}`) console.debug(`WC WalletConnectSDK.wcCall.pair; pairLink: ${pairLink}`)
wcCalls.getPairings((allPairings) => { d.engine.runJavaScript(`
wc.pair("${pairLink}")
console.debug(`WC WalletConnectSDK.wcCall.pair; response: ${JSON.stringify(allPairings, null, 2)}`) .then((value) => {
wc.statusObject.onPairResponse("")
let pairingTopic = d.getPairingTopicFromPairingUrl(pairLink);
// Find pairing by topic
const pairing = allPairings.find((p) => p.topic === pairingTopic);
if (pairing)
{
if (pairing.active) {
console.warn("pair: already paired")
return
}
}
d.engine.runJavaScript(`wc.pair("${pairLink}")`, function(result) {
if (result && !!result.error)
{
console.error("pair: ", result.error)
}
}) })
} .catch((e) => {
) wc.statusObject.onPairResponse(e.message)
})
`
)
} }
function approvePairSession(sessionProposal, supportedNamespaces) { function approveSession(sessionProposal, supportedNamespaces) {
console.debug(`WC WalletConnectSDK.wcCall.approvePairSession; sessionProposal: ${JSON.stringify(sessionProposal)}, supportedNamespaces: ${JSON.stringify(supportedNamespaces)}`) console.debug(`WC WalletConnectSDK.wcCall.approveSession; sessionProposal: ${JSON.stringify(sessionProposal)}, supportedNamespaces: ${JSON.stringify(supportedNamespaces)}`)
d.engine.runJavaScript(`wc.approvePairSession(${JSON.stringify(sessionProposal)}, ${JSON.stringify(supportedNamespaces)})`, function(result) { d.engine.runJavaScript(`
wc.approveSession(${JSON.stringify(sessionProposal)}, ${JSON.stringify(supportedNamespaces)})
console.debug(`WC WalletConnectSDK.wcCall.approvePairSession; response: ${JSON.stringify(result, null, 2)}`) .then((value) => {
wc.statusObject.onApproveSessionResponse(${JSON.stringify(sessionProposal)}, "")
if (result) { })
if (!!result.error) .catch((e) => {
{ wc.statusObject.onApproveSessionResponse(${JSON.stringify(sessionProposal)}, e.message)
console.error("approvePairSession: ", result.error) })
root.pairAcceptedResult(sessionProposal, false, result.error) `
return )
}
// Update the temporary expiry with the one from the pairing
d.resetPairingsModel((pairing) => {
if (pairing.topic === sessionProposal.params.pairingTopic) {
sessionProposal.params.expiry = pairing.expiry
root.pairAcceptedResult(sessionProposal, true, result.error)
}
})
}
})
} }
function rejectPairSession(id) { function rejectSession(id) {
console.debug(`WC WalletConnectSDK.wcCall.rejectPairSession; id: ${id}`) console.debug(`WC WalletConnectSDK.wcCall.rejectSession; id: ${id}`)
d.engine.runJavaScript(`wc.rejectPairSession(${id})`, function(result) { d.engine.runJavaScript(`
wc.rejectSession(${id})
console.debug(`WC WalletConnectSDK.wcCall.rejectPairSession; response: ${JSON.stringify(result, null, 2)}`) .then((value) => {
wc.statusObject.onRejectSessionResponse("")
d.resetPairingsModel() })
if (result) { .catch((e) => {
if (!!result.error) wc.statusObject.onRejectSessionResponse(e.message)
{ })
console.error("rejectPairSession: ", result.error) `
root.pairRejectedResult(false, result.error) )
return
}
root.pairRejectedResult(true, result.error)
}
})
} }
function acceptSessionRequest(topic, id, signature) { function acceptSessionRequest(topic, id, signature) {
@ -241,7 +240,7 @@ Item {
d.engine.runJavaScript(`wc.respondSessionRequest("${topic}", ${id}, "${signature}")`, function(result) { d.engine.runJavaScript(`wc.respondSessionRequest("${topic}", ${id}, "${signature}")`, function(result) {
console.debug(`WC WalletConnectSDK.wcCall.acceptSessionRequest; response: ${JSON.stringify(allPairings, null, 2)}`) console.debug(`WC WalletConnectSDK.wcCall.acceptSessionRequest; response: ${JSON.stringify(result, null, 2)}`)
if (result) { if (result) {
if (!!result.error) if (!!result.error)
@ -252,7 +251,9 @@ Item {
} }
root.sessionRequestUserAnswerResult(true, result.error) root.sessionRequestUserAnswerResult(true, result.error)
} }
d.resetPairingsModel() d.resetPairingsModel()
d.resetSessionsModel()
}) })
} }
@ -263,6 +264,9 @@ Item {
console.debug(`WC WalletConnectSDK.wcCall.rejectSessionRequest; response: ${JSON.stringify(result, null, 2)}`) console.debug(`WC WalletConnectSDK.wcCall.rejectSessionRequest; response: ${JSON.stringify(result, null, 2)}`)
d.resetPairingsModel()
d.resetSessionsModel()
if (result) { if (result) {
if (!!result.error) if (!!result.error)
{ {
@ -272,24 +276,37 @@ Item {
} }
root.sessionRequestUserAnswerResult(false, result.error) root.sessionRequestUserAnswerResult(false, result.error)
} }
d.resetPairingsModel()
}) })
} }
function disconnectPairing(topic) { function disconnectTopic(topic) {
console.debug(`WC WalletConnectSDK.wcCall.disconnectPairing; topic: "${topic}"`) console.debug(`WC WalletConnectSDK.wcCall.disconnectTopic; topic: "${topic}"`)
d.engine.runJavaScript(`wc.disconnect("${topic}")`, function(result) { d.engine.runJavaScript(`
console.debug(`WC WalletConnectSDK.wcCall.disconnect; response: ${JSON.stringify(result, null, 2)}`) wc.disconnect("${topic}")
.then((value) => {
wc.statusObject.onDisconnectResponse("")
})
.catch((e) => {
wc.statusObject.onDisconnectResponse(e.message)
})
`
)
}
if (result) { function ping(topic) {
if (!!result.error) { console.debug(`WC WalletConnectSDK.wcCall.ping; topic: "${topic}"`)
console.error("disconnect: ", result.error)
return d.engine.runJavaScript(`
} wc.ping("${topic}")
} .then((value) => {
d.resetPairingsModel() wc.statusObject.onPingResponse("")
}) })
.catch((e) => {
wc.statusObject.onPingResponse(e.message)
})
`
)
} }
function auth(authLink) { function auth(authLink) {
@ -378,68 +395,93 @@ Item {
} }
} }
function sdkInitialized(error) function sdkInitialized(error) {
{ console.debug(`WC WalletConnectSDK.sdkInitialized; error: ${error}`)
d.sdkReady = !error d.sdkReady = !error
root.sdkInit(d.sdkReady, error) root.sdkInit(d.sdkReady, error)
} }
function onSessionProposal(details) function onPairResponse(error) {
{ console.debug(`WC WalletConnectSDK.onPairResponse; error: ${error}`)
console.debug(`WC WalletConnectSDK.onSessionProposal; details: ${JSON.stringify(details, null, 2)}`)
root.pairSessionProposal(details)
} }
function onSessionUpdate(details) function onPingResponse(error) {
{ console.debug(`WC WalletConnectSDK.onPingResponse; error: ${error}`)
}
function onDisconnectResponse(error) {
console.debug(`WC WalletConnectSDK.onDisconnectResponse; error: ${error}`)
d.resetPairingsModel()
d.resetSessionsModel()
}
function onApproveSessionResponse(sessionProposal, error) {
console.debug(`WC WalletConnectSDK.onApproveSessionResponse; sessionProposal: ${JSON.stringify(sessionProposal, null, 2)}, error: ${error}`)
// Update the temporary expiry with the one from the pairing
d.resetPairingsModel((pairing) => {
if (pairing.topic === sessionProposal.params.pairingTopic) {
sessionProposal.params.expiry = pairing.expiry
root.approveSessionResult(sessionProposal, error)
}
})
d.resetSessionsModel()
}
function onRejectSessionResponse(error) {
console.debug(`WC WalletConnectSDK.onRejectSessionResponse; error: ${error}`)
root.rejectSessionResult(error)
d.resetPairingsModel()
d.resetSessionsModel()
}
function onSessionProposal(details) {
console.debug(`WC WalletConnectSDK.onSessionProposal; details: ${JSON.stringify(details, null, 2)}`)
root.sessionProposal(details)
}
function onSessionUpdate(details) {
console.debug(`WC TODO WalletConnectSDK.onSessionUpdate; details: ${JSON.stringify(details, null, 2)}`) console.debug(`WC TODO WalletConnectSDK.onSessionUpdate; details: ${JSON.stringify(details, null, 2)}`)
} }
function onSessionExtend(details) function onSessionExtend(details) {
{
console.debug(`WC TODO WalletConnectSDK.onSessionExtend; details: ${JSON.stringify(details, null, 2)}`) console.debug(`WC TODO WalletConnectSDK.onSessionExtend; details: ${JSON.stringify(details, null, 2)}`)
} }
function onSessionPing(details) function onSessionPing(details) {
{
console.debug(`WC TODO WalletConnectSDK.onSessionPing; details: ${JSON.stringify(details, null, 2)}`) console.debug(`WC TODO WalletConnectSDK.onSessionPing; details: ${JSON.stringify(details, null, 2)}`)
} }
function onSessionDelete(details) function onSessionDelete(details) {
{
console.debug(`WC WalletConnectSDK.onSessionDelete; details: ${JSON.stringify(details, null, 2)}`) console.debug(`WC WalletConnectSDK.onSessionDelete; details: ${JSON.stringify(details, null, 2)}`)
root.sessionDelete(details) root.sessionDelete(details)
d.resetPairingsModel()
d.resetSessionsModel()
} }
function onSessionExpire(details) function onSessionExpire(details) {
{
console.debug(`WC TODO WalletConnectSDK.onSessionExpire; details: ${JSON.stringify(details, null, 2)}`) console.debug(`WC TODO WalletConnectSDK.onSessionExpire; details: ${JSON.stringify(details, null, 2)}`)
} }
function onSessionRequest(details) function onSessionRequest(details) {
{
console.debug(`WC WalletConnectSDK.onSessionRequest; details: ${JSON.stringify(details, null, 2)}`) console.debug(`WC WalletConnectSDK.onSessionRequest; details: ${JSON.stringify(details, null, 2)}`)
root.sessionRequestEvent(details) root.sessionRequestEvent(details)
} }
function onSessionRequestSent(details) function onSessionRequestSent(details) {
{
console.debug(`WC TODO WalletConnectSDK.onSessionRequestSent; details: ${JSON.stringify(details, null, 2)}`) console.debug(`WC TODO WalletConnectSDK.onSessionRequestSent; details: ${JSON.stringify(details, null, 2)}`)
} }
function onSessionEvent(details) function onSessionEvent(details) {
{
console.debug(`WC TODO WalletConnectSDK.onSessionEvent; details: ${JSON.stringify(details, null, 2)}`) console.debug(`WC TODO WalletConnectSDK.onSessionEvent; details: ${JSON.stringify(details, null, 2)}`)
} }
function onProposalExpire(details) function onProposalExpire(details) {
{
console.debug(`WC WalletConnectSDK.onProposalExpire; details: ${JSON.stringify(details, null, 2)}`) console.debug(`WC WalletConnectSDK.onProposalExpire; details: ${JSON.stringify(details, null, 2)}`)
root.pairSessionProposalExpired() root.sessionProposalExpired()
} }
function onAuthRequest(details) function onAuthRequest(details) {
{
console.debug(`WC WalletConnectSDK.onAuthRequest; details: ${JSON.stringify(details, null, 2)}`) console.debug(`WC WalletConnectSDK.onAuthRequest; details: ${JSON.stringify(details, null, 2)}`)
root.authRequest(details) root.authRequest(details)
} }
@ -449,6 +491,10 @@ Item {
id: pairings id: pairings
} }
ListModel {
id: sessions
}
WebEngineLoader { WebEngineLoader {
id: loader id: loader

File diff suppressed because one or more lines are too long

View File

@ -104,51 +104,51 @@ window.wc = {
}, },
// TODO: there is a corner case when attempting to pair with a link that is already paired or was rejected won't trigger any event back // TODO: there is a corner case when attempting to pair with a link that is already paired or was rejected won't trigger any event back
pair: function (uri) { pair: async function (uri) {
return { await window.wc.web3wallet.pair({ uri });
result: window.wc.web3wallet.pair({ uri }),
error: ""
};
}, },
getPairings: function () { getPairings: function () {
return { return window.wc.web3wallet.core.pairing.getPairings();
result: window.wc.core.pairing.getPairings(),
error: ""
};
}, },
disconnect: function (topic) { getActiveSessions: function () {
return { return window.wc.web3wallet.getActiveSessions();
result: window.wc.core.pairing.disconnect({ topic: topic }),
error: ""
};
}, },
approvePairSession: function (sessionProposal, supportedNamespaces) { disconnect: async function (topic) {
await window.wc.web3wallet.disconnectSession({
topic,
reason: getSdkError('USER_DISCONNECTED')
});
},
ping: async function (topic) {
await window.wc.web3wallet.engine.signClient.ping({ topic });
},
approveSession: async function (sessionProposal, supportedNamespaces) {
const { id, params } = sessionProposal; const { id, params } = sessionProposal;
const { relays } = params
const approvedNamespaces = buildApprovedNamespaces({ const approvedNamespaces = buildApprovedNamespaces({
proposal: params, proposal: params,
supportedNamespaces: supportedNamespaces, supportedNamespaces: supportedNamespaces,
}); });
return { await window.wc.web3wallet.approveSession({
result: window.wc.web3wallet.approveSession({
id, id,
relayProtocol: relays[0].protocol,
namespaces: approvedNamespaces, namespaces: approvedNamespaces,
}), });
error: ""
};
}, },
rejectPairSession: function (id) {
return { rejectSession: async function (id) {
result: window.wc.web3wallet.rejectSession({ await window.wc.web3wallet.rejectSession({
id: id, id,
reason: getSdkError("USER_REJECTED"), // TODO USER_REJECTED_METHODS, USER_REJECTED_CHAINS, USER_REJECTED_EVENTS reason: getSdkError("USER_REJECTED"), // TODO USER_REJECTED_METHODS, USER_REJECTED_CHAINS, USER_REJECTED_EVENTS
}), });
error: ""
};
}, },
auth: function (uri) { auth: function (uri) {