diff --git a/src/app/modules/main/wallet_section/module.nim b/src/app/modules/main/wallet_section/module.nim index e1a5625813..a2d6fc1cec 100644 --- a/src/app/modules/main/wallet_section/module.nim +++ b/src/app/modules/main/wallet_section/module.nim @@ -175,7 +175,7 @@ proc newModule*( result.collectibleDetailsController = collectible_detailsc.newController(int32(backend_collectibles.CollectiblesRequestID.WalletAccount), networkService, events) result.filter = initFilter(result.controller) - result.walletConnectService = wc_service.newService(result.events, result.threadpool, settingsService, transactionService) + result.walletConnectService = wc_service.newService(result.events, result.threadpool, settingsService, transactionService, keycardService) result.walletConnectController = wc_controller.newController(result.walletConnectService, walletAccountService) result.dappsConnectorService = connector_service.newService(result.events) diff --git a/src/app/modules/shared_modules/wallet_connect/controller.nim b/src/app/modules/shared_modules/wallet_connect/controller.nim index d21522f55e..bf9169cd77 100644 --- a/src/app/modules/shared_modules/wallet_connect/controller.nim +++ b/src/app/modules/shared_modules/wallet_connect/controller.nim @@ -1,6 +1,8 @@ import NimQml import chronicles, times, json +import app/global/global_singleton +import app_service/common/utils import app_service/service/wallet_connect/service as wallet_connect_service import app_service/service/wallet_account/service as wallet_account_service @@ -9,6 +11,8 @@ import helpers logScope: topics = "wallet-connect-controller" +type + SigningCallbackFn* = proc(topic: string, id: string, keyUid: string, address: string, signature: string) QtObject: type @@ -27,6 +31,10 @@ QtObject: result.QObject.setup + ## signals emitted by this controller + proc userAuthenticationResult*(self: Controller, topic: string, id: string, error: bool, password: string, pin: string, payload: string) {.signal.} + proc signingResultReceived*(self: Controller, topic: string, id: string, data: string) {.signal.} + proc addWalletConnectSession*(self: Controller, session_json: string): bool {.slot.} = return self.service.addSession(session_json) @@ -41,7 +49,7 @@ QtObject: # Emits signal dappsListReceived with the list of dApps proc getDapps*(self: Controller): bool {.slot.} = let res = self.service.getDapps() - if res == "": + if res.len == 0: return false else: self.dappsListReceived(res) @@ -61,32 +69,128 @@ QtObject: self.activeSessionsReceived(resultStr) return true - 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 proc authenticateUser*(self: Controller, topic: string, id: string, address: string, payload: string): bool {.slot.} = - let acc = self.walletAccountService.getAccountByAddress(address) - if acc.keyUid == "": + let keypair = self.walletAccountService.getKeypairByAccountAddress(address) + if keypair.isNil: return false - - return self.service.authenticateUser(acc.keyUid, proc(password: string, pin: string, success: bool) = - self.userAuthenticationResult(topic, id, success, password, pin, payload) + var keyUid = singletonInstance.userProfile.getKeyUid() + if keypair.migratedToKeycard(): + keyUid = keypair.keyUid + return self.service.authenticateUser(keyUid, proc(receivedKeyUid: string, password: string, pin: string) = + if receivedKeyUid.len == 0 or receivedKeyUid != keyUid or password.len == 0: + self.userAuthenticationResult(topic, id, false, "", "", "") + return + self.userAuthenticationResult(topic, id, true, password, pin, payload) ) - proc signMessageUnsafe*(self: Controller, address: string, password: string, message: string): string {.slot.} = - return self.service.signMessageUnsafe(address, password, message) + proc signOnKeycard(self: Controller, address: string): bool = + let keypair = self.walletAccountService.getKeypairByAccountAddress(address) + if keypair.isNil: + raise newException(CatchableError, "cannot resolve keypair for address: " & address) + return keypair.migratedToKeycard() - proc signMessage*(self: Controller, address: string, password: string, message: string): string {.slot.} = - return self.service.signMessage(address, password, message) + proc preparePassword(self: Controller, password: string): string = + if singletonInstance.userProfile.getIsKeycardUser(): + return password + return hashPassword(password) - proc safeSignTypedData*(self: Controller, address: string, password: string, typedDataJson: string, chainId: int, legacy: bool): string {.slot.} = - return self.service.safeSignTypedData(address, password, typedDataJson, chainId, legacy) + proc signMessageWithCallback(self: Controller, topic: string, id: string, address: string, message: string, password: string, + pin: string, callback: SigningCallbackFn) = + var res = "" + try: + if message.len == 0: + raise newException(CatchableError, "message is empty") + if self.signOnKeycard(address): + let acc = self.walletAccountService.getAccountByAddress(address) + if acc.isNil: + raise newException(CatchableError, "cannot resolve account for address: " & address) + if not self.service.runSigningOnKeycard( + acc.keyUid, + acc.path, + singletonInstance.utils.removeHexPrefix(message), + pin, + proc(keyUid: string, signature: string) = + if keyUid.len == 0 or keyUid != acc.keyUid or signature.len == 0: + raise newException(CatchableError, "keycard signing failed") + callback(topic, id, keyUid, address, signature) + ): + raise newException(CatchableError, "runSigningOnKeycard failed") + debug "signMessageWithCallback: signing on keycard started successfully" + return + let finalPassword = self.preparePassword(password) + res = self.service.signMessage(address, finalPassword, message) + except Exception as e: + error "signMessageWithCallback failed: ", msg=e.msg + callback(topic, id, "", address, res) - proc signTransaction*(self: Controller, address: string, chainId: int, password: string, txJson: string): string {.slot.} = - return self.service.signTransaction(address, chainId, password, txJson) + proc signMessage*(self: Controller, topic: string, id: string, address: string, message: string, password: string, pin: string) {.slot.} = + var res = "" + try: + if message.len == 0: + raise newException(CatchableError, "message is empty") + let hashedMessage = self.service.hashMessageEIP191(message) + if hashedMessage.len == 0: + raise newException(CatchableError, "hashMessageEIP191 failed") + self.signMessageWithCallback(topic, id, address, hashedMessage, password, pin, + proc (topic: string, id: string, keyUid: string, address: string, signature: string) = + self.signingResultReceived(topic, id, signature) + ) + except Exception as e: + error "signMessage failed: ", msg=e.msg + self.signingResultReceived(topic, id, res) - proc sendTransaction*(self: Controller, address: string, chainId: int, password: string, txJson: string): string {.slot.} = - return self.service.sendTransaction(address, chainId, password, txJson) + proc signMessageUnsafe*(self: Controller, topic: string, id: string, address: string, message: string, password: string, pin: string) {.slot.} = + self.signMessage(topic, id, address, message, password, pin) + + proc safeSignTypedData*(self: Controller, topic: string, id: string, address: string, typedDataJson: string, chainId: int, legacy: bool, + password: string, pin: string): string {.slot.} = + var res = "" + try: + var dataToSign = "" + if legacy: + dataToSign = self.service.hashTypedData(typedDataJson) + else: + dataToSign = self.service.hashTypedDataV4(typedDataJson) + if dataToSign.len == 0: + raise newException(CatchableError, "hashTypedData failed") + self.signMessageWithCallback(topic, id, address, dataToSign, password, pin, + proc (topic: string, id: string, keyUid: string, address: string, signature: string) = + self.signingResultReceived(topic, id, signature) + ) + except Exception as e: + error "safeSignTypedData failed: ", msg=e.msg + self.signingResultReceived(topic, id, res) + + proc signTransaction*(self: Controller, topic: string, id: string, address: string, chainId: int, txJson: string, password: string, pin: string) {.slot.} = + var res = "" + try: + let (txHash, txData) = self.service.buildTransaction(chainId, txJson) + if txHash.len == 0 or txData.isNil: + raise newException(CatchableError, "building transaction failed") + self.signMessageWithCallback(topic, id, address, txHash, password, pin, + proc (topic: string, id: string, keyUid: string, address: string, signature: string) = + let rawTx = self.service.buildRawTransaction(chainId, $txData, signature) + self.signingResultReceived(topic, id, rawTx) + ) + except Exception as e: + error "signTransaction failed: ", msg=e.msg + self.signingResultReceived(topic, id, res) + + proc sendTransaction*(self: Controller, topic: string, id: string, address: string, chainId: int, txJson: string, password: string, pin: string) {.slot.} = + var res = "" + try: + let (txHash, txData) = self.service.buildTransaction(chainId, txJson) + if txHash.len == 0 or txData.isNil: + raise newException(CatchableError, "building transaction failed") + self.signMessageWithCallback(topic, id, address, txHash, password, pin, + proc (topic: string, id: string, keyUid: string, address: string, signature: string) = + let signedTxHash = self.service.sendTransactionWithSignature(chainId, $txData, signature) + self.signingResultReceived(topic, id, signedTxHash) + ) + except Exception as e: + error "sendTransaction failed: ", msg=e.msg + self.signingResultReceived(topic, id, res) proc getEstimatedTime(self: Controller, chainId: int, maxFeePerGasHex: string): int {.slot.} = return self.service.getEstimatedTime(chainId, maxFeePerGasHex).int diff --git a/src/app_service/service/wallet_connect/service.nim b/src/app_service/service/wallet_connect/service.nim index 15c8a0c404..e8c791b999 100644 --- a/src/app_service/service/wallet_connect/service.nim +++ b/src/app_service/service/wallet_connect/service.nim @@ -1,4 +1,4 @@ -import NimQml, chronicles, times, json +import NimQml, chronicles, times, json, uuids import strutils import backend/wallet_connect as status_go @@ -8,6 +8,7 @@ import app_service/service/settings/service as settings_service import app_service/common/wallet_constants from app_service/service/transaction/dto import PendingTransactionTypeDto import app_service/service/transaction/service as tr +import app_service/service/keycard/service as keycard_service import app/global/global_singleton @@ -25,7 +26,8 @@ logScope: const UNIQUE_WALLET_CONNECT_MODULE_IDENTIFIER* = "WalletSection-WCModule" type - AuthenticationResponseFn* = proc(password: string, pin: string, success: bool) + AuthenticationResponseFn* = proc(keyUid: string, password: string, pin: string) + SignResponseFn* = proc(keyUid: string, signature: string) QtObject: type Service* = ref object of QObject @@ -33,8 +35,11 @@ QtObject: threadpool: ThreadPool settingsService: settings_service.Service transactions: tr.Service + keycardService: keycard_service.Service + connectionKeycardResponse: UUID authenticationCallback: AuthenticationResponseFn + signCallback: SignResponseFn proc delete*(self: Service) = self.QObject.delete @@ -44,6 +49,7 @@ QtObject: threadpool: ThreadPool, settingsService: settings_service.Service, transactions: tr.Service, + keycardService: keycard_service.Service, ): Service = new(result, delete) result.QObject.setup @@ -52,25 +58,19 @@ QtObject: result.threadpool = threadpool result.settingsService = settings_service result.transactions = transactions + result.keycardService = keycardService proc init*(self: Service) = 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(args.password, args.pin, true) + self.authenticationCallback(args.keyUid, args.password, args.pin) proc addSession*(self: Service, session_json: string): bool = # TODO #14588: call it async @@ -114,105 +114,104 @@ QtObject: let testChains = self.settingsService.areTestNetworksEnabled() # TODO #14588: call it async 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 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 - proc signMessageUnsafe*(self: Service, address: string, password: string, message: string): string = - return status_go.signMessageUnsafe(address, password, message) + proc hashMessageEIP191*(self: Service, message: string): string = + let hashRes = hashMessageEIP191("0x" & toHex(message)) + if not hashRes.error.isNil: + error "hashMessageEIP191 failed: ", msg=hashRes.error.message + return "" + return hashRes.result.getStr() - proc signMessage*(self: Service, address: string, password: string, message: string): string = - return status_go.signMessage(address, password, message) + proc signMessage*(self: Service, address: string, hashedPassword: string, hashedMessage: string): string = + var signMsgRes: JsonNode + let err = wallet.signMessage(signMsgRes, + hashedMessage, + address, + hashedPassword) + if err.len > 0: + error "status-go - wallet_signMessage failed", err=err + return signMsgRes.getStr - proc safeSignTypedData*(self: Service, address: string, password: string, typedDataJson: string, chainId: int, legacy: bool): string = - return status_go.safeSignTypedData(address, password, typedDataJson, chainId, legacy) - - proc signTransaction*(self: Service, address: string, chainId: int, password: string, txJson: string): string = + proc buildTransaction*(self: Service, chainId: int, txJson: string): tuple[txToSign: string, txData: JsonNode] = var buildTxResponse: JsonNode var err = wallet.buildTransaction(buildTxResponse, chainId, txJson) if err.len > 0: error "status-go - wallet_buildTransaction failed", err=err - return "" + return if buildTxResponse.isNil or buildTxResponse.kind != JsonNodeKind.JObject or not buildTxResponse.hasKey("txArgs") or not buildTxResponse.hasKey("messageToSign"): error "unexpected wallet_buildTransaction response" - return "" - var txToBeSigned = buildTxResponse["messageToSign"].getStr - if txToBeSigned.len != wallet_constants.TX_HASH_LEN_WITH_PREFIX: + return + result.txToSign = buildTxResponse["messageToSign"].getStr + if result.txToSign.len != wallet_constants.TX_HASH_LEN_WITH_PREFIX: error "unexpected tx hash length" - return "" - - var signMsgRes: JsonNode - err = wallet.signMessage(signMsgRes, - txToBeSigned, - address, - hashPassword(password)) - if err.len > 0: - error "status-go - wallet_signMessage failed", err=err - let signature = singletonInstance.utils.removeHexPrefix(signMsgRes.getStr) + return + result.txData = buildTxResponse["txArgs"] + proc buildRawTransaction*(self: Service, chainId: int, txData: string, signature: string): string = var txResponse: JsonNode - err = wallet.buildRawTransaction(txResponse, chainId, $buildTxResponse["txArgs"], signature) + var err = wallet.buildRawTransaction(txResponse, chainId, txData, signature) if err.len > 0: error "status-go - wallet_buildRawTransaction failed", err=err - return "" + return if txResponse.isNil or txResponse.kind != JsonNodeKind.JObject or not txResponse.hasKey("rawTx"): - error "unexpected buildRawTransaction response" - return "" - + error "unexpected wallet_buildRawTransaction response" + return return txResponse["rawTx"].getStr - proc sendTransaction*(self: Service, address: string, chainId: int, password: string, txJson: string): string = - var buildTxResponse: JsonNode - var err = wallet.buildTransaction(buildTxResponse, chainId, txJson) - if err.len > 0: - error "status-go - wallet_buildTransaction failed", err=err - return "" - if buildTxResponse.isNil or buildTxResponse.kind != JsonNodeKind.JObject or - not buildTxResponse.hasKey("txArgs") or not buildTxResponse.hasKey("messageToSign"): - error "unexpected wallet_buildTransaction response" - return "" - var txToBeSigned = buildTxResponse["messageToSign"].getStr - if txToBeSigned.len != wallet_constants.TX_HASH_LEN_WITH_PREFIX: - error "unexpected tx hash length" - return "" - - var signMsgRes: JsonNode - err = wallet.signMessage(signMsgRes, - txToBeSigned, - address, - hashPassword(password)) - if err.len > 0: - error "status-go - wallet_signMessage failed", err=err - let signature = singletonInstance.utils.removeHexPrefix(signMsgRes.getStr) - + proc sendTransactionWithSignature*(self: Service, chainId: int, txData: string, signature: string): string = var txResponse: JsonNode - err = wallet.sendTransactionWithSignature(txResponse, chainId, - $PendingTransactionTypeDto.WalletConnectTransfer, $buildTxResponse["txArgs"], signature) + let err = wallet.sendTransactionWithSignature(txResponse, + chainId, + $PendingTransactionTypeDto.WalletConnectTransfer, + txData, + singletonInstance.utils.removeHexPrefix(signature)) if err.len > 0: error "status-go - sendTransactionWithSignature failed", err=err return "" if txResponse.isNil or txResponse.kind != JsonNodeKind.JString: error "unexpected sendTransactionWithSignature response" return "" - return txResponse.getStr + proc hashTypedData*(self: Service, data: string): string = + var response: JsonNode + let err = wallet.hashTypedData(response, data) + if err.len > 0: + error "status-go - hashTypedData failed", err=err + return "" + if response.isNil or response.kind != JsonNodeKind.JString: + error "unexpected hashTypedData response" + return "" + return response.getStr + + proc hashTypedDataV4*(self: Service, data: string): string = + var response: JsonNode + let err = wallet.hashTypedDataV4(response, data) + if err.len > 0: + error "status-go - hashTypedDataV4 failed", err=err + return "" + if response.isNil or response.kind != JsonNodeKind.JString: + error "unexpected hashTypedDataV4 response" + return "" + return response.getStr + # empty maxFeePerGasHex will fetch the current chain's maxFeePerGas proc getEstimatedTime*(self: Service, chainId: int, maxFeePerGasHex: string): EstimatedTime = var maxFeePerGas: float64 @@ -237,4 +236,41 @@ QtObject: return self.transactions.getEstimatedTime(chainId, $(maxFeePerGas)) proc getSuggestedFees*(self: Service, chainId: int): SuggestedFeesDto = - return self.transactions.suggestedFees(chainId) \ No newline at end of file + return self.transactions.suggestedFees(chainId) + +proc disconnectKeycardReponseSignal(self: Service) = + self.events.disconnect(self.connectionKeycardResponse) + +proc connectKeycardReponseSignal(self: Service) = + self.connectionKeycardResponse = self.events.onWithUUID(SIGNAL_KEYCARD_RESPONSE) do(e: Args): + let args = KeycardLibArgs(e) + self.disconnectKeycardReponseSignal() + if self.signCallback == nil: + error "unexpected user authenticated event; no callback set" + return + defer: + self.signCallback = nil + let currentFlow = self.keycardService.getCurrentFlow() + if currentFlow != KCSFlowType.Sign: + error "unexpected keycard flow type: ", currentFlow + self.signCallback("", "") + return + let signature = "0x" & + singletonInstance.utils.removeHexPrefix(args.flowEvent.txSignature.r) & + singletonInstance.utils.removeHexPrefix(args.flowEvent.txSignature.s) & + singletonInstance.utils.removeHexPrefix(args.flowEvent.txSignature.v) + self.signCallback(args.flowEvent.keyUid, signature) + +proc cancelCurrentFlow*(self: Service) = + self.keycardService.cancelCurrentFlow() + +proc runSigningOnKeycard*(self: Service, keyUid: string, path: string, hashedMessageToSign: string, pin: string, callback: SignResponseFn): bool = + if pin.len == 0: + return false + if self.signCallback != nil: + return false + self.signCallback = callback + self.cancelCurrentFlow() + self.connectKeycardReponseSignal() + self.keycardService.startSignFlow(path, hashedMessageToSign, pin) + return true \ No newline at end of file diff --git a/src/backend/wallet.nim b/src/backend/wallet.nim index 9be7b556ea..dcc891e039 100644 --- a/src/backend/wallet.nim +++ b/src/backend/wallet.nim @@ -1,7 +1,9 @@ -import json, logging +import json, json_serialization, logging import core, response_type from ./gen import rpc +import status_go + rpc(signMessage, "wallet"): message: string address: string @@ -92,3 +94,21 @@ proc sendTransactionWithSignature*(resultOut: var JsonNode, chainId: int, txType except Exception as e: warn e.msg return e.msg + +proc hashTypedData*(resultOut: var JsonNode, data: string): string = + try: + let rawResponse = status_go.hashTypedData(data) + var response = Json.decode(rawResponse, RpcResponse[JsonNode]) + return prepareResponse(resultOut, response) + except Exception as e: + warn e.msg + return e.msg + +proc hashTypedDataV4*(resultOut: var JsonNode, data: string): string = + try: + let rawResponse = status_go.hashTypedDataV4(data) + var response = Json.decode(rawResponse, RpcResponse[JsonNode]) + return prepareResponse(resultOut, response) + except Exception as e: + warn e.msg + return e.msg \ No newline at end of file diff --git a/src/backend/wallet_connect.nim b/src/backend/wallet_connect.nim index d3abba0844..37cffb3b1b 100644 --- a/src/backend/wallet_connect.nim +++ b/src/backend/wallet_connect.nim @@ -1,15 +1,8 @@ import options, logging import json, json_serialization import core, response_type -import strutils from gen import rpc -import backend/wallet - -import status_go - -import app_service/service/community/dto/sign_params -import app_service/common/utils rpc(addWalletConnectSession, "wallet"): sessionJson: string @@ -23,13 +16,6 @@ rpc(getWalletConnectActiveSessions, "wallet"): rpc(hashMessageEIP191, "wallet"): message: string -rpc(safeSignTypedDataForDApps, "wallet"): - typedJson: string - address: string - password: string - chainId: int - legacy: bool - proc isSuccessResponse(rpcResponse: RpcResponse[JsonNode]): bool = return rpcResponse.error.isNil @@ -53,7 +39,7 @@ proc disconnectSession*(topic: string): bool = proc getActiveSessions*(validAtTimestamp: int64): JsonNode = try: let rpcRes = getWalletConnectActiveSessions(validAtTimestamp) - + if(not isSuccessResponse(rpcRes)): return nil @@ -83,47 +69,4 @@ proc getDapps*(validAtEpoch: int64, testChains: bool): string = return if jsonArray != "null": jsonArray else: "[]" except Exception as e: error "GetWalletConnectDapps failed: ", "msg", e.msg - return "" - -proc signMessageUnsafe*(address: string, password: string, message: string): string = - try: - let signParams = SignParamsDto(address: address, password: hashPassword(password), data: "0x" & toHex(message)) - let paramsStr = $toJson(signParams) - let rpcResRaw = status_go.signMessage(paramsStr) - - let rpcRes = Json.decode(rpcResRaw, RpcResponse[JsonNode]) - if(not rpcRes.error.isNil): - return "" - return rpcRes.result.getStr() - except Exception as e: - error "status_go.signMessage failed: ", "msg", e.msg - return "" - -proc signMessage*(address: string, password: string, message: string): string = - try: - let hashRes = hashMessageEIP191("0x" & toHex(message)) - if not isSuccessResponse(hashRes): - error "wallet_hashMessageEIP191 failed: ", "msg", hashRes.error.message - return "" - - let safeHash = hashRes.result.getStr() - let signRes = wallet.signMessage(safeHash, address, hashPassword(password)) - if not isSuccessResponse(signRes): - error "wallet_signMessage failed: ", "msg", signRes.error.message - return "" - - return signRes.result.getStr() - except Exception as e: - error "signMessageForDApps failed: ", "msg", e.msg - return "" - -proc safeSignTypedData*(address: string, password: string, typedDataJson: string, chainId: int, legacy: bool): string = - try: - let rpcRes = safeSignTypedDataForDApps(typedDataJson, address, hashPassword(password), chainId, legacy) - if not isSuccessResponse(rpcRes): - return "" - - return rpcRes.result.getStr() - except Exception as e: - error (if legacy: "wallet_safeSignTypedDataForDApps" else: "wallet_signTypedDataV4") & " failed: ", "msg", e.msg - return "" + return "" \ No newline at end of file diff --git a/storybook/qmlTests/tests/tst_DAppsWorkflow.qml b/storybook/qmlTests/tests/tst_DAppsWorkflow.qml index f666441f10..c967448b9b 100644 --- a/storybook/qmlTests/tests/tst_DAppsWorkflow.qml +++ b/storybook/qmlTests/tests/tst_DAppsWorkflow.qml @@ -274,7 +274,7 @@ Item { compare(handler.store.authenticateUserCalls.length, 1, "expected a call to store.authenticateUser") let store = handler.store - store.userAuthenticated(td.topic, td.request.id, "password", "") + store.userAuthenticated(td.topic, td.request.id, "hello world", "") compare(store.signMessageCalls.length, 1, "expected a call to store.signMessage") compare(store.signMessageCalls[0].message, td.request.data) } diff --git a/ui/app/AppLayouts/Wallet/services/dapps/DAppsRequestHandler.qml b/ui/app/AppLayouts/Wallet/services/dapps/DAppsRequestHandler.qml index 38ef6e4e87..b8d2230829 100644 --- a/ui/app/AppLayouts/Wallet/services/dapps/DAppsRequestHandler.qml +++ b/ui/app/AppLayouts/Wallet/services/dapps/DAppsRequestHandler.qml @@ -116,6 +116,22 @@ SQUtils.QObject { root.displayToastMessage(qsTr("Failed to authenticate %1 from %2").arg(methodStr).arg(session.peer.metadata.url), true) }) } + + function onSigningResult(topic, id, data) { + let isSuccessful = (data != "") + if (isSuccessful) { + // acceptSessionRequest will trigger an sdk.sessionRequestUserAnswerResult signal + sdk.acceptSessionRequest(topic, id, data) + } else { + console.error("signing error") + var request = requests.findRequest(topic, id) + if (request === null) { + console.error("Error finding event for topic", topic, "id", id) + return + } + root.sessionRequestResult(request, isSuccessful) + } + } } SQUtils.QObject { @@ -334,64 +350,75 @@ SQUtils.QObject { return } - if (password !== "") { - let actionResult = "" - if (request.method === SessionRequest.methods.sign.name) { - actionResult = store.signMessageUnsafe(request.topic, request.id, - request.account.address, password, - SessionRequest.methods.personalSign.getMessageFromData(request.data)) - } else if (request.method === SessionRequest.methods.personalSign.name) { - actionResult = store.signMessage(request.topic, request.id, - request.account.address, password, - SessionRequest.methods.personalSign.getMessageFromData(request.data)) - } else if (request.method === SessionRequest.methods.signTypedData_v4.name || - request.method === SessionRequest.methods.signTypedData.name) - { - let legacy = request.method === SessionRequest.methods.signTypedData.name - actionResult = store.safeSignTypedData(request.topic, request.id, - request.account.address, password, + if (password === "") { + console.error("No password provided to sign message") + return + } + + if (request.method === SessionRequest.methods.sign.name) { + store.signMessageUnsafe(request.topic, + request.id, + request.account.address, + SessionRequest.methods.personalSign.getMessageFromData(request.data), + password, + pin) + } else if (request.method === SessionRequest.methods.personalSign.name) { + store.signMessage(request.topic, + request.id, + request.account.address, + SessionRequest.methods.personalSign.getMessageFromData(request.data), + password, + pin) + } else if (request.method === SessionRequest.methods.signTypedData_v4.name || + request.method === SessionRequest.methods.signTypedData.name) + { + let legacy = request.method === SessionRequest.methods.signTypedData.name + store.safeSignTypedData(request.topic, + request.id, + request.account.address, SessionRequest.methods.signTypedData.getMessageFromData(request.data), - request.network.chainId, legacy) - } else if (d.isTransactionMethod(request.method)) { - let txObj = d.getTxObject(request.method, request.data) - if (!!payload) { - let feesInfoJson = payload - let hexFeesJson = root.store.convertFeesInfoToHex(feesInfoJson) - if (!!hexFeesJson) { - let feesInfo = JSON.parse(hexFeesJson) - if (feesInfo.maxFeePerGas) { - txObj.maxFeePerGas = feesInfo.maxFeePerGas - } - if (feesInfo.maxPriorityFeePerGas) { - txObj.maxPriorityFeePerGas = feesInfo.maxPriorityFeePerGas - } + request.network.chainId, + legacy, + password, + pin) + } else if (d.isTransactionMethod(request.method)) { + let txObj = d.getTxObject(request.method, request.data) + if (!!payload) { + let feesInfoJson = payload + let hexFeesJson = root.store.convertFeesInfoToHex(feesInfoJson) + if (!!hexFeesJson) { + let feesInfo = JSON.parse(hexFeesJson) + if (feesInfo.maxFeePerGas) { + txObj.maxFeePerGas = feesInfo.maxFeePerGas + } + if (feesInfo.maxPriorityFeePerGas) { + txObj.maxPriorityFeePerGas = feesInfo.maxPriorityFeePerGas } - delete txObj.gasLimit - delete txObj.gasPrice - } - // Remove nonce from txObj to be auto-filled by the wallet - delete txObj.nonce - - if (request.method === SessionRequest.methods.signTransaction.name) { - actionResult = store.signTransaction(request.topic, request.id, - request.account.address, request.network.chainId, password, txObj) - } else if (request.method === SessionRequest.methods.sendTransaction.name) { - actionResult = store.sendTransaction(request.topic, request.id, - request.account.address, request.network.chainId, password, txObj) } + delete txObj.gasLimit + delete txObj.gasPrice } + // Remove nonce from txObj to be auto-filled by the wallet + delete txObj.nonce - let isSuccessful = (actionResult != "") - if (isSuccessful) { - // acceptSessionRequest will trigger an sdk.sessionRequestUserAnswerResult signal - sdk.acceptSessionRequest(request.topic, request.id, actionResult) - } else { - root.sessionRequestResult(request, isSuccessful) + if (request.method === SessionRequest.methods.signTransaction.name) { + store.signTransaction(request.topic, + request.id, + request.account.address, + request.network.chainId, + txObj, + password, + pin) + } else if (request.method === SessionRequest.methods.sendTransaction.name) { + store.sendTransaction( + request.topic, + request.id, + request.account.address, + request.network.chainId, + txObj, + password, + pin) } - } else if (pin !== "") { - console.debug("TODO #15097 sign message using keycard: ", request.data) - } else { - console.error("No password or pin provided to sign message") } } diff --git a/ui/app/AppLayouts/Wallet/services/dapps/DappsConnectorSDK.qml b/ui/app/AppLayouts/Wallet/services/dapps/DappsConnectorSDK.qml index f3a334ce1b..9637842835 100644 --- a/ui/app/AppLayouts/Wallet/services/dapps/DappsConnectorSDK.qml +++ b/ui/app/AppLayouts/Wallet/services/dapps/DappsConnectorSDK.qml @@ -214,48 +214,59 @@ WalletConnectSDKBase { return } - if (password !== "") { - var actionResult = "" - if (request.method === SessionRequest.methods.sign.name) { - actionResult = store.signMessageUnsafe(request.topic, request.id, - request.account.address, password, - SessionRequest.methods.personalSign.getMessageFromData(request.data)) - } else if (request.method === SessionRequest.methods.personalSign.name) { - actionResult = store.signMessage(request.topic, request.id, - request.account.address, password, - SessionRequest.methods.personalSign.getMessageFromData(request.data)) - } else if (request.method === SessionRequest.methods.signTypedData_v4.name || - request.method === SessionRequest.methods.signTypedData.name) - { - let legacy = request.method === SessionRequest.methods.signTypedData.name - actionResult = store.safeSignTypedData(request.topic, request.id, - request.account.address, password, + if (password === "") { + console.error("No password provided to sign message") + return + } + + if (request.method === SessionRequest.methods.sign.name) { + store.signMessageUnsafe(request.topic, + request.id, + request.account.address, + SessionRequest.methods.personalSign.getMessageFromData(request.data), + password, + pin) + } else if (request.method === SessionRequest.methods.personalSign.name) { + store.signMessage(request.topic, + request.id, + request.account.address, + SessionRequest.methods.personalSign.getMessageFromData(request.data), + password, + pin) + } else if (request.method === SessionRequest.methods.signTypedData_v4.name || + request.method === SessionRequest.methods.signTypedData.name) + { + let legacy = request.method === SessionRequest.methods.signTypedData.name + store.safeSignTypedData(request.topic, + request.id, + request.account.address, SessionRequest.methods.signTypedData.getMessageFromData(request.data), - request.network.chainId, legacy) - } else if (request.method === SessionRequest.methods.signTransaction.name) { - let txObj = SessionRequest.methods.signTransaction.getTxObjFromData(request.data) - actionResult = store.signTransaction(request.topic, request.id, - request.account.address, request.network.chainId, password, txObj) - } else if (request.method === SessionRequest.methods.sendTransaction.name) { - let txObj = SessionRequest.methods.sendTransaction.getTxObjFromData(request.data) - actionResult = store.sendTransaction(request.topic, request.id, - request.account.address, request.network.chainId, password, txObj) - } - let isSuccessful = (actionResult != "") - if (isSuccessful) { - // acceptSessionRequest will trigger an sdk.sessionRequestUserAnswerResult signal - acceptSessionRequest(request.topic, request.method, request.id, actionResult) - } else { - root.sessionRequestResult(request, isSuccessful) - } - } else if (pin !== "") { - console.debug("TODO #15097 sign message using keycard: ", request.data) - } else { - console.error("No password or pin provided to sign message") + request.network.chainId, + legacy, + password, + pin) + } else if (request.method === SessionRequest.methods.signTransaction.name) { + let txObj = SessionRequest.methods.signTransaction.getTxObjFromData(request.data) + store.signTransaction(request.topic, + request.id, + request.account.address, + request.network.chainId, + txObj, + password, + pin) + } else if (request.method === SessionRequest.methods.sendTransaction.name) { + let txObj = SessionRequest.methods.sendTransaction.getTxObjFromData(request.data) + store.sendTransaction(request.topic, + request.id, + request.account.address, + request.network.chainId, + txObj, + password, + pin) } } - function acceptSessionRequest(topic, method, id, signature) { + function acceptSessionRequest(topic, id, signature) { console.debug(`Connector DappsConnectorSDK.acceptSessionRequest; requestId: ${root.requestId}, signature: "${signature}"`) sessionRequestLoader.active = false @@ -322,6 +333,16 @@ WalletConnectSDKBase { root.displayToastMessage(qsTr("Failed to authenticate %1").arg(session.peer.metadata.url), true) }) } + + function onSigningResult(topic, id, data) { + let isSuccessful = (data != "") + if (isSuccessful) { + // acceptSessionRequest will trigger an sdk.sessionRequestUserAnswerResult signal + d.acceptSessionRequest(topic, id, data) + } else { + console.error("signing error") + } + } } Loader { diff --git a/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectService.qml b/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectService.qml index b9b42c2a76..a1298cfc53 100644 --- a/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectService.qml +++ b/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectService.qml @@ -245,10 +245,6 @@ QObject { readonly property var supportedAccountsModel: SortFilterProxyModel { sourceModel: root.walletRootStore.nonWatchAccounts - filters: ValueFilter { - roleName: "keycardAccount" - value: false - } } property var currentSessionProposal: null diff --git a/ui/imports/shared/stores/DAppsStore.qml b/ui/imports/shared/stores/DAppsStore.qml index af316f2511..0be4933e9c 100644 --- a/ui/imports/shared/stores/DAppsStore.qml +++ b/ui/imports/shared/stores/DAppsStore.qml @@ -12,6 +12,8 @@ QObject { signal userAuthenticated(string topic, string id, string password, string pin, string payload) signal userAuthenticationFailed(string topic, string id) + signal signingResult(string topic, string id, string data) + function addWalletConnectSession(sessionJson) { return controller.addWalletConnectSession(sessionJson) } @@ -31,19 +33,16 @@ QObject { } } - // Returns the hex encoded signature of the message or empty string if error - function signMessageUnsafe(topic, id, address, password, message) { - return controller.signMessageUnsafe(address, password, message) + function signMessageUnsafe(topic, id, address, message, password, pin = "") { + controller.signMessageUnsafe(topic, id, address, message, password, pin) } - // Returns the hex encoded signature of the message or empty string if error - function signMessage(topic, id, address, password, message) { - return controller.signMessage(address, password, message) + function signMessage(topic, id, address, message, password, pin = "") { + controller.signMessage(topic, id, address, message, password, pin) } - // Returns the hex encoded signature of the typedDataJson or empty string if error - function safeSignTypedData(topic, id, address, password, typedDataJson, chainId, legacy) { - return controller.safeSignTypedData(address, password, typedDataJson, chainId, legacy) + function safeSignTypedData(topic, id, address, typedDataJson, chainId, legacy, password, pin = "") { + controller.safeSignTypedData(topic, id, address, typedDataJson, chainId, legacy, password, pin) } // Remove leading zeros from hex number as expected by status-go @@ -91,16 +90,14 @@ QObject { return JSON.parse(controller.getSuggestedFeesJson(chainId)) } - // Returns the hex encoded signature of the transaction or empty string if error function signTransaction(topic, id, address, chainId, password, txObj) { let tx = prepareTxForStatusGo(txObj) - return controller.signTransaction(address, chainId, password, JSON.stringify(tx)) + controller.signTransaction(topic, id, address, chainId, JSON.stringify(tx), password, pin) } - // Returns the hash of the transaction or empty string if error - function sendTransaction(topic, id, address, chainId, password, txObj) { + function sendTransaction(topic, id, address, chainId, txObj, password, pin = "") { let tx = prepareTxForStatusGo(txObj) - return controller.sendTransaction(address, chainId, password, JSON.stringify(tx)) + controller.sendTransaction(topic, id, address, chainId, JSON.stringify(tx), password, pin) } /// \c getDapps triggers an async response to \c dappsListReceived @@ -149,5 +146,9 @@ QObject { root.userAuthenticationFailed(topic, id) } } + + function onSigningResultReceived(topic, id, data) { + root.signingResult(topic, id, data) + } } -} \ No newline at end of file +} diff --git a/vendor/nim-status-go b/vendor/nim-status-go index 5546ffaea2..8923d70ec0 160000 --- a/vendor/nim-status-go +++ b/vendor/nim-status-go @@ -1 +1 @@ -Subproject commit 5546ffaea2b2135e7a3da58845e230d8e26fe709 +Subproject commit 8923d70ec0348c7b238ce6d5d86ebe6682577ba4