From 5e899bad2f31ab09fe046999f25263260d4eaec3 Mon Sep 17 00:00:00 2001 From: kyranjamie Date: Mon, 10 Feb 2025 14:50:26 +0100 Subject: [PATCH] feat: add stx_signStructuredMessage, closes LEA-1960 --- .../messaging/rpc-message-handler.ts | 10 ++- .../rpc-methods/sign-stacks-message.ts | 75 +++++++++++++------ test-app/src/components/signature.tsx | 2 +- 3 files changed, 63 insertions(+), 24 deletions(-) diff --git a/src/background/messaging/rpc-message-handler.ts b/src/background/messaging/rpc-message-handler.ts index bb510ff6cd..99e8361028 100644 --- a/src/background/messaging/rpc-message-handler.ts +++ b/src/background/messaging/rpc-message-handler.ts @@ -12,7 +12,10 @@ import { rpcOpen } from './rpc-methods/open'; import { rpcSendTransfer } from './rpc-methods/send-transfer'; import { rpcSignMessage } from './rpc-methods/sign-message'; import { rpcSignPsbt } from './rpc-methods/sign-psbt'; -import { rpcSignStacksMessage } from './rpc-methods/sign-stacks-message'; +import { + rpcSignStacksMessage, + rpcSignStacksStructuredMessage, +} from './rpc-methods/sign-stacks-message'; import { rpcStxCallContract } from './rpc-methods/stx-call-contract'; import { rpcStxGetAddresses } from './rpc-methods/stx-get-addresses'; import { rpcSupportedMethods } from './rpc-methods/supported-methods'; @@ -69,6 +72,11 @@ export async function rpcMessageHandler(message: WalletRequests, port: chrome.ru break; } + case 'stx_signStructuredMessage': { + await rpcSignStacksStructuredMessage(message, port); + break; + } + case 'stx_getAddresses': { await rpcStxGetAddresses(message, port); break; diff --git a/src/background/messaging/rpc-methods/sign-stacks-message.ts b/src/background/messaging/rpc-methods/sign-stacks-message.ts index dba4dce45e..65497456e5 100644 --- a/src/background/messaging/rpc-methods/sign-stacks-message.ts +++ b/src/background/messaging/rpc-methods/sign-stacks-message.ts @@ -1,9 +1,12 @@ +import { serializeCV } from '@stacks/transactions'; + import { RpcErrorCode, type StxSignMessageRequest, type StxSignMessageRequestParamsStructured, + type StxSignStructuredMessageRequest, } from '@leather.io/rpc'; -import { isDefined, isUndefined } from '@leather.io/utils'; +import { isDefined, isString, isUndefined } from '@leather.io/utils'; import { RouteUrls } from '@shared/route-urls'; import { @@ -21,15 +24,17 @@ import { } from '../messaging-utils'; import { trackRpcRequestError, trackRpcRequestSuccess } from '../rpc-message-handler'; -export async function rpcSignStacksMessage( - message: StxSignMessageRequest, - port: chrome.runtime.Port +async function handleRpcSignStacksMessage( + method: 'stx_signMessage' | 'stx_signStructuredMessage', + message: StxSignMessageRequest | StxSignStructuredMessageRequest, + port: chrome.runtime.Port, + requestParams: RequestParams ) { if (isUndefined(message.params)) { - void trackRpcRequestError({ endpoint: message.method, error: 'Undefined parameters' }); + void trackRpcRequestError({ endpoint: method, error: 'Undefined parameters' }); chrome.tabs.sendMessage( getTabIdFromPort(port), - makeRpcErrorResponse('stx_signMessage', { + makeRpcErrorResponse(method, { id: message.id, error: { code: RpcErrorCode.INVALID_REQUEST, message: 'Parameters undefined' }, }) @@ -38,10 +43,10 @@ export async function rpcSignStacksMessage( } if (!validateRpcSignStacksMessageParams(message.params)) { - void trackRpcRequestError({ endpoint: message.method, error: 'Invalid parameters' }); + void trackRpcRequestError({ endpoint: method, error: 'Invalid parameters' }); chrome.tabs.sendMessage( getTabIdFromPort(port), - makeRpcErrorResponse('stx_signMessage', { + makeRpcErrorResponse(method, { id: message.id, error: { code: RpcErrorCode.INVALID_PARAMS, @@ -52,8 +57,26 @@ export async function rpcSignStacksMessage( return; } - void trackRpcRequestSuccess({ endpoint: message.method }); + void trackRpcRequestSuccess({ endpoint: method }); + + const { urlParams, tabId } = makeSearchParamsWithDefaults(port, requestParams); + + const { id } = await triggerRequestWindowOpen(RouteUrls.RpcStacksSignature, urlParams); + listenForPopupClose({ + tabId, + id, + response: makeRpcErrorResponse(method, { + id: message.id, + error: { + code: RpcErrorCode.USER_REJECTION, + message: 'User rejected the Stacks message signing request', + }, + }), + }); +} + +export function rpcSignStacksMessage(message: StxSignMessageRequest, port: chrome.runtime.Port) { const requestParams: RequestParams = [ ['message', message.params.message], ['messageType', message.params.messageType], @@ -71,19 +94,27 @@ export async function rpcSignStacksMessage( ]); } - const { urlParams, tabId } = makeSearchParamsWithDefaults(port, requestParams); + return handleRpcSignStacksMessage('stx_signMessage', message, port, requestParams); +} - const { id } = await triggerRequestWindowOpen(RouteUrls.RpcStacksSignature, urlParams); +export function rpcSignStacksStructuredMessage( + message: StxSignStructuredMessageRequest, + port: chrome.runtime.Port +) { + const requestParams: RequestParams = [ + ['requestId', message.id], + ['messageType', 'structured'], + [ + 'message', + isString(message.params.message) + ? message.params.message + : serializeCV(message.params.message), + ], + [ + 'domain', + isString(message.params.domain) ? message.params.domain : serializeCV(message.params.domain), + ], + ]; - listenForPopupClose({ - tabId, - id, - response: makeRpcErrorResponse('stx_signMessage', { - id: message.id, - error: { - code: RpcErrorCode.USER_REJECTION, - message: 'User rejected the Stacks message signing request', - }, - }), - }); + return handleRpcSignStacksMessage('stx_signStructuredMessage', message, port, requestParams); } diff --git a/test-app/src/components/signature.tsx b/test-app/src/components/signature.tsx index cb0071518d..334d51fa02 100644 --- a/test-app/src/components/signature.tsx +++ b/test-app/src/components/signature.tsx @@ -187,7 +187,7 @@ export const Signature = () => { const stringMessage = serializeCV(message); const stringDomain = serializeCV(domain); - const result = await window.LeatherProvider.request('stx_signMessage', { + const result = await window.LeatherProvider.request('stx_signStructuredMessage', { message: stringMessage, messageType: 'structured', domain: stringDomain,