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 name*: string
iconUrl*: string iconUrl*: string
type ConnectorPersonalSignSignal* = ref object of Signal type ConnectorSignSignal* = ref object of Signal
url*: string url*: string
name*: string name*: string
iconUrl*: string iconUrl*: string
requestId*: string requestId*: string
challenge*: string challenge*: string
address*: string address*: string
signMethod*: string
proc fromEvent*(T: type ConnectorSendRequestAccountsSignal, event: JsonNode): ConnectorSendRequestAccountsSignal = proc fromEvent*(T: type ConnectorSendRequestAccountsSignal, event: JsonNode): ConnectorSendRequestAccountsSignal =
result = ConnectorSendRequestAccountsSignal() result = ConnectorSendRequestAccountsSignal()
@ -67,11 +68,12 @@ proc fromEvent*(T: type ConnectorRevokeDAppPermissionSignal, event: JsonNode): C
result.name = event["event"]{"name"}.getStr() result.name = event["event"]{"name"}.getStr()
result.iconUrl = event["event"]{"iconUrl"}.getStr() result.iconUrl = event["event"]{"iconUrl"}.getStr()
proc fromEvent*(T: type ConnectorPersonalSignSignal, event: JsonNode): ConnectorPersonalSignSignal = proc fromEvent*(T: type ConnectorSignSignal, event: JsonNode): ConnectorSignSignal =
result = ConnectorPersonalSignSignal() result = ConnectorSignSignal()
result.url = event["event"]{"url"}.getStr() result.url = event["event"]{"url"}.getStr()
result.name = event["event"]{"name"}.getStr() result.name = event["event"]{"name"}.getStr()
result.iconUrl = event["event"]{"iconUrl"}.getStr() result.iconUrl = event["event"]{"iconUrl"}.getStr()
result.requestId = event["event"]{"requestId"}.getStr() result.requestId = event["event"]{"requestId"}.getStr()
result.challenge = event["event"]{"challenge"}.getStr() result.challenge = event["event"]{"challenge"}.getStr()
result.address = event["event"]{"address"}.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" ConnectorSendTransaction = "connector.sendTransaction"
ConnectorGrantDAppPermission = "connector.dAppPermissionGranted" ConnectorGrantDAppPermission = "connector.dAppPermissionGranted"
ConnectorRevokeDAppPermission = "connector.dAppPermissionRevoked" ConnectorRevokeDAppPermission = "connector.dAppPermissionRevoked"
ConnectorPersonalSign = "connector.personalSign" ConnectorSign = "connector.Sign"
Unknown Unknown
proc event*(self:SignalType):string = proc event*(self:SignalType):string =

View File

