feat(dapps) authenticate user for session request
Trigger authentication with identity of the request Track the authentication using the identity and allow only once Add tests for the new functionality Minor improvements around the code Closes #15018
This commit is contained in:
parent
3b2a7b8f08
commit
4d080e12aa
|
@ -14,7 +14,6 @@ import app_service/service/collectible/service as collectible_service
|
|||
import app_service/service/currency/service as currency_service
|
||||
import app_service/service/transaction/service as transaction_service
|
||||
import app_service/service/wallet_account/service as wallet_account_service
|
||||
import app_service/service/wallet_connect/service as wallet_connect_service
|
||||
import app_service/service/bookmarks/service as bookmark_service
|
||||
import app_service/service/dapp_permissions/service as dapp_permissions_service
|
||||
import app_service/service/privacy/service as privacy_service
|
||||
|
@ -81,7 +80,6 @@ type
|
|||
currencyService: currency_service.Service
|
||||
transactionService: transaction_service.Service
|
||||
walletAccountService: wallet_account_service.Service
|
||||
walletConnectService: wallet_connect_service.Service
|
||||
bookmarkService: bookmark_service.Service
|
||||
dappPermissionsService: dapp_permissions_service.Service
|
||||
providerService: provider_service.Service
|
||||
|
@ -192,7 +190,6 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController =
|
|||
statusFoundation.events, statusFoundation.threadpool, result.settingsService, result.accountsService,
|
||||
result.tokenService, result.networkService, result.currencyService
|
||||
)
|
||||
result.walletConnectService = wallet_connect_service.newService(statusFoundation.events, statusFoundation.threadpool, result.settingsService)
|
||||
result.messageService = message_service.newService(
|
||||
statusFoundation.events,
|
||||
statusFoundation.threadpool,
|
||||
|
@ -328,7 +325,6 @@ proc delete*(self: AppController) =
|
|||
self.tokenService.delete
|
||||
self.transactionService.delete
|
||||
self.walletAccountService.delete
|
||||
self.walletConnectService.delete
|
||||
self.aboutService.delete
|
||||
self.networkService.delete
|
||||
self.activityCenterService.delete
|
||||
|
@ -456,7 +452,6 @@ proc load(self: AppController) =
|
|||
self.collectibleService.init()
|
||||
self.currencyService.init()
|
||||
self.walletAccountService.init()
|
||||
self.walletConnectService.init()
|
||||
|
||||
# Apply runtime log level settings
|
||||
if not main_constants.runtimeLogLevelSet():
|
||||
|
|
|
@ -168,7 +168,7 @@ proc newModule*(
|
|||
result.filter = initFilter(result.controller)
|
||||
|
||||
result.walletConnectService = wc_service.newService(result.events, result.threadpool, settingsService)
|
||||
result.walletConnectController = wc_controller.newController(result.walletConnectService)
|
||||
result.walletConnectController = wc_controller.newController(result.walletConnectService, walletAccountService)
|
||||
|
||||
result.view = newView(result, result.activityController, result.tmpActivityControllers, result.activityDetailsController, result.collectibleDetailsController, result.walletConnectController)
|
||||
|
||||
|
@ -341,6 +341,7 @@ method load*(self: Module) =
|
|||
self.overviewModule.load()
|
||||
self.sendModule.load()
|
||||
self.networksModule.load()
|
||||
self.walletConnectService.init()
|
||||
|
||||
method isLoaded*(self: Module): bool =
|
||||
return self.moduleLoaded
|
||||
|
|
|
@ -2,6 +2,7 @@ import NimQml
|
|||
import chronicles
|
||||
|
||||
import app_service/service/wallet_connect/service as wallet_connect_service
|
||||
import app_service/service/wallet_account/service as wallet_account_service
|
||||
|
||||
logScope:
|
||||
topics = "wallet-connect-controller"
|
||||
|
@ -10,14 +11,16 @@ QtObject:
|
|||
type
|
||||
Controller* = ref object of QObject
|
||||
service: wallet_connect_service.Service
|
||||
walletAccountService: wallet_account_service.Service
|
||||
|
||||
proc delete*(self: Controller) =
|
||||
self.QObject.delete
|
||||
|
||||
proc newController*(service: wallet_connect_service.Service): Controller =
|
||||
proc newController*(service: wallet_connect_service.Service, walletAccountService: wallet_account_service.Service): Controller =
|
||||
new(result, delete)
|
||||
|
||||
result.service = service
|
||||
result.walletAccountService = walletAccountService
|
||||
|
||||
result.QObject.setup
|
||||
|
||||
|
@ -34,3 +37,15 @@ QtObject:
|
|||
else:
|
||||
self.dappsListReceived(res)
|
||||
return true
|
||||
|
||||
proc userAuthenticationResult*(self: Controller, topic: string, id: string, error: bool) {.signal.}
|
||||
|
||||
# Beware, it will fail if an authentication is already in progress
|
||||
proc authenticateUser*(self: Controller, topic: string, id: string, address: string): bool {.slot.} =
|
||||
let acc = self.walletAccountService.getAccountByAddress(address)
|
||||
if acc.keyUid == "":
|
||||
return false
|
||||
|
||||
return self.service.authenticateUser(acc.keyUid, proc(success: bool) =
|
||||
self.userAuthenticationResult(topic, id, success)
|
||||
)
|
|
@ -10,17 +10,26 @@ import app/core/eventemitter
|
|||
import app/core/signals/types
|
||||
import app/core/tasks/[threadpool]
|
||||
|
||||
import app/modules/shared_modules/keycard_popup/io_interface as keycard_shared_module
|
||||
|
||||
logScope:
|
||||
topics = "wallet-connect-service"
|
||||
|
||||
# include async_tasks
|
||||
|
||||
const UNIQUE_WALLET_CONNECT_MODULE_IDENTIFIER* = "WalletSection-WCModule"
|
||||
|
||||
type
|
||||
AuthenticationResponseFn* = proc(success: bool)
|
||||
|
||||
QtObject:
|
||||
type Service* = ref object of QObject
|
||||
events: EventEmitter
|
||||
threadpool: ThreadPool
|
||||
settingsService: settings_service.Service
|
||||
|
||||
authenticationCallback: AuthenticationResponseFn
|
||||
|
||||
proc delete*(self: Service) =
|
||||
self.QObject.delete
|
||||
|
||||
|
@ -31,12 +40,29 @@ QtObject:
|
|||
): Service =
|
||||
new(result, delete)
|
||||
result.QObject.setup
|
||||
|
||||
result.events = events
|
||||
result.threadpool = threadpool
|
||||
result.settingsService = settings_service
|
||||
|
||||
proc init*(self: Service) =
|
||||
discard
|
||||
self.events.on(SIGNAL_SHARED_KEYCARD_MODULE_USER_AUTHENTICATED) do(e: Args):
|
||||
let args = SharedKeycarModuleArgs(e)
|
||||
if args.uniqueIdentifier != UNIQUE_WALLET_CONNECT_MODULE_IDENTIFIER:
|
||||
return
|
||||
|
||||
if self.authenticationCallback == nil:
|
||||
error "unexpected user authenticated event; no callback set"
|
||||
return
|
||||
defer:
|
||||
self.authenticationCallback = nil
|
||||
|
||||
if args.password == "" and args.pin == "":
|
||||
info "fail to authenticate user"
|
||||
self.authenticationCallback(false)
|
||||
return
|
||||
|
||||
self.authenticationCallback(true)
|
||||
|
||||
proc addSession*(self: Service, session_json: string): bool =
|
||||
# TODO #14588: call it async
|
||||
|
@ -47,3 +73,16 @@ QtObject:
|
|||
let testChains = self.settingsService.areTestNetworksEnabled()
|
||||
# TODO #14588: call it async
|
||||
return status_go.getDapps(validAtEpoch, testChains)
|
||||
|
||||
# Will fail if another authentication is in progress
|
||||
proc authenticateUser*(self: Service, keyUid: string, callback: AuthenticationResponseFn): bool =
|
||||
if self.authenticationCallback != nil:
|
||||
return false
|
||||
self.authenticationCallback = callback
|
||||
|
||||
let data = SharedKeycarModuleAuthenticationArgs(
|
||||
uniqueIdentifier: UNIQUE_WALLET_CONNECT_MODULE_IDENTIFIER,
|
||||
keyUid: keyUid)
|
||||
|
||||
self.events.emit(SIGNAL_SHARED_KEYCARD_MODULE_AUTHENTICATE_USER, data)
|
||||
return true
|
|
@ -4,6 +4,7 @@ import QtQuick.Layouts 1.15
|
|||
import QtQml 2.15
|
||||
import Qt.labs.settings 1.0
|
||||
import QtTest 1.15
|
||||
import QtQml.Models 2.14
|
||||
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Utils 0.1
|
||||
|
@ -30,7 +31,6 @@ import shared.stores 1.0
|
|||
Item {
|
||||
id: root
|
||||
|
||||
// qml Splitter
|
||||
SplitView {
|
||||
anchors.fill: parent
|
||||
|
||||
|
@ -213,6 +213,42 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
StatusDialog {
|
||||
id: authMockDialog
|
||||
title: "Authenticate user"
|
||||
visible: false
|
||||
|
||||
property string topic: ""
|
||||
property string id: ""
|
||||
|
||||
ColumnLayout {
|
||||
RowLayout {
|
||||
StatusBaseText { text: "Topic" }
|
||||
StatusBaseText { text: authMockDialog.topic }
|
||||
StatusBaseText { text: "ID" }
|
||||
StatusBaseText { text: authMockDialog.id }
|
||||
}
|
||||
}
|
||||
footer: StatusDialogFooter {
|
||||
rightButtons: ObjectModel {
|
||||
StatusButton {
|
||||
text: qsTr("Reject")
|
||||
onClicked: {
|
||||
walletConnectService.store.userAuthenticationFailed(authMockDialog.topic, authMockDialog.id)
|
||||
authMockDialog.close()
|
||||
}
|
||||
}
|
||||
StatusButton {
|
||||
text: qsTr("Authenticate")
|
||||
onClicked: {
|
||||
walletConnectService.store.userAuthenticated(authMockDialog.topic, authMockDialog.id)
|
||||
authMockDialog.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WalletConnectService {
|
||||
id: walletConnectService
|
||||
|
||||
|
@ -224,6 +260,9 @@ Item {
|
|||
|
||||
store: DAppsStore {
|
||||
signal dappsListReceived(string dappsJson)
|
||||
signal userAuthenticated(string topic, string id)
|
||||
signal userAuthenticationFailed(string topic, string id)
|
||||
signal sessionRequestExecuted(var payload, bool success)
|
||||
|
||||
function addWalletConnectSession(sessionJson) {
|
||||
console.info("Persist Session", sessionJson)
|
||||
|
@ -243,6 +282,13 @@ Item {
|
|||
this.dappsListReceived(JSON.stringify(d.persistedDapps))
|
||||
return true
|
||||
}
|
||||
|
||||
function authenticateUser(topic, id, address) {
|
||||
authMockDialog.topic = topic
|
||||
authMockDialog.id = id
|
||||
authMockDialog.open()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
walletStore: WalletStore {
|
||||
|
@ -253,6 +299,8 @@ Item {
|
|||
property var accounts: customAccountsModel.count > 0 ? customAccountsModel : defaultAccountsModel
|
||||
readonly property ListModel ownAccounts: accounts
|
||||
}
|
||||
|
||||
onDisplayToastMessage: (message, error) => console.info("Storybook - toast message: ", message, !!error ? "; error: " + error: "")
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -22,8 +22,8 @@ import utils 1.0
|
|||
Item {
|
||||
id: root
|
||||
|
||||
// width: 600
|
||||
// height: 400
|
||||
width: 600
|
||||
height: 400
|
||||
|
||||
// Component {
|
||||
// id: sdkComponent
|
||||
|
@ -79,6 +79,9 @@ Item {
|
|||
|
||||
// DAppsStore {
|
||||
// signal dappsListReceived(string dappsJson)
|
||||
// signal userAuthenticated(string topic, string id)
|
||||
// signal userAuthenticationFailed(string topic, string id)
|
||||
// signal sessionRequestExecuted(var payload, bool success)
|
||||
|
||||
// // By default, return no dapps in store
|
||||
// function getDapps() {
|
||||
|
@ -90,6 +93,16 @@ Item {
|
|||
// function addWalletConnectSession(sessionJson) {
|
||||
// addWalletConnectSessionCalls.push({sessionJson})
|
||||
// }
|
||||
|
||||
// property var authenticateUserCalls: []
|
||||
// function authenticateUser(topic, id, address) {
|
||||
// authenticateUserCalls.push({topic, id, address})
|
||||
// }
|
||||
|
||||
// property var signMessageCalls: []
|
||||
// function signMessage(message) {
|
||||
// signMessageCalls.push({message})
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
|
@ -120,6 +133,52 @@ Item {
|
|||
// }
|
||||
// }
|
||||
|
||||
// Component {
|
||||
// id: dappsRequestHandlerComponent
|
||||
|
||||
// DAppsRequestHandler {
|
||||
// }
|
||||
// }
|
||||
|
||||
// TestCase {
|
||||
// id: requestHandlerTest
|
||||
// name: "DAppsRequestHandler"
|
||||
|
||||
// property DAppsRequestHandler handler: null
|
||||
|
||||
// SignalSpy {
|
||||
// id: displayToastMessageSpy
|
||||
// target: requestHandlerTest.handler
|
||||
// signalName: "onDisplayToastMessage"
|
||||
// }
|
||||
|
||||
// function init() {
|
||||
// let walletStore = createTemporaryObject(walletStoreComponent, root)
|
||||
// verify(!!walletStore)
|
||||
// let sdk = createTemporaryObject(sdkComponent, root, { projectId: "12ab" })
|
||||
// verify(!!sdk)
|
||||
// let store = createTemporaryObject(dappsStoreComponent, root)
|
||||
// verify(!!store)
|
||||
// handler = createTemporaryObject(dappsRequestHandlerComponent, root, {sdk: sdk, store: store, walletStore: walletStore})
|
||||
// verify(!!handler)
|
||||
// }
|
||||
|
||||
// function cleanup() {
|
||||
// displayToastMessageSpy.clear()
|
||||
// }
|
||||
|
||||
// function test_TestAuthentication() {
|
||||
// let td = mockSessionRequestEvent(this, handler.sdk, handler.walletStore)
|
||||
// handler.authenticate(td.request)
|
||||
// compare(handler.store.authenticateUserCalls.length, 1, "expected a call to store.authenticateUser")
|
||||
|
||||
// let store = handler.store
|
||||
// store.userAuthenticated(td.topic, td.request.id)
|
||||
// compare(store.signMessageCalls.length, 1, "expected a call to store.signMessage")
|
||||
// compare(store.signMessageCalls[0].message, td.request.data)
|
||||
// }
|
||||
// }
|
||||
|
||||
// TestCase {
|
||||
// id: walletConnectServiceTest
|
||||
// name: "WalletConnectService"
|
||||
|
@ -263,6 +322,10 @@ Item {
|
|||
// verify(!!request.data, "expected data to be set")
|
||||
// compare(request.data.message, message, "expected message to be set")
|
||||
// }
|
||||
|
||||
// // TODO #14757: add tests with multiple session requests coming in; validate that authentication is serialized and in order
|
||||
// // function tst_SessionRequestQueueMultiple() {
|
||||
// // }
|
||||
// }
|
||||
|
||||
// Component {
|
||||
|
@ -394,7 +457,7 @@ Item {
|
|||
// waitForRendering(controlUnderTest)
|
||||
|
||||
// compare(dappsListReadySpy.count, 0, "expected NO dappsListReady signal to be emitted")
|
||||
// mouseClick(controlUnderTest, Qt.LeftButton)
|
||||
// mouseClick(controlUnderTest)
|
||||
// waitForRendering(controlUnderTest)
|
||||
// compare(dappsListReadySpy.count, 1, "expected dappsListReady signal to be emitted")
|
||||
|
||||
|
@ -402,7 +465,7 @@ Item {
|
|||
// verify(!!popup)
|
||||
// verify(popup.opened)
|
||||
|
||||
// mouseClick(Overlay.overlay, Qt.LeftButton)
|
||||
// popup.close()
|
||||
// waitForRendering(controlUnderTest)
|
||||
|
||||
// verify(!popup.opened)
|
||||
|
@ -411,7 +474,7 @@ Item {
|
|||
// function test_OpenPairModal() {
|
||||
// waitForRendering(controlUnderTest)
|
||||
|
||||
// mouseClick(controlUnderTest, Qt.LeftButton)
|
||||
// mouseClick(controlUnderTest)
|
||||
// waitForRendering(controlUnderTest)
|
||||
|
||||
// let popup = findChild(controlUnderTest, "dappsPopup")
|
||||
|
@ -422,7 +485,7 @@ Item {
|
|||
// verify(!!connectButton)
|
||||
|
||||
// verify(pairWCReadySpy.count === 0, "expected NO pairWCReady signal to be emitted")
|
||||
// mouseClick(connectButton, Qt.LeftButton)
|
||||
// mouseClick(connectButton)
|
||||
// waitForRendering(controlUnderTest)
|
||||
// verify(pairWCReadySpy.count === 1, "expected pairWCReady signal to be emitted")
|
||||
|
||||
|
@ -437,42 +500,11 @@ Item {
|
|||
// }
|
||||
// }
|
||||
|
||||
// function mockSessionRequestEvent() {
|
||||
// let service = controlUnderTest.wcService
|
||||
// let account = service.walletStore.accounts.get(1)
|
||||
// let network = service.walletStore.flatNetworks.get(1)
|
||||
// let method = "personal_sign"
|
||||
// let message = "hello world"
|
||||
// let params = [Helpers.strToHex(message), account.address]
|
||||
// let topic = "b536a"
|
||||
// let requestEvent = JSON.parse(Testing.formatSessionRequest(network.chainId, method, params, topic))
|
||||
// let request = createTemporaryObject(sessionRequestComponent, root, {
|
||||
// event: requestEvent,
|
||||
// topic,
|
||||
// id: requestEvent.id,
|
||||
// method: Constants.personal_sign,
|
||||
// account,
|
||||
// network,
|
||||
// data: message
|
||||
// })
|
||||
// // All calls to SDK are expected as events to be made by the wallet connect SDK
|
||||
// let sdk = service.wcSDK
|
||||
|
||||
// // Expect to have calls to getActiveSessions from service initialization
|
||||
// let prevRequests = sdk.getActiveSessionsCallbacks.length
|
||||
// sdk.sessionRequestEvent(requestEvent)
|
||||
// // Service will trigger a sessionRequest event following the getActiveSessions call
|
||||
// let callback = sdk.getActiveSessionsCallbacks[prevRequests].callback
|
||||
// let session = JSON.parse(Testing.formatApproveSessionResponse([network.chainId, 7], [account.address]))
|
||||
// callback({"b536a": session})
|
||||
|
||||
// return {sdk, session, account, network, topic, id: request.id}
|
||||
// }
|
||||
|
||||
// function test_OpenDappRequestModal() {
|
||||
// waitForRendering(controlUnderTest)
|
||||
|
||||
// let td = mockSessionRequestEvent()
|
||||
// let service = controlUnderTest.wcService
|
||||
// let td = mockSessionRequestEvent(this, service.wcSDK, service.walletStore)
|
||||
|
||||
// waitForRendering(controlUnderTest)
|
||||
// let popup = findChild(controlUnderTest, "dappsRequestModal")
|
||||
|
@ -494,7 +526,8 @@ Item {
|
|||
// function test_RejectDappRequestModal() {
|
||||
// waitForRendering(controlUnderTest)
|
||||
|
||||
// let td = mockSessionRequestEvent()
|
||||
// let service = controlUnderTest.wcService
|
||||
// let td = mockSessionRequestEvent(this, service.wcSDK, service.walletStore)
|
||||
|
||||
// waitForRendering(controlUnderTest)
|
||||
// let popup = findChild(controlUnderTest, "dappsRequestModal")
|
||||
|
@ -502,11 +535,11 @@ Item {
|
|||
|
||||
// let rejectButton = findChild(popup, "rejectButton")
|
||||
|
||||
// mouseClick(rejectButton, Qt.LeftButton)
|
||||
// mouseClick(rejectButton)
|
||||
// compare(td.sdk.rejectSessionRequestCalls.length, 1, "expected a call to service.rejectSessionRequest")
|
||||
// let args = td.sdk.rejectSessionRequestCalls[0]
|
||||
// compare(args.topic, td.topic, "expected topic to be set")
|
||||
// compare(args.id, td.id, "expected id to be set")
|
||||
// compare(args.id, td.request.id, "expected id to be set")
|
||||
// compare(args.error, false, "expected no error; it was user rejected")
|
||||
|
||||
// waitForRendering(controlUnderTest)
|
||||
|
@ -514,4 +547,32 @@ Item {
|
|||
// verify(!popup.visible)
|
||||
// }
|
||||
// }
|
||||
|
||||
// function mockSessionRequestEvent(tc, sdk, walletStore) {
|
||||
// let account = walletStore.accounts.get(1)
|
||||
// let network = walletStore.flatNetworks.get(1)
|
||||
// let method = "personal_sign"
|
||||
// let message = "hello world"
|
||||
// let params = [Helpers.strToHex(message), account.address]
|
||||
// let topic = "b536a"
|
||||
// let requestEvent = JSON.parse(Testing.formatSessionRequest(network.chainId, method, params, topic))
|
||||
// let request = tc.createTemporaryObject(sessionRequestComponent, root, {
|
||||
// event: requestEvent,
|
||||
// topic,
|
||||
// id: requestEvent.id,
|
||||
// method: Constants.personal_sign,
|
||||
// account,
|
||||
// network,
|
||||
// data: message
|
||||
// })
|
||||
// // Expect to have calls to getActiveSessions from service initialization
|
||||
// let prevRequests = sdk.getActiveSessionsCallbacks.length
|
||||
// sdk.sessionRequestEvent(requestEvent)
|
||||
// // Service might trigger a sessionRequest event following the getActiveSessions call
|
||||
// let callback = sdk.getActiveSessionsCallbacks[prevRequests].callback
|
||||
// let session = JSON.parse(Testing.formatApproveSessionResponse([network.chainId, 7], [account.address]))
|
||||
// callback({"b536a": session})
|
||||
|
||||
// return {sdk, session, account, network, topic, request}
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -61,12 +61,13 @@ ConnectedDappsButton {
|
|||
sourceComponent: DAppsListPopup {
|
||||
visible: true
|
||||
|
||||
model: wcService.dappsModel
|
||||
model: root.wcService.dappsModel
|
||||
|
||||
onPairWCDapp: {
|
||||
pairWCLoader.active = true
|
||||
this.close()
|
||||
}
|
||||
|
||||
onOpened: {
|
||||
this.x = root.width - this.contentWidth - 2 * this.padding
|
||||
this.y = root.height + 4
|
||||
|
@ -91,8 +92,8 @@ ConnectedDappsButton {
|
|||
visible: true
|
||||
|
||||
onClosed: connectDappLoader.active = false
|
||||
accounts: wcService.validAccounts
|
||||
flatNetworks: wcService.flatNetworks
|
||||
accounts: root.wcService.validAccounts
|
||||
flatNetworks: root.wcService.flatNetworks
|
||||
|
||||
onConnect: {
|
||||
root.wcService.approvePairSession(sessionProposal, dappChains, selectedAccount)
|
||||
|
@ -136,17 +137,30 @@ ConnectedDappsButton {
|
|||
onClosed: sessionRequestLoader.active = false
|
||||
|
||||
onSign: {
|
||||
console.debug("@dd TODO sign session request")
|
||||
if (!request) {
|
||||
console.error("Error signing: request is null")
|
||||
return
|
||||
}
|
||||
root.wcService.requestHandler.authenticate(request)
|
||||
}
|
||||
|
||||
onReject: {
|
||||
let userRejected = true
|
||||
wcService.requestHandler.rejectSessionRequest(request, userRejected)
|
||||
root.wcService.requestHandler.rejectSessionRequest(request, userRejected)
|
||||
close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.wcService ? root.wcService.requestHandler : null
|
||||
|
||||
function onSessionRequestResult(payload, isSuccess) {
|
||||
// TODO #14927 handle this properly
|
||||
sessionRequestLoader.active = false
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.wcService
|
||||
|
||||
|
|
|
@ -23,16 +23,32 @@ QObject {
|
|||
sdk.rejectSessionRequest(request.topic, request.id, error)
|
||||
}
|
||||
|
||||
/// Beware, it will fail if called multiple times before getting an answer
|
||||
function authenticate(request) {
|
||||
return store.authenticateUser(request.topic, request.id, request.account.address)
|
||||
}
|
||||
|
||||
signal sessionRequest(SessionRequestResolved request)
|
||||
signal displayToastMessage(string message, bool error)
|
||||
signal sessionRequestResult(var payload, bool isSuccess)
|
||||
|
||||
/// Supported methods
|
||||
property QtObject methods: QtObject {
|
||||
readonly property string personalSign: Constants.personal_sign
|
||||
readonly property string sendTransaction: "eth_sendTransaction"
|
||||
readonly property QtObject personalSign: QtObject {
|
||||
readonly property string name: Constants.personal_sign
|
||||
readonly property string userString: qsTr("sign")
|
||||
}
|
||||
readonly property QtObject sendTransaction: QtObject {
|
||||
readonly property string name: "eth_sendTransaction"
|
||||
readonly property string userString: qsTr("send transaction")
|
||||
}
|
||||
readonly property var all: [personalSign, sendTransaction]
|
||||
}
|
||||
|
||||
function getSupportedMethods() {
|
||||
return [root.methods.personalSign, root.methods.sendTransaction]
|
||||
return methods.all.map(function(method) {
|
||||
return method.name
|
||||
})
|
||||
}
|
||||
|
||||
Connections {
|
||||
|
@ -47,6 +63,65 @@ QObject {
|
|||
}
|
||||
requests.enqueue(obj)
|
||||
}
|
||||
|
||||
function onSessionRequestUserAnswerResult(topic, id, accept, error) {
|
||||
var request = requests.findRequest(topic, id)
|
||||
if (request === null) {
|
||||
console.error("Error finding event for topic", topic, "id", id)
|
||||
return
|
||||
}
|
||||
let methodStr = d.methodToUserString(request.method)
|
||||
if (!methodStr) {
|
||||
console.error("Error finding user string for method", request.method)
|
||||
return
|
||||
}
|
||||
|
||||
d.lookupSession(topic, function(session) {
|
||||
if (session === null)
|
||||
return
|
||||
if (error) {
|
||||
root.displayToastMessage(qsTr("Fail to %1 from %2").arg(methodStr).arg(session.peer.metadata.url), true)
|
||||
// TODO #14757 handle SDK error on user accept/reject
|
||||
console.error(`Error accepting session request for topic: ${topic}, id: ${id}, accept: ${accept}, error: ${error}`)
|
||||
return
|
||||
}
|
||||
|
||||
let actionStr = accept ? qsTr("accepted") : qsTr("rejected")
|
||||
root.displayToastMessage("%1 %2 %3".arg(session.peer.metadata.url).arg(methodStr).arg(actionStr), false)
|
||||
root.sessionRequestApprovalResult()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.store
|
||||
|
||||
function onUserAuthenticated(topic, id) {
|
||||
var request = requests.findRequest(topic, id)
|
||||
if (request === null) {
|
||||
console.error("Error finding event for topic", topic, "id", id)
|
||||
return
|
||||
}
|
||||
d.executeSessionRequest(request)
|
||||
}
|
||||
|
||||
function onUserAuthenticationFailed(topic, id) {
|
||||
var request = requests.findRequest(topic, id)
|
||||
let methodStr = d.methodToUserString(request.method)
|
||||
if (request === null || !methodStr) {
|
||||
return
|
||||
}
|
||||
d.lookupSession(topic, function(session) {
|
||||
if (session === null)
|
||||
return
|
||||
root.displayToastMessage(qsTr("Failed to authenticate %1 from %2").arg(methodStr).arg(session.peer.metadata.url), true)
|
||||
})
|
||||
}
|
||||
|
||||
function onSessionRequestExecuted(payload, isSuccess) {
|
||||
// TODO #14927 handle this properly
|
||||
root.sessionRequestResult(payload, isSuccess)
|
||||
}
|
||||
}
|
||||
|
||||
QObject {
|
||||
|
@ -74,19 +149,18 @@ QObject {
|
|||
// Check later to have a valid request object
|
||||
if (!getSupportedMethods().includes(method)
|
||||
// TODO #14927: support method eth_sendTransaction
|
||||
|| method == "eth_sendTransaction") {
|
||||
|| method == root.methods.sendTransaction.name) {
|
||||
console.error("Unsupported method", method)
|
||||
return null
|
||||
}
|
||||
|
||||
sdk.getActiveSessions((res) => {
|
||||
Object.keys(res).forEach((topic) => {
|
||||
if (topic === obj.topic) {
|
||||
let session = res[topic]
|
||||
d.lookupSession(obj.topic, function(session) {
|
||||
if (session === null) {
|
||||
console.error("DAppsRequestHandler.lookupSession: error finding session for topic", obj.topic)
|
||||
return
|
||||
}
|
||||
obj.resolveDappInfoFromSession(session)
|
||||
root.sessionRequest(obj)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
return obj
|
||||
|
@ -94,7 +168,7 @@ QObject {
|
|||
|
||||
/// Returns null if the account is not found
|
||||
function lookupAccountFromEvent(event, method) {
|
||||
if (method === root.methods.personalSign) {
|
||||
if (method === root.methods.personalSign.name) {
|
||||
if (event.params.request.params.length < 2) {
|
||||
return null
|
||||
}
|
||||
|
@ -111,7 +185,7 @@ QObject {
|
|||
|
||||
/// Returns null if the network is not found
|
||||
function lookupNetworkFromEvent(event, method) {
|
||||
if (method === root.methods.personalSign) {
|
||||
if (method === root.methods.personalSign.name) {
|
||||
let chainId = Helpers.chainIdFromEip155(event.params.chainId)
|
||||
for (let i = 0; i < walletStore.flatNetworks.count; i++) {
|
||||
let network = ModelUtils.get(walletStore.flatNetworks, i)
|
||||
|
@ -124,7 +198,7 @@ QObject {
|
|||
}
|
||||
|
||||
function extractMethodData(event, method) {
|
||||
if (method === root.methods.personalSign) {
|
||||
if (method === root.methods.personalSign.name) {
|
||||
if (event.params.request.params.length == 0) {
|
||||
return null
|
||||
}
|
||||
|
@ -134,6 +208,35 @@ QObject {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
function methodToUserString(method) {
|
||||
for (let i = 0; i < methods.all.length; i++) {
|
||||
if (methods.all[i].name === method) {
|
||||
return methods.all[i].userString
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
function lookupSession(topicToLookup, callback) {
|
||||
sdk.getActiveSessions((res) => {
|
||||
Object.keys(res).forEach((topic) => {
|
||||
if (topic === topicToLookup) {
|
||||
let session = res[topic]
|
||||
callback(session)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function executeSessionRequest(request) {
|
||||
if (request.method === root.methods.personalSign.name) {
|
||||
store.signMessage(request.data.message)
|
||||
console.debug("TODO #14927 sign message: ", request.data.message)
|
||||
} else {
|
||||
console.error("Unsupported method to execute: ", request.method)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The queue is used to ensure that the events are processed in the order they are received but they could be
|
||||
|
|
|
@ -206,10 +206,10 @@ WalletConnectSDKBase {
|
|||
d.engine.runJavaScript(`
|
||||
wc.respondSessionRequest("${topic}", ${id}, "${signature}")
|
||||
.then((value) => {
|
||||
wc.statusObject.onRespondSessionRequestResponse("")
|
||||
wc.statusObject.onAcceptSessionRequestResponse("${topic}", ${id}, "")
|
||||
})
|
||||
.catch((e) => {
|
||||
wc.statusObject.onRespondSessionRequestResponse(e.message)
|
||||
wc.statusObject.onAcceptSessionRequestResponse("${topic}", ${id}, e.message)
|
||||
})
|
||||
`
|
||||
)
|
||||
|
@ -221,10 +221,10 @@ WalletConnectSDKBase {
|
|||
d.engine.runJavaScript(`
|
||||
wc.rejectSessionRequest("${topic}", ${id}, "${error}")
|
||||
.then((value) => {
|
||||
wc.statusObject.onRejectSessionRequestResponse("")
|
||||
wc.statusObject.onRejectSessionRequestResponse("${topic}", ${id}, "")
|
||||
})
|
||||
.catch((e) => {
|
||||
wc.statusObject.onRejectSessionRequestResponse(e.message)
|
||||
wc.statusObject.onRejectSessionRequestResponse("${topic}", ${id}, e.message)
|
||||
})
|
||||
`
|
||||
)
|
||||
|
@ -387,14 +387,16 @@ WalletConnectSDKBase {
|
|||
root.rejectSessionResult(error)
|
||||
}
|
||||
|
||||
function onRespondSessionRequestResponse(error) {
|
||||
console.debug(`WC WalletConnectSDK.onRespondSessionRequestResponse; error: ${error}`)
|
||||
root.sessionRequestUserAnswerResult(true, error)
|
||||
function onAcceptSessionRequestResponse(topic, id, error) {
|
||||
console.debug(`WC WalletConnectSDK.onAcceptSessionRequestResponse; topic: ${topic}, id: ${id} error: ${error}`)
|
||||
let responseToAccept = true
|
||||
root.sessionRequestUserAnswerResult(topic, id, responseToAccept, error)
|
||||
}
|
||||
|
||||
function onRejectSessionRequestResponse(error) {
|
||||
console.debug(`WC WalletConnectSDK.onRejectSessionRequestResponse; error: ${error}`)
|
||||
root.sessionRequestUserAnswerResult(false, error)
|
||||
function onRejectSessionRequestResponse(topic, id, error) {
|
||||
console.debug(`WC WalletConnectSDK.onRejectSessionRequestResponse; topic: ${topic}, id: ${id}, error: ${error}`)
|
||||
let responseToAccept = false
|
||||
root.sessionRequestUserAnswerResult(topic, id, responseToAccept, error)
|
||||
}
|
||||
|
||||
function onSessionProposal(details) {
|
||||
|
|
|
@ -13,7 +13,7 @@ Item {
|
|||
signal approveSessionResult(var approvedNamespaces, string error)
|
||||
signal rejectSessionResult(string error)
|
||||
signal sessionRequestEvent(var sessionRequest)
|
||||
signal sessionRequestUserAnswerResult(bool accept, string error)
|
||||
signal sessionRequestUserAnswerResult(string topic, string id, bool accept /* not reject */, string error)
|
||||
|
||||
signal authRequest(var request)
|
||||
signal authMessageFormated(string formatedMessage, string address)
|
||||
|
|
|
@ -164,6 +164,9 @@ QObject {
|
|||
onSessionRequest: (request) => {
|
||||
root.sessionRequest(request)
|
||||
}
|
||||
onDisplayToastMessage: (message, error) => {
|
||||
root.displayToastMessage(message, error)
|
||||
}
|
||||
}
|
||||
|
||||
DAppsListProvider {
|
||||
|
|
|
@ -4,8 +4,8 @@ import QtQuick 2.15
|
|||
ListModel {
|
||||
id: root
|
||||
|
||||
function enqueue(event) {
|
||||
root.append(event);
|
||||
function enqueue(request) {
|
||||
root.append(request);
|
||||
}
|
||||
|
||||
function dequeue() {
|
||||
|
@ -16,4 +16,15 @@ ListModel {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// returns null if not found
|
||||
function findRequest(topic, id) {
|
||||
for (var i = 0; i < root.count; i++) {
|
||||
let entry = root.get(i)
|
||||
if (entry.topic === topic && entry.id === id) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2052,6 +2052,11 @@ Item {
|
|||
Component.onCompleted: {
|
||||
Global.walletConnectService = walletConnectService
|
||||
}
|
||||
|
||||
onDisplayToastMessage: (message, isErr) => {
|
||||
Global.displayToastMessage(message, "", isErr ? "warning" : "checkmark-circle", false,
|
||||
isErr ? Constants.ephemeralNotificationType.danger : Constants.ephemeralNotificationType.success, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -196,11 +196,14 @@ StatusDialog {
|
|||
id: footer
|
||||
|
||||
leftButtons: ObjectModel {
|
||||
MaxFeesDisplay {}
|
||||
MaxFeesDisplay {
|
||||
}
|
||||
Item {
|
||||
width: 20
|
||||
}
|
||||
EstimatedTimeDisplay {}
|
||||
EstimatedTimeDisplay {
|
||||
visible: !!root.estimatedTimeText
|
||||
}
|
||||
}
|
||||
|
||||
rightButtons: ObjectModel {
|
||||
|
@ -218,8 +221,6 @@ StatusDialog {
|
|||
height: 44
|
||||
text: qsTr("Sign")
|
||||
|
||||
enabled: false
|
||||
|
||||
onClicked: {
|
||||
root.sign()
|
||||
}
|
||||
|
@ -228,21 +229,32 @@ StatusDialog {
|
|||
}
|
||||
|
||||
component MaxFeesDisplay: ColumnLayout {
|
||||
visible: root.maxFeesText
|
||||
StatusBaseText {
|
||||
text: qsTr("Max fees:")
|
||||
|
||||
font.pixelSize: 12
|
||||
color: Theme.palette.directColor1
|
||||
}
|
||||
StatusBaseText {
|
||||
id: maxFeesDisplay
|
||||
text: root.maxFeesText
|
||||
|
||||
visible: !!root.maxFeesText
|
||||
|
||||
font.pixelSize: 16
|
||||
font.weight: Font.DemiBold
|
||||
}
|
||||
StatusBaseText {
|
||||
text: qsTr("No fees")
|
||||
|
||||
visible: !maxFeesDisplay.visible
|
||||
|
||||
font.pixelSize: maxFeesDisplay.font.pixelSize
|
||||
font.weight: maxFeesDisplay.font.weight
|
||||
}
|
||||
}
|
||||
|
||||
component EstimatedTimeDisplay: ColumnLayout {
|
||||
visible: root.estimatedTimeText
|
||||
StatusBaseText {
|
||||
text: qsTr("Est. time:")
|
||||
font.pixelSize: 12
|
||||
|
|
|
@ -9,11 +9,26 @@ QObject {
|
|||
|
||||
/// \c dappsJson serialized from status-go.wallet.GetDapps
|
||||
signal dappsListReceived(string dappsJson)
|
||||
signal userAuthenticated(string topic, string id)
|
||||
signal userAuthenticationFailed(string topic, string id)
|
||||
signal sessionRequestExecuted(var payload, bool success)
|
||||
|
||||
function addWalletConnectSession(sessionJson) {
|
||||
controller.addWalletConnectSession(sessionJson)
|
||||
}
|
||||
|
||||
function authenticateUser(topic, id, address) {
|
||||
let ok = controller.authenticateUser(topic, id, address)
|
||||
if(!ok) {
|
||||
root.userAuthenticationFailed()
|
||||
}
|
||||
}
|
||||
|
||||
function signMessage(message) {
|
||||
// TODO #14927 implement me
|
||||
root.sessionRequestExecuted(message, true)
|
||||
}
|
||||
|
||||
/// \c getDapps triggers an async response to \c dappsListReceived
|
||||
function getDapps() {
|
||||
return controller.getDapps()
|
||||
|
@ -26,5 +41,13 @@ QObject {
|
|||
function onDappsListReceived(dappsJson) {
|
||||
root.dappsListReceived(dappsJson)
|
||||
}
|
||||
|
||||
function onUserAuthenticationResult(topic, id, success) {
|
||||
if (success) {
|
||||
root.userAuthenticated(topic, id)
|
||||
} else {
|
||||
root.userAuthenticationFailed(topic, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue