feat(BC): Implement eth_signTypedData_v4 in BrowserConnect

This commit renames the personalSign to Sign in nim and status-go to enable `eth_signTypedData_v4` signing.
The sing request coming from the status-go API contains the signing method to be used by the client. Currently we're supporting personal sign and signTypedData_v4.

The only difference between these two signing methods is the order of challenge and address in the `params` array. This is handled in the SappsConnectorSDK::buildSignRequest
This commit is contained in:
Alex Jbanca 2024-11-19 14:14:59 +02:00
parent ca0456ecae
commit 1449306160
No known key found for this signature in database
GPG Key ID: 6004079575C21C5D
10 changed files with 89 additions and 74 deletions

View File

@ -29,13 +29,14 @@ type ConnectorRevokeDAppPermissionSignal* = ref object of Signal
name*: string
iconUrl*: string
type ConnectorPersonalSignSignal* = ref object of Signal
type ConnectorSignSignal* = ref object of Signal
url*: string
name*: string
iconUrl*: string
requestId*: string
challenge*: string
address*: string
signMethod*: string
proc fromEvent*(T: type ConnectorSendRequestAccountsSignal, event: JsonNode): ConnectorSendRequestAccountsSignal =
result = ConnectorSendRequestAccountsSignal()
@ -67,11 +68,12 @@ proc fromEvent*(T: type ConnectorRevokeDAppPermissionSignal, event: JsonNode): C
result.name = event["event"]{"name"}.getStr()
result.iconUrl = event["event"]{"iconUrl"}.getStr()
proc fromEvent*(T: type ConnectorPersonalSignSignal, event: JsonNode): ConnectorPersonalSignSignal =
result = ConnectorPersonalSignSignal()
proc fromEvent*(T: type ConnectorSignSignal, event: JsonNode): ConnectorSignSignal =
result = ConnectorSignSignal()
result.url = event["event"]{"url"}.getStr()
result.name = event["event"]{"name"}.getStr()
result.iconUrl = event["event"]{"iconUrl"}.getStr()
result.requestId = event["event"]{"requestId"}.getStr()
result.challenge = event["event"]{"challenge"}.getStr()
result.address = event["event"]{"address"}.getStr()
result.signMethod = event["event"]{"method"}.getStr()

View File

@ -74,7 +74,7 @@ type SignalType* {.pure.} = enum
ConnectorSendTransaction = "connector.sendTransaction"
ConnectorGrantDAppPermission = "connector.dAppPermissionGranted"
ConnectorRevokeDAppPermission = "connector.dAppPermissionRevoked"
ConnectorPersonalSign = "connector.personalSign"
ConnectorSign = "connector.Sign"
Unknown
proc event*(self:SignalType):string =

View File

@ -145,7 +145,7 @@ QtObject:
of SignalType.ConnectorSendTransaction: ConnectorSendTransactionSignal.fromEvent(jsonSignal)
of SignalType.ConnectorGrantDAppPermission: ConnectorGrantDAppPermissionSignal.fromEvent(jsonSignal)
of SignalType.ConnectorRevokeDAppPermission: ConnectorRevokeDAppPermissionSignal.fromEvent(jsonSignal)
of SignalType.ConnectorPersonalSign: ConnectorPersonalSignSignal.fromEvent(jsonSignal)
of SignalType.ConnectorSign: ConnectorSignSignal.fromEvent(jsonSignal)
else: Signal()
result.signalType = signalType

View File

