Skip to content

Commit 7e1dd0d

Browse files
Change sign rewards message payload (#554)
Changes the SignRewardsMessage request payload from ```ts { account: { address: "" }, message: "" } ``` to ```ts { accountId: "", message: "" } ``` This allows to read the account from state via `this.#accountsService.findById(accountId);` instead of `this.#accountsService.findByAddress(address);`, to benefit from more efficient lookup. Also adds a button in test dapp to test the endpoint: <img width="255" height="165" alt="image" src="https://github.com/user-attachments/assets/ff5313f8-b759-498f-b1b0-47937fdcf2d1" /> <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Change signRewardsMessage to use accountId instead of address and add a test dapp UI/handler to invoke it. > > - **Snap**: > - **API change**: `signRewardsMessage` now accepts `accountId` (UUID) instead of `account.address`; OpenRPC schema and examples updated. > - **Handler**: use `accountsService.findById(accountId)` and validate rewards message address against `account.address` before `signMessage`. > - **Validation**: update request structs to `accountId`; keep `RewardsMessage` parsing/validation. > - **RPC wiring**: add `TestDappRpcRequestMethod.SignRewardsMessage` route that forwards to `ClientRequestMethod.SignRewardsMessage`. > - **Permissions**: allow `TestDappRpcRequestMethod.SignRewardsMessage` for dev dapp origins. > - **Tests**: update unit tests to use `accountId` and `findById` assertions. > - **Site**: > - Add `ClientRequest` component with "Sign Rewards Message" button; lists accounts via keyring, invokes `signRewardsMessage`, and shows success toast. > - Register `ClientRequest` in `Handlers`. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 3b21090. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent ff710bd commit 7e1dd0d

File tree

10 files changed

+95
-26
lines changed

10 files changed

+95
-26
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/* eslint-disable @typescript-eslint/no-unused-vars */
2+
import { Button, Card, Flex } from '@chakra-ui/react';
3+
import type { KeyringAccount } from '@metamask/keyring-api';
4+
import { KeyringRpcMethod } from '@metamask/keyring-api';
5+
6+
import { TestDappRpcRequestMethod } from '../../../../snap/src/core/handlers/onRpcRequest/types';
7+
import { useInvokeKeyring, useInvokeSnap } from '../../hooks';
8+
import { useShowToasterForResponse } from '../../hooks/useToasterForResponse';
9+
10+
export const ClientRequest = () => {
11+
const invokeSnap = useInvokeSnap();
12+
const { showToasterForResponse } = useShowToasterForResponse();
13+
const invokeKeyring = useInvokeKeyring();
14+
15+
const showSuccessToast = (title: string) =>
16+
showToasterForResponse(
17+
{ result: 'ok' },
18+
{
19+
title,
20+
},
21+
);
22+
23+
const signRewardsMessage = async () => {
24+
const accountsToSet = (await invokeKeyring({
25+
method: KeyringRpcMethod.ListAccounts,
26+
})) as KeyringAccount[];
27+
const account = accountsToSet[0];
28+
if (!account) {
29+
return;
30+
}
31+
const { id: accountId, address } = account;
32+
33+
const timestamp = Math.floor(Date.now() / 1000);
34+
35+
const response = await invokeSnap({
36+
method: TestDappRpcRequestMethod.SignRewardsMessage,
37+
params: {
38+
accountId,
39+
message: btoa(`rewards,${address},${timestamp}`),
40+
},
41+
});
42+
43+
showSuccessToast(
44+
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
45+
`Successfully signed the rewards message! Signature: ${(response as any).signature}`,
46+
);
47+
};
48+
49+
return (
50+
<Card.Root>
51+
<Card.Header>
52+
<Card.Title>ClientRequest</Card.Title>
53+
</Card.Header>
54+
<Card.Body>
55+
<Flex>
56+
<Button variant="outline" onClick={signRewardsMessage}>
57+
Sign Rewards Message
58+
</Button>
59+
</Flex>
60+
</Card.Body>
61+
</Card.Root>
62+
);
63+
};

packages/site/src/components/Handlers/Handlers.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Text as ChakraText, Flex, Stack } from '@chakra-ui/react';
22

33
import { Accounts } from './Accounts';
4+
import { ClientRequest } from './ClientRequest';
45
import { Lifecycle } from './Lifecycle';
56
import { OnProtocolRequest } from './OnProtocolRequest';
67
import { WebSockets } from './WebSockets';
@@ -15,6 +16,7 @@ export const Handlers = () => (
1516
<WebSockets />
1617
<Lifecycle />
1718
<Accounts />
19+
<ClientRequest />
1820
</Stack>
1921
</Flex>
2022
);

packages/snap/openrpc.json

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -695,11 +695,11 @@
695695
"paramStructure": "by-name",
696696
"params": [
697697
{
698-
"name": "account",
699-
"summary": "The account to use for signing the transaction",
698+
"name": "accountId",
699+
"summary": "The UUID of the account to use for signing the message",
700700
"required": true,
701701
"schema": {
702-
"$ref": "#/components/schemas/WalletAccount"
702+
"$ref": "#/components/schemas/Uuid"
703703
}
704704
},
705705
{
@@ -741,10 +741,8 @@
741741
"description": "Example of signing a rewards message for a Solana address",
742742
"params": [
743743
{
744-
"name": "account",
745-
"value": {
746-
"address": "BLw3RweJmfbTapJRgnPRvd962YDjFYAnVGd1p5hmZ5tP"
747-
}
744+
"name": "accountId",
745+
"value": "c747acb9-1b2b-4352-b9da-3d658fcc3cc7"
748746
},
749747
{
750748
"name": "message",

packages/snap/snap.manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"url": "https://github.com/MetaMask/snap-solana-wallet.git"
88
},
99
"source": {
10-
"shasum": "TTK9syaPsZFKQk016Wz1yNlpXLFYJ/LFiukpGHiXyOE=",
10+
"shasum": "k8LmcKs1C8v0XMNdO+rMMcd9PULlPonVHBprzUmNipU=",
1111
"location": {
1212
"npm": {
1313
"filePath": "dist/bundle.js",

packages/snap/src/core/handlers/onClientRequest/ClientRequestHandler.test.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,7 @@ describe('ClientRequestHandler', () => {
534534
const utf8ToBase64 = (utf8: string): string =>
535535
pipe(utf8, getUtf8Codec().encode, getBase64Codec().decode);
536536

537-
const { address } = MOCK_SOLANA_KEYRING_ACCOUNT_0;
537+
const { id: accountId, address } = MOCK_SOLANA_KEYRING_ACCOUNT_0;
538538
const mockTimestamp = 1736660000;
539539

540540
// Helper function to create a request with a utf8 message. Defaults to a valid rewards message.
@@ -543,9 +543,7 @@ describe('ClientRequestHandler', () => {
543543
id: 1,
544544
method: ClientRequestMethod.SignRewardsMessage,
545545
params: {
546-
account: {
547-
address,
548-
},
546+
accountId,
549547
message: utf8ToBase64(
550548
utf8Message ?? `rewards,${address},${mockTimestamp}`,
551549
),
@@ -570,7 +568,7 @@ describe('ClientRequestHandler', () => {
570568
signatureType: 'ed25519' as const,
571569
};
572570
jest
573-
.spyOn(mockAccountsService, 'findByAddress')
571+
.spyOn(mockAccountsService, 'findById')
574572
.mockResolvedValue(MOCK_SOLANA_KEYRING_ACCOUNT_0);
575573
jest.spyOn(mockWalletService, 'signMessage').mockResolvedValue(response);
576574

@@ -595,7 +593,7 @@ describe('ClientRequestHandler', () => {
595593

596594
it('throws an error if account is not found', async () => {
597595
const invalidAccountRequest = createRequest();
598-
mockAccountsService.findByAddress.mockResolvedValue(null);
596+
mockAccountsService.findById.mockResolvedValue(null);
599597

600598
await expect(handler.handle(invalidAccountRequest)).rejects.toThrow(
601599
'Account not found',
@@ -604,7 +602,7 @@ describe('ClientRequestHandler', () => {
604602

605603
it('throws an error if address in message does not match signing account', async () => {
606604
const signingAccount = MOCK_SOLANA_KEYRING_ACCOUNT_0;
607-
mockAccountsService.findByAddress.mockResolvedValue(signingAccount);
605+
mockAccountsService.findById.mockResolvedValue(signingAccount);
608606

609607
// Use a valid Solana address format but different from the signing account
610608
const differentAddress = MOCK_SOLANA_KEYRING_ACCOUNT_1.address;

packages/snap/src/core/handlers/onClientRequest/ClientRequestHandler.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -297,24 +297,21 @@ export class ClientRequestHandler {
297297
assert(request, SignRewardsMessageRequestStruct);
298298

299299
const {
300-
params: {
301-
account: { address },
302-
message,
303-
},
300+
params: { accountId, message },
304301
} = request;
305302

306-
const account = await this.#accountsService.findByAddress(address);
303+
const account = await this.#accountsService.findById(accountId);
307304
if (!account) {
308-
throw new InvalidParamsError(`Account not found: ${address}`) as Error;
305+
throw new InvalidParamsError(`Account not found: ${accountId}`) as Error;
309306
}
310307

311308
// Parse the rewards message to extract the address
312309
const { address: messageAddress } = parseRewardsMessage(message);
313310

314311
// Validate that the address in the message matches the signing account
315-
if (messageAddress !== address) {
312+
if (messageAddress !== account.address) {
316313
throw new InvalidParamsError(
317-
`Address in rewards message (${messageAddress}) does not match signing account address (${address})`,
314+
`Address in rewards message (${messageAddress}) does not match signing account address (${account.address})`,
318315
) as Error;
319316
}
320317

packages/snap/src/core/handlers/onClientRequest/validation.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -208,9 +208,7 @@ export const RewardsMessageStruct = refine(
208208
);
209209

210210
export const SignRewardsMessageRequestParamsStruct = object({
211-
account: object({
212-
address: SolanaAddressStruct,
213-
}),
211+
accountId: UuidStruct,
214212
message: RewardsMessageStruct,
215213
});
216214

packages/snap/src/core/handlers/onRpcRequest/index.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
eventEmitter,
99
keyring,
1010
} from '../../../snapContext';
11+
import { ClientRequestMethod } from '../onClientRequest';
1112
import { getFeeForTransaction } from './getFeeForTransaction';
1213
import { RpcRequestMethod, TestDappRpcRequestMethod } from './types';
1314

@@ -57,4 +58,14 @@ export const handlers: Record<RpcRequestMethod, OnRpcRequestHandler> = {
5758
await clientRequestHandler.handle(request);
5859
return null;
5960
},
61+
[TestDappRpcRequestMethod.SignRewardsMessage as any]: async ({
62+
request,
63+
}: {
64+
request: JsonRpcRequest;
65+
}) => {
66+
return clientRequestHandler.handle({
67+
...request,
68+
method: ClientRequestMethod.SignRewardsMessage,
69+
});
70+
},
6071
};

packages/snap/src/core/handlers/onRpcRequest/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,5 @@ export enum TestDappRpcRequestMethod {
1616
SynchronizeAccounts = 'synchronizeAccounts',
1717
SetAccountSelected = 'setAccountSelected',
1818
ConfirmSend = 'confirmSend',
19+
SignRewardsMessage = 'signRewardsMessage',
1920
}

packages/snap/src/permissions.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ const dappPermissions = isDev
4646
TestDappRpcRequestMethod.SynchronizeAccounts,
4747
TestDappRpcRequestMethod.SetAccountSelected,
4848
TestDappRpcRequestMethod.ConfirmSend,
49+
TestDappRpcRequestMethod.SignRewardsMessage,
4950
])
5051
: new Set([]);
5152

0 commit comments

Comments
 (0)