@ -145,7 +145,7 @@ QtObject:
of SignalType.ConnectorSendTransaction: ConnectorSendTransactionSignal.fromEvent(jsonSignal) of SignalType.ConnectorSendTransaction: ConnectorSendTransactionSignal.fromEvent(jsonSignal)
of SignalType.ConnectorGrantDAppPermission: ConnectorGrantDAppPermissionSignal.fromEvent(jsonSignal) of SignalType.ConnectorGrantDAppPermission: ConnectorGrantDAppPermissionSignal.fromEvent(jsonSignal)
of SignalType.ConnectorRevokeDAppPermission: ConnectorRevokeDAppPermissionSignal.fromEvent(jsonSignal) of SignalType.ConnectorRevokeDAppPermission: ConnectorRevokeDAppPermissionSignal.fromEvent(jsonSignal)
of SignalType.ConnectorPersonalSign: ConnectorPersonalSignSignal.fromEvent(jsonSignal) of SignalType.ConnectorSign: ConnectorSignSignal.fromEvent(jsonSignal)
else: Signal() else: Signal()
result.signalType = signalType 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_EVENT_CONNECTOR_SEND_TRANSACTION* = "ConnectorSendTransaction"
const SIGNAL_CONNECTOR_GRANT_DAPP_PERMISSION* = "ConnectorGrantDAppPermission" const SIGNAL_CONNECTOR_GRANT_DAPP_PERMISSION* = "ConnectorGrantDAppPermission"
const SIGNAL_CONNECTOR_REVOKE_DAPP_PERMISSION* = "ConnectorRevokeDAppPermission" const SIGNAL_CONNECTOR_REVOKE_DAPP_PERMISSION* = "ConnectorRevokeDAppPermission"
const SIGNAL_CONNECTOR_PERSONAL_SIGN* = "ConnectorPersonalSign" const SIGNAL_CONNECTOR_SIGN* = "ConnectorSign"
logScope: logScope:
topics = "connector-controller" topics = "connector-controller"
@ -32,14 +32,14 @@ QtObject:
proc disconnected*(self: Controller, payload: string) {.signal.} proc disconnected*(self: Controller, payload: string) {.signal.}
proc sendTransaction*(self: Controller, requestId: string, 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 approveConnectResponse*(self: Controller, payload: string, error: bool) {.signal.}
proc rejectConnectResponse*(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 approveTransactionResponse*(self: Controller, topic: string, requestId: string, error: bool) {.signal.}
proc rejectTransactionResponse*(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 approveSignResponse*(self: Controller, topic: string, requestId: string, error: bool) {.signal.}
proc rejectPersonalSignResponse*(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 = proc newController*(service: connector_service.Service, events: EventEmitter): Controller =
new(result, delete) new(result, delete)
@ -97,17 +97,18 @@ QtObject:
controller.disconnected(dappInfo.toJson()) controller.disconnected(dappInfo.toJson())
result.events.on(SIGNAL_CONNECTOR_PERSONAL_SIGN) do(e: Args): result.events.on(SIGNAL_CONNECTOR_SIGN) do(e: Args):
let params = ConnectorPersonalSignSignal(e) let params = ConnectorSignSignal(e)
let dappInfo = %*{ let dappInfo = %*{
"icon": params.iconUrl, "icon": params.iconUrl,
"name": params.name, "name": params.name,
"url": params.url, "url": params.url,
"challenge": params.challenge, "challenge": params.challenge,
"address": params.address, "address": params.address,
"method": params.signMethod,
} }
controller.personalSign(params.requestId, dappInfo.toJson()) controller.sign(params.requestId, dappInfo.toJson())
result.QObject.setup result.QObject.setup
@ -146,11 +147,11 @@ QtObject:
proc getDApps*(self: Controller): string {.slot.} = proc getDApps*(self: Controller): string {.slot.} =
return self.service.getDApps() return self.service.getDApps()
proc approvePersonalSigning*(self: Controller, sessionTopic: string, requestId: string, signature: string): bool {.slot.} = proc approveSigning*(self: Controller, sessionTopic: string, requestId: string, signature: string): bool {.slot.} =
result = self.service.approvePersonalSignRequest(requestId, signature) result = self.service.approveSignRequest(requestId, signature)
self.approvePersonalSignResponse(sessionTopic, requestId, not result) self.approveSignResponse(sessionTopic, requestId, not result)
proc rejectPersonalSigning*(self: Controller, sessionTopic: string, requestId: string): bool {.slot.} = proc rejectSigning*(self: Controller, sessionTopic: string, requestId: string): bool {.slot.} =
result = self.service.rejectPersonalSigning(requestId) result = self.service.rejectSigning(requestId)
self.rejectPersonalSignResponse(sessionTopic, requestId, not result) 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_EVENT_CONNECTOR_SEND_TRANSACTION* = "ConnectorSendTransaction"
const SIGNAL_CONNECTOR_GRANT_DAPP_PERMISSION* = "ConnectorGrantDAppPermission" const SIGNAL_CONNECTOR_GRANT_DAPP_PERMISSION* = "ConnectorGrantDAppPermission"
const SIGNAL_CONNECTOR_REVOKE_DAPP_PERMISSION* = "ConnectorRevokeDAppPermission" 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 # Enum with events
type Event* = enum type Event* = enum
@ -84,17 +84,17 @@ QtObject:
self.events.emit(SIGNAL_CONNECTOR_REVOKE_DAPP_PERMISSION, data) 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: if self.eventHandler == nil:
return return
var data = ConnectorPersonalSignSignal(e) var data = ConnectorSignSignal(e)
if not data.requestId.len() == 0: if not data.requestId.len() == 0:
error "ConnectorPersonalSignSignal failed, requestId is empty" error "ConnectorSignSignal failed, requestId is empty"
return 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) = proc registerEventsHandler*(self: Service, handler: EventHandlerFn) =
@ -165,17 +165,17 @@ QtObject:
error "getDApps failed: ", err=e.msg error "getDApps failed: ", err=e.msg
return "[]" return "[]"
proc approvePersonalSignRequest*(self: Service, requestId: string, signature: string): bool = proc approveSignRequest*(self: Service, requestId: string, signature: string): bool =
try: try:
var args = PersonalSignAcceptedArgs() var args = SignAcceptedArgs()
args.requestId = requestId args.requestId = requestId
args.signature = signature args.signature = signature
return status_go.sendPersonalSignAcceptedFinishedRpc(args) return status_go.sendSignAcceptedFinishedRpc(args)
except Exception as e: except Exception as e:
error "sendPersonalSigAcceptedFinishedRpc failed: ", err=e.msg error "sendSigAcceptedFinishedRpc failed: ", err=e.msg
return false return false
proc rejectPersonalSigning*(self: Service, requestId: string): bool = proc rejectSigning*(self: Service, requestId: string): bool =
rejectRequest(self, requestId, status_go.sendPersonalSignRejectedFinishedRpc, "sendPersonalSignRejectedFinishedRpc failed: ") 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 type RecallDAppPermissionArgs* = ref object of RootObj
dAppUrl* {.serializedFieldName("dAppUrl").}: string dAppUrl* {.serializedFieldName("dAppUrl").}: string
type PersonalSignAcceptedArgs* = ref object of RootObj type SignAcceptedArgs* = ref object of RootObj
requestId* {.serializedFieldName("requestId").}: string requestId* {.serializedFieldName("requestId").}: string
signature* {.serializedFieldName("signature").}: string signature* {.serializedFieldName("signature").}: string
@ -45,10 +45,10 @@ rpc(recallDAppPermission, "connector"):
rpc(getPermittedDAppsList, "connector"): rpc(getPermittedDAppsList, "connector"):
discard discard
rpc(personalSignAccepted, "connector"): rpc(signAccepted, "connector"):
args: PersonalSignAcceptedArgs args: SignAcceptedArgs
rpc(personalSignRejected, "connector"): rpc(signRejected, "connector"):
args: RejectedArgs args: RejectedArgs
proc isSuccessResponse(rpcResponse: RpcResponse[JsonNode]): bool = proc isSuccessResponse(rpcResponse: RpcResponse[JsonNode]): bool =
@ -69,8 +69,8 @@ proc sendTransactionRejectedFinishedRpc*(args: RejectedArgs): bool =
proc recallDAppPermissionFinishedRpc*(dAppUrl: string): bool = proc recallDAppPermissionFinishedRpc*(dAppUrl: string): bool =
return isSuccessResponse(recallDAppPermission(dAppUrl)) return isSuccessResponse(recallDAppPermission(dAppUrl))
proc sendPersonalSignAcceptedFinishedRpc*(args: PersonalSignAcceptedArgs): bool = proc sendSignAcceptedFinishedRpc*(args: SignAcceptedArgs): bool =
return isSuccessResponse(personalSignAccepted(args)) return isSuccessResponse(signAccepted(args))
proc sendPersonalSignRejectedFinishedRpc*(args: RejectedArgs): bool = proc sendSignRejectedFinishedRpc*(args: RejectedArgs): bool =
return isSuccessResponse(personalSignRejected(args)) return isSuccessResponse(signRejected(args))

View File

@ -374,7 +374,8 @@ Item {
accountsModel: dappModule.accountsModel accountsModel: dappModule.accountsModel
store: SharedStores.BrowserConnectStore { store: SharedStores.BrowserConnectStore {
signal connectRequested(string requestId, string dappJson) 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 connected(string dappJson)
signal disconnected(string dappJson) signal disconnected(string dappJson)
@ -385,6 +386,8 @@ Item {
signal approveTransactionResponse(string topic, string requestId, bool error) signal approveTransactionResponse(string topic, string requestId, bool error)
signal rejectTransactionResponse(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 { store: SharedStores.DAppsStore {

View File

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

View File

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

2
vendor/status-go vendored

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