@ -13,7 +13,7 @@ const SIGNAL_CONNECTOR_SEND_REQUEST_ACCOUNTS* = "ConnectorSendRequestAccounts"
const SIGNAL_CONNECTOR_EVENT_CONNECTOR_SEND_TRANSACTION* = "ConnectorSendTransaction"
const SIGNAL_CONNECTOR_GRANT_DAPP_PERMISSION* = "ConnectorGrantDAppPermission"
const SIGNAL_CONNECTOR_REVOKE_DAPP_PERMISSION* = "ConnectorRevokeDAppPermission"
const SIGNAL_CONNECTOR_PERSONAL_SIGN* = "ConnectorPersonalSign"
const SIGNAL_CONNECTOR_SIGN* = "ConnectorSign"
logScope:
topics = "connector-controller"
@ -32,14 +32,14 @@ QtObject:
proc disconnected*(self: Controller, payload: string) {.signal.}
proc sendTransaction*(self: Controller, requestId: string, payload: string) {.signal.}
proc personalSign(self: Controller, requestId: string, payload: string) {.signal.}
proc sign(self: Controller, requestId: string, payload: string) {.signal.}
proc approveConnectResponse*(self: Controller, payload: string, error: bool) {.signal.}
proc rejectConnectResponse*(self: Controller, payload: string, error: bool) {.signal.}
proc approveTransactionResponse*(self: Controller, topic: string, requestId: string, error: bool) {.signal.}
proc rejectTransactionResponse*(self: Controller, topic: string, requestId: string, error: bool) {.signal.}
proc approvePersonalSignResponse*(self: Controller, topic: string, requestId: string, error: bool) {.signal.}
proc rejectPersonalSignResponse*(self: Controller, topic: string, requestId: string, error: bool) {.signal.}
proc approveSignResponse*(self: Controller, topic: string, requestId: string, error: bool) {.signal.}
proc rejectSignResponse*(self: Controller, topic: string, requestId: string, error: bool) {.signal.}
proc newController*(service: connector_service.Service, events: EventEmitter): Controller =
new(result, delete)
@ -97,17 +97,18 @@ QtObject:
controller.disconnected(dappInfo.toJson())
result.events.on(SIGNAL_CONNECTOR_PERSONAL_SIGN) do(e: Args):
let params = ConnectorPersonalSignSignal(e)
result.events.on(SIGNAL_CONNECTOR_SIGN) do(e: Args):
let params = ConnectorSignSignal(e)
let dappInfo = %*{
"icon": params.iconUrl,
"name": params.name,
"url": params.url,
"challenge": params.challenge,
"address": params.address,
"method": params.signMethod,
}
controller.personalSign(params.requestId, dappInfo.toJson())
controller.sign(params.requestId, dappInfo.toJson())
result.QObject.setup
@ -146,11 +147,11 @@ QtObject:
proc getDApps*(self: Controller): string {.slot.} =
return self.service.getDApps()
proc approvePersonalSigning*(self: Controller, sessionTopic: string, requestId: string, signature: string): bool {.slot.} =
result = self.service.approvePersonalSignRequest(requestId, signature)
self.approvePersonalSignResponse(sessionTopic, requestId, not result)
proc approveSigning*(self: Controller, sessionTopic: string, requestId: string, signature: string): bool {.slot.} =
result = self.service.approveSignRequest(requestId, signature)
self.approveSignResponse(sessionTopic, requestId, not result)
proc rejectPersonalSigning*(self: Controller, sessionTopic: string, requestId: string): bool {.slot.} =
result = self.service.rejectPersonalSigning(requestId)
self.rejectPersonalSignResponse(sessionTopic, requestId, not result)
proc rejectSigning*(self: Controller, sessionTopic: string, requestId: string): bool {.slot.} =
result = self.service.rejectSigning(requestId)
self.rejectSignResponse(sessionTopic, requestId, not result)

View File

