DEMO on how would connector quickly integrate by simulating a

wallet-connect session
This commit is contained in:
Stefan 2024-07-10 21:28:25 +03:00
parent 78e605d928
commit 1526afae6f
8 changed files with 333 additions and 7 deletions

View File

@ -19,6 +19,7 @@ import ./activity/details_controller as activity_detailsc
import app/modules/shared_modules/collectible_details/controller as collectible_detailsc
import app/modules/shared_modules/wallet_connect/controller as wc_controller
import app/modules/shared_modules/connector/controller as connector_controller
import app/global/global_singleton
import app/core/eventemitter
@ -39,6 +40,7 @@ import app_service/service/network_connection/service as network_connection_serv
import app_service/service/devices/service as devices_service
import app_service/service/community_tokens/service as community_tokens_service
import app_service/service/wallet_connect/service as wc_service
import app_service/service/connector/service as connector_service
import backend/collectibles as backend_collectibles
@ -80,7 +82,9 @@ type
savedAddressService: saved_address_service.Service
devicesService: devices_service.Service
walletConnectService: wc_service.Service
dappsConnectorService: connector_service.Service
walletConnectController: wc_controller.Controller
dappsConnectorController: connector_controller.Controller
activityController: activityc.Controller
collectibleDetailsController: collectible_detailsc.Controller
@ -171,7 +175,10 @@ proc newModule*(
result.walletConnectService = wc_service.newService(result.events, result.threadpool, settingsService)
result.walletConnectController = wc_controller.newController(result.walletConnectService, walletAccountService)
result.view = newView(result, result.activityController, result.tmpActivityControllers, result.activityDetailsController, result.collectibleDetailsController, result.walletConnectController)
result.dappsConnectorService = connector_service.newService(result.events)
result.dappsConnectorController = connector_controller.newController(result.dappsConnectorService)
result.view = newView(result, result.activityController, result.tmpActivityControllers, result.activityDetailsController, result.collectibleDetailsController, result.walletConnectController, result.dappsConnectorController)
result.viewVariant = newQVariant(result.view)
method delete*(self: Module) =
@ -348,6 +355,7 @@ method load*(self: Module) =
self.sendModule.load()
self.networksModule.load()
self.walletConnectService.init()
self.dappsConnectorService.init()
method isLoaded*(self: Module): bool =
return self.moduleLoaded

View File

@ -6,6 +6,7 @@ import app/modules/shared_modules/collectible_details/controller as collectible_
import ./io_interface
import app/modules/shared_models/currency_amount
import app/modules/shared_modules/wallet_connect/controller as wc_controller
import app/modules/shared_modules/connector/controller as connector_controller
type
ActivityControllerArray* = array[2, activityc.Controller]
@ -26,6 +27,7 @@ QtObject:
isNonArchivalNode: bool
keypairOperabilityForObservedAccount: string
wcController: QVariant
dappsConnectorController: QVariant
walletReady: bool
addressFilters: string
currentCurrency: string
@ -37,6 +39,7 @@ QtObject:
proc delete*(self: View) =
self.wcController.delete
self.dappsConnectorController.delete
self.QObject.delete
@ -45,7 +48,8 @@ QtObject:
tmpActivityControllers: ActivityControllerArray,
activityDetailsController: activity_detailsc.Controller,
collectibleDetailsController: collectible_detailsc.Controller,
wcController: wc_controller.Controller): View =
wcController: wc_controller.Controller,
dappsConnectorController: connector_controller.Controller): View =
new(result, delete)
result.delegate = delegate
result.activityController = activityController
@ -53,6 +57,7 @@ QtObject:
result.activityDetailsController = activityDetailsController
result.collectibleDetailsController = collectibleDetailsController
result.wcController = newQVariant(wcController)
result.dappsConnectorController = newQVariant(dappsConnectorController)
result.setup()
@ -248,6 +253,14 @@ QtObject:
QtProperty[QVariant] walletConnectController:
read = getWalletConnectController
proc getDappsConnectorController(self: View): QVariant {.slot.} =
if self.dappsConnectorController == nil:
return newQVariant()
return self.dappsConnectorController
QtProperty[QVariant] dappsConnectorController:
read = getDappsConnectorController
proc walletReadyChanged*(self: View) {.signal.}
proc getWalletReady*(self: View): bool {.slot.} =

View File

@ -0,0 +1,50 @@
import NimQml
import chronicles
import app_service/service/connector/service as connector_service
logScope:
topics = "connector-controller"
QtObject:
type
Controller* = ref object of QObject
service: connector_service.Service
proc delete*(self: Controller) =
self.QObject.delete
proc dappRequestsToConnect*(self: Controller, payload: string) {.signal.}
proc newController*(service: connector_service.Service): Controller =
new(result, delete)
result.service = service
service.registerEventsHandler(proc (event: connector_service.Event, payload: string) =
# TODO: there is some compilation error here. Need to propagate event to signal
# case event
# of connector_service.Event.DappConnect:
# result.dappRequestsToConnect(payload)
# TODO of SendTransaction:
discard
)
result.QObject.setup
proc approveDappConnectRequest*(self: Controller, accountsJson: string): bool {.slot.} =
return self.service.approveDappConnect(accountsJson)
proc rejectDappConnectRequest*(self: Controller, id: string): bool {.slot.} =
return self.service.rejectDappConnect(id)
proc errorProcessingDappConnectRequest*(self: Controller): bool {.slot.} =
return self.service.errorProcessingDappConnect()
proc getDappsJson*(self: Controller): string {.slot.} =
# TODO: return a json string with the list of dApps to be parsed in ConnectorSDK.getActiveSessions
#return @[{url, name, icon}].toJson
return ""
proc disconnectDapp*(self: Controller, url: string): bool {.slot.} =
# TODO: call service ...
return true

View File

@ -0,0 +1,68 @@
import NimQml, chronicles, json
import backend/connector as status_go
import app/global/global_singleton
import app/core/eventemitter
import app/core/signals/types
logScope:
topics = "connector-service"
# Enum with events
type Event* = enum
DappConnect
# TODO SendTransaction
# Event handler function
type EventHandlerFn* = proc(event: Event, payload: string)
# This can be ditched for now and process everything in the controller;
# However, it would be good to have the DB based calls async and this might be needed
QtObject:
type Service* = ref object of QObject
events: EventEmitter
eventHandler: EventHandlerFn
proc delete*(self: Service) =
self.QObject.delete
proc newService*(
events: EventEmitter
): Service =
new(result, delete)
result.QObject.setup
result.events = events
proc init*(self: Service) =
self.events.on(SignalType.Wallet.event, proc(e: Args) =
if self.eventHandler == nil:
return
var data = WalletSignal(e)
# TODO Uncomment if still considering to use the request ID for request identification
#if not data.requestId.isSome():
# return
case data.eventType:
of status_go.EventConnectorSendRequestAccounts:
# TODO: propagate up to the presentation layer via controller
discard
# TODO of status_go.EventConnectorSendTransaction:
)
proc registerEventsHandler*(self: Service, handler: EventHandlerFn) =
self.eventHandler = handler
proc approveDappConnect*(self: Service, accountsJson: string): bool =
return status_go.requestAccountsFinishedRpc(accountsJson, status_go.RequestAccountNoError)
proc rejectDappConnect*(self: Service, id: string): bool =
# Pass the id to status-go
return status_go.requestAccountsFinishedRpc("{?}", status_go.RequestAccountRejectError)
proc errorProcessingDappConnect*(self: Service): bool =
return status_go.requestAccountsFinishedRpc("[]", status_go.RequestAccountGenericError)

52
src/backend/connector.nim Normal file
View File

@ -0,0 +1,52 @@
import options, logging
import json, json_serialization
import core, response_type
from gen import rpc
const
EventConnectorSendRequestAccounts* = "connector.sendRequestAccounts"
EventConnectorSendTransaction* = "connector.sendTransaction"
# TODO: improve on the way to handle the same errors in status-go
RequestAccountNoError* = ""
RequestAccountRejectError* = "connector.RequestAccountRejectError"
RequestAccountGenericError* = "connector.RequestAccountGenericError"
# type RequestAccountsFinishedArgs struct {
# Accounts []types.Address
# Error *error
# }
# implement above in nim
type RequestAccountsFinishedArgs* = ref object of RootObj
accounts* {.serializedFieldName("accounts").}: seq[string]
error* {.serializedFieldName("error").}: string
# TODO This file is the bridge between nim and RPC calls. We should define all the connector-related RPCs here.
# TODO Adjust the namespace as you see fit.
rpc(requestAccountsFinished, "connector"):
args: RequestAccountsFinishedArgs
proc isSuccessResponse(rpcResponse: RpcResponse[JsonNode]): bool =
return rpcResponse.error.isNil
# accountsJson is a stringified JS array
proc requestAccountsFinishedRpc*(accountsJson: string, error: string): bool =
try:
let args = RequestAccountsFinishedArgs()
if error.len > 0:
args.error = error
else:
let accountsJN = parseJson(accountsJson)
# TODO fill in args.accounts with the parsed accountsJN
let rpcRes = requestAccountsFinished(args)
return isSuccessResponse(rpcRes):
except Exception as e:
error "TODO failed: ", "msg", e.msg
return false
# TODO rendTransactionFinishedRpc
# TODO recallDAppPermissionRpc

View File

@ -0,0 +1,113 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtWebEngine 1.10
import QtWebChannel 1.15
import StatusQ.Core.Utils 0.1 as SQUtils
import StatusQ.Components 0.1
import "types"
// Act as another layer of abstraction to the WalletConnectSDKBase
// Quick hack until the WalletConnectSDKBase could be refactored to a more generic DappProviderBase with API to match
// the UX requirements
WalletConnectSDKBase {
id: root
// Nim connector.controller instance
required property controller
readonly property alias sdkReady: true
readonly property bool active: true
implicitWidth: 1
implicitHeight: 1
Connections {
target: controller
function dappRequestsToConnect(dappInfo): {
root.sessionProposal(JSON.parse(`{
"id": ${dappInfo.id},
"params": {
"id": ${dappInfo.id},
"optionalNamespaces": {},
"proposer": {
"metadata": {
"description": "-",
"icons": [
"${dappInfo.icon}"
],
"name": "${dappInfo.name}",
"url": "${dappInfo.url}"
},
},
"requiredNamespaces": {
"eip155": {
"chains": [
"eip155:${dappInfo.chainId}"
],
"events": [],
"methods": ["eth_sendTransaction"],
}
}
}
}`))
}
}
getActiveSessions: function(callback) {
let sessionTemplate = (dappUrl, dappName, dappIcon) => {
return JSON.parse(`{
"peer": {
"metadata": {
"description": "-",
"icons": [
"${dappIcon}"
],
"name": "${dappName}",
"url": "${dappUrl}"
}
},
"topic": "dappUrl"
}`)
}
let dapps = JSON.parse(controller.getDappsJson())
var sessions = []
for (let i = 0; i < dapps.length; i++) {
let dapp = dapps[i]
let session = sessionTemplate(dapp.url, dapp.name, dapp.icon)
sessions.push(session)
}
callback(sessions.push(session))
}
disconnectSession: function(topic) {
controller.disconnectDapp(topic)
}
approveSession: function(sessionProposal, supportedNamespaces) {
// TODO extract the account from the sessionProposal
controller.approveDappConnectRequest()
}
rejectSession: function(id) {
// TODO: ensure id is propagated
controller.rejectDappConnectRequest(id)
}
acceptSessionRequest: function(topic, id, signature) {
// TODO propagate to controller
}
rejectSessionRequest: function(topic, id, error) {
// TODO propagate to controller
}
// We don't expect requests for these. They are here only to spot errors
pair: function(pairLink) { console.error("ConnectorSDK.pair: not implemented") }
getPairings: function(callback) { console.error("ConnectorSDK.getPairings: not implemented") }
disconnectPairing: function(topic) { console.error("ConnectorSDK.disconnectPairing: not implemented") }
buildApprovedNamespaces: function(params, supportedNamespaces) { console.error("ConnectorSDK.buildApprovedNamespaces: not implemented") }
}

View File

@ -105,6 +105,7 @@ QtObject {
readonly property var tmpActivityController1: walletSectionInst.tmpActivityController1
readonly property var activityDetailsController: walletSectionInst.activityDetailsController
readonly property var walletConnectController: walletSectionInst.walletConnectController
readonly property var dappsConnectorController: walletSectionInst.dappsConnectorController
readonly property bool isAccountTokensReloading: walletSectionInst.isAccountTokensReloading
readonly property double lastReloadTimestamp: walletSectionInst.lastReloadTimestamp

View File

@ -2042,6 +2042,31 @@ Item {
}
}
Component {
id: walletConnectSDK
WalletConnectSDK {
active: WalletStore.RootStore.walletSectionInst.walletReady
projectId: WalletStore.RootStore.appSettings.walletConnectProjectID
}
}
Component {
id: dappsConnector
DappsConnectorSDK {
controller: WalletStore.RootStore.dappsConnectorController
}
}
// Temporary solution to use the dapps connector until a proper abstraction is in place
Loader {
id: sdkLoader
sourceComponent: Global.featureFlags.dappsConnectorEnabled
? dappsConnector
: walletConnectSDK
}
Loader {
id: walletConnectServiceLoader
@ -2050,11 +2075,7 @@ Item {
sourceComponent: WalletConnectService {
id: walletConnectService
wcSDK: WalletConnectSDK {
active: WalletStore.RootStore.walletSectionInst.walletReady
projectId: WalletStore.RootStore.appSettings.walletConnectProjectID
}
wcSDK: sdkLoader.item
store: DAppsStore {
controller: WalletStore.RootStore.walletConnectController
}