@ -16,7 +16,7 @@ const SIGNAL_CONNECTOR_SEND_REQUEST_ACCOUNTS* = "ConnectorSendRequestAccounts"
const SIGNAL_CONNECTOR_EVENT_CONNECTOR_SEND_TRANSACTION* = "ConnectorSendTransaction"
const SIGNAL_CONNECTOR_GRANT_DAPP_PERMISSION* = "ConnectorGrantDAppPermission"
const SIGNAL_CONNECTOR_REVOKE_DAPP_PERMISSION* = "ConnectorRevokeDAppPermission"
const SIGNAL_CONNECTOR_EVENT_CONNECTOR_PERSONAL_SIGN* = "ConnectorPersonalSign"
const SIGNAL_CONNECTOR_EVENT_CONNECTOR_SIGN* = "ConnectorSign"
# Enum with events
type Event* = enum
@ -84,17 +84,17 @@ QtObject:
self.events.emit(SIGNAL_CONNECTOR_REVOKE_DAPP_PERMISSION, data)
)
self.events.on(SignalType.ConnectorPersonalSign.event, proc(e: Args) =
self.events.on(SignalType.ConnectorSign.event, proc(e: Args) =
if self.eventHandler == nil:
return
var data = ConnectorPersonalSignSignal(e)
var data = ConnectorSignSignal(e)
if not data.requestId.len() == 0:
error "ConnectorPersonalSignSignal failed, requestId is empty"
error "ConnectorSignSignal failed, requestId is empty"
return
self.events.emit(SIGNAL_CONNECTOR_EVENT_CONNECTOR_PERSONAL_SIGN, data)
self.events.emit(SIGNAL_CONNECTOR_EVENT_CONNECTOR_SIGN, data)
)
proc registerEventsHandler*(self: Service, handler: EventHandlerFn) =
@ -165,17 +165,17 @@ QtObject:
error "getDApps failed: ", err=e.msg
return "[]"
proc approvePersonalSignRequest*(self: Service, requestId: string, signature: string): bool =
proc approveSignRequest*(self: Service, requestId: string, signature: string): bool =
try:
var args = PersonalSignAcceptedArgs()
var args = SignAcceptedArgs()
args.requestId = requestId
args.signature = signature
return status_go.sendPersonalSignAcceptedFinishedRpc(args)
return status_go.sendSignAcceptedFinishedRpc(args)
except Exception as e:
error "sendPersonalSigAcceptedFinishedRpc failed: ", err=e.msg
error "sendSigAcceptedFinishedRpc failed: ", err=e.msg
return false
proc rejectPersonalSigning*(self: Service, requestId: string): bool =
rejectRequest(self, requestId, status_go.sendPersonalSignRejectedFinishedRpc, "sendPersonalSignRejectedFinishedRpc failed: ")
proc rejectSigning*(self: Service, requestId: string): bool =
rejectRequest(self, requestId, status_go.sendSignRejectedFinishedRpc, "sendSignRejectedFinishedRpc failed: ")

View File

@ -23,7 +23,7 @@ type RejectedArgs* = ref object of RootObj
type RecallDAppPermissionArgs* = ref object of RootObj
dAppUrl* {.serializedFieldName("dAppUrl").}: string
type PersonalSignAcceptedArgs* = ref object of RootObj
type SignAcceptedArgs* = ref object of RootObj
requestId* {.serializedFieldName("requestId").}: string
signature* {.serializedFieldName("signature").}: string
@ -45,10 +45,10 @@ rpc(recallDAppPermission, "connector"):
rpc(getPermittedDAppsList, "connector"):
discard
rpc(personalSignAccepted, "connector"):
args: PersonalSignAcceptedArgs
rpc(signAccepted, "connector"):
args: SignAcceptedArgs
rpc(personalSignRejected, "connector"):
rpc(signRejected, "connector"):
args: RejectedArgs
proc isSuccessResponse(rpcResponse: RpcResponse[JsonNode]): bool =
@ -69,8 +69,8 @@ proc sendTransactionRejectedFinishedRpc*(args: RejectedArgs): bool =
proc recallDAppPermissionFinishedRpc*(dAppUrl: string): bool =
return isSuccessResponse(recallDAppPermission(dAppUrl))
proc sendPersonalSignAcceptedFinishedRpc*(args: PersonalSignAcceptedArgs): bool =
return isSuccessResponse(personalSignAccepted(args))
proc sendSignAcceptedFinishedRpc*(args: SignAcceptedArgs): bool =
return isSuccessResponse(signAccepted(args))
proc sendPersonalSignRejectedFinishedRpc*(args: RejectedArgs): bool =
return isSuccessResponse(personalSignRejected(args))
proc sendSignRejectedFinishedRpc*(args: RejectedArgs): bool =
return isSuccessResponse(signRejected(args))

View File

@ -374,7 +374,8 @@ Item {
accountsModel: dappModule.accountsModel
store: SharedStores.BrowserConnectStore {
signal connectRequested(string requestId, string dappJson)
signal signRequested(string requestId, string requestJson)
signal sendTransaction(string requestId, string requestJson)
signal sign(string requestId, string dappJson)
signal connected(string dappJson)
signal disconnected(string dappJson)
@ -385,6 +386,8 @@ Item {
signal approveTransactionResponse(string topic, string requestId, bool error)
signal rejectTransactionResponse(string topic, string requestId, bool error)
signal approveSignResponse(string topic, string requestId, bool error)
signal rejectSignResponse(string topic, string requestId, bool error)
}
}
store: SharedStores.DAppsStore {

View File

@ -53,22 +53,22 @@ WalletConnectSDKBase {
}
}
function onPersonalSign(requestId, dappInfoString) {
function onSign(requestId, dappInfoString) {
try {
const dappInfo = JSON.parse(dappInfoString)
const mainNet = SQUtils.ModelUtils.getByKey(root.networksModel, "layer", 1)
if (!mainNet) {
root.store.rejectPersonalSign(requestId)
console.error("Mainnet not found - personal sign failed")
root.store.rejectSign(requestId)
console.error("Mainnet not found - sign failed")
return
}
const event = d.buildSignRequest(requestId, dappInfo.url, mainNet.chainId, dappInfo.challenge, dappInfo.address)
const event = d.buildSignRequest(requestId, dappInfo.url, mainNet.chainId, dappInfo.challenge, dappInfo.address, dappInfo.method)
d.sessionRequests.set(requestId, event)
root.sessionRequestEvent(event)
} catch (e) {
d.sessionRequests.delete(requestId)
root.store.rejectPersonalSign("", requestId)
root.store.rejectSign("", requestId)
console.error("Failed to parse dappInfo for session request", e)
}
}
@ -111,21 +111,21 @@ WalletConnectSDKBase {
}
}
function onApprovePersonalSignResponse(topic, requestId, error) {
function onApproveSignResponse(topic, requestId, error) {
try {
const errorStr = error ? "Faled to approve personal sign" : ""
const errorStr = error ? "Faled to approve sign" : ""
root.sessionRequestUserAnswerResult(topic, requestId, true, errorStr)
} catch (e) {
console.error("Failed to approve personal sign response", e)
console.error("Failed to approve sign response", e)
}
}
function onRejectPersonalSignResponse(topic, requestId, error) {
function onRejectSignResponse(topic, requestId, error) {
try {
const errorStr = error ? "Faled to reject personal sign" : ""
const errorStr = error ? "Faled to reject sign" : ""
root.sessionRequestUserAnswerResult(topic, requestId, false, errorStr)
} catch (e) {
console.error("Failed to reject personal sign response", e)
console.error("Failed to reject sign response", e)
}
}
}
@ -165,8 +165,9 @@ WalletConnectSDKBase {
const event = d.sessionRequests.get(requestId)
if (event.params.request.method === SessionRequest.methods.sendTransaction.name) {
root.store.approveTransaction(topic, requestId, signature)
} else if (event.params.request.method === SessionRequest.methods.personalSign.name) {
root.store.approvePersonalSign(topic, requestId, signature)
} else if (event.params.request.method === SessionRequest.methods.personalSign.name ||
event.params.request.method === SessionRequest.methods.signTypedData_v4.name) {
root.store.approveSign(topic, requestId, signature)
} else {
root.sessionRequestUserAnswerResult(topic, requestId, false, "Unknown request method")
console.error("Unknown request method", event.params.request.method)
@ -182,8 +183,9 @@ WalletConnectSDKBase {
const event = d.sessionRequests.get(requestId)
if (event.params.request.method === SessionRequest.methods.sendTransaction.name) {
root.store.rejectTransaction(topic, requestId, error)
} else if (event.params.request.method === SessionRequest.methods.personalSign.name) {
root.store.rejectPersonalSign(topic, requestId)
} else if (event.params.request.method === SessionRequest.methods.personalSign.name ||
event.params.request.method === SessionRequest.methods.signTypedData_v4.name) {
root.store.rejectSign(topic, requestId)
} else {
root.sessionRequestUserAnswerResult(topic, requestId, false, "Unknown request method")
console.error("Unknown request method", event.params.request.method)
@ -292,18 +294,25 @@ WalletConnectSDKBase {
}
}
function buildSignRequest(requestId, topic, chainId, challenge, address) {
function buildSignRequest(requestId, topic, chainId, challenge, address, method) {
let params = []
if (method == SessionRequest.methods.personalSign.name) {
params = [challenge, address]
} else if (method == SessionRequest.methods.signTypedData_v4.name) {
params = [address, challenge]
} else {
console.error("Unknown sign method", method)
return
}
return {
id: requestId,
topic,
params: {
chainId: `eip155:${chainId}`,
request: {
method: SessionRequest.methods.personalSign.name,
params: [
challenge,
address
]
method,
params
}
}
}

View File

@ -10,7 +10,7 @@ SQUtils.QObject {
// Signals driven by the dApp
signal connectRequested(string requestId, string dappJson)
signal sendTransaction(string requestId, string requestJson)
signal personalSign(string requestId, string dappJson)
signal sign(string requestId, string dappJson)
signal connected(string dappJson)
signal disconnected(string dappJson)
@ -21,8 +21,8 @@ SQUtils.QObject {
signal approveTransactionResponse(string topic, string requestId, bool error)
signal rejectTransactionResponse(string topic, string requestId, bool error)
signal approvePersonalSignResponse(string topic, string requestId, bool error)
signal rejectPersonalSignResponse(string topic, string requestId, bool error)
signal approveSignResponse(string topic, string requestId, bool error)
signal rejectSignResponse(string topic, string requestId, bool error)
function approveConnection(id, account, chainId) {
return controller.approveConnection(id, account, chainId)
@ -48,12 +48,12 @@ SQUtils.QObject {
return controller.getDApps()
}
function approvePersonalSign(topic, requestId, signature) {
return controller.approvePersonalSigning(topic, requestId, signature)
function approveSign(topic, requestId, signature) {
return controller.approveSigning(topic, requestId, signature)
}
function rejectPersonalSign(topic, requestId) {
return controller.rejectPersonalSigning(topic, requestId)
function rejectSign(topic, requestId) {
return controller.rejectSigning(topic, requestId)
}
Connections {
@ -67,8 +67,8 @@ SQUtils.QObject {
root.sendTransaction(requestId, requestJson)
}
function onPersonalSign(requestId, dappJson) {
root.personalSign(requestId, dappJson)
function onSign(requestId, dappJson) {
root.sign(requestId, dappJson)
}
function onConnected(dappJson) {
@ -95,12 +95,12 @@ SQUtils.QObject {
root.rejectTransactionResponse(topic, requestId, error)
}
function onApprovePersonalSignResponse(topic, requestId, error) {
root.approvePersonalSignResponse(topic, requestId, error)
function onApproveSignResponse(topic, requestId, error) {
root.approveSignResponse(topic, requestId, error)
}
function onRejectPersonalSignResponse(topic, requestId, error) {
root.rejectPersonalSignResponse(topic, requestId, error)
function onRejectSignResponse(topic, requestId, error) {
root.rejectSignResponse(topic, requestId, error)
}
}
}

2
vendor/status-go vendored

@ -1 +1 @@
Subproject commit 43f355a391ae2ccf304c9b71a1a6af35a3ddb67e
Subproject commit e953cb6c95ebc6d79a552e27f85cd4047500046f