Skip to content

Commit 0c68a1d

Browse files
committed
Update entropy example and test-snaps to allow specifying entropy source
1 parent 50821dc commit 0c68a1d

File tree

12 files changed

+167
-21
lines changed

12 files changed

+167
-21
lines changed

packages/examples/packages/get-entropy/snap.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { SnapConfig } from '@metamask/snaps-cli';
22
import { resolve } from 'path';
33

44
const config: SnapConfig = {
5-
input: resolve(__dirname, 'src/index.ts'),
5+
input: resolve(__dirname, 'src/index.tsx'),
66
server: {
77
port: 8009,
88
},

packages/examples/packages/get-entropy/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/snaps.git"
88
},
99
"source": {
10-
"shasum": "lmkCL1vKbBcM9oIiHMkfGq1P7CATFx/fksf+kvlv+eU=",
10+
"shasum": "RAiYJUhEOg3GuNxpzlXbK6OVvBgLTtaS/R14t9oagY8=",
1111
"location": {
1212
"npm": {
1313
"filePath": "dist/bundle.js",

packages/examples/packages/get-entropy/src/index.ts renamed to packages/examples/packages/get-entropy/src/index.tsx

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
11
import type { OnRpcRequestHandler } from '@metamask/snaps-sdk';
22
import {
33
DialogType,
4-
panel,
5-
text,
6-
heading,
7-
copyable,
8-
UserRejectedRequestError,
94
MethodNotFoundError,
5+
UserRejectedRequestError,
106
} from '@metamask/snaps-sdk';
7+
import { Box, Copyable, Heading, Text } from '@metamask/snaps-sdk/jsx';
118
import { bytesToHex, stringToBytes } from '@metamask/utils';
129
import { sign } from '@noble/bls12-381';
1310

1411
import type { SignMessageParams } from './types';
15-
import { getEntropy } from './utils';
12+
import { getEntropy, getEntropySourceName } from './utils';
1613

1714
/**
1815
* Handle incoming JSON-RPC requests from the dapp, sent through the
@@ -31,31 +28,40 @@ import { getEntropy } from './utils';
3128
export const onRpcRequest: OnRpcRequestHandler = async ({ request }) => {
3229
switch (request.method) {
3330
case 'signMessage': {
34-
const { message, salt } = request.params as SignMessageParams;
31+
const { message, salt, source } = request.params as SignMessageParams;
3532

3633
const approved = await snap.request({
3734
method: 'snap_dialog',
3835
params: {
3936
type: DialogType.Confirmation,
40-
content: panel([
41-
heading('Signature request'),
42-
text(
43-
'Do you want to sign the following message with snap entropy?',
44-
),
45-
copyable(message),
46-
]),
37+
content: (
38+
<Box>
39+
<Heading>Signature request</Heading>
40+
<Text>
41+
Do you want to sign the following message with Snap entropy, and
42+
the entropy source "{await getEntropySourceName(source)}"?
43+
</Text>
44+
<Copyable value={message} />
45+
</Box>
46+
),
4747
},
4848
});
4949

5050
if (!approved) {
5151
throw new UserRejectedRequestError();
5252
}
5353

54-
const privateKey = await getEntropy(salt);
54+
const privateKey = await getEntropy(salt, source);
5555
const newLocal = await sign(stringToBytes(message), privateKey);
5656
return bytesToHex(newLocal);
5757
}
5858

59+
case 'getEntropySources': {
60+
return await snap.request({
61+
method: 'snap_listEntropySources',
62+
});
63+
}
64+
5965
default:
6066
throw new MethodNotFoundError({ method: request.method });
6167
}

packages/examples/packages/get-entropy/src/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,10 @@ export type SignMessageParams = {
1515
* Defaults to "Signing key".
1616
*/
1717
salt?: string;
18+
19+
/**
20+
* The entropy source to use for the signature. If not provided, the primary
21+
* entropy source will be used.
22+
*/
23+
source?: string | undefined;
1824
};

packages/examples/packages/get-entropy/src/utils.ts

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,54 @@ import { remove0x } from '@metamask/utils';
1313
*
1414
* @param salt - The salt to use for the entropy derivation. Using a different
1515
* salt will result in completely different entropy being generated.
16+
* @param source - The entropy source to use for the entropy derivation. If not
17+
* provided, the primary entropy source will be used.
1618
* @returns The generated entropy, without the leading "0x".
1719
* @see https://docs.metamask.io/snaps/reference/rpc-api/#snap_getentropy
1820
*/
19-
export async function getEntropy(salt = 'Signing key') {
21+
export async function getEntropy(
22+
salt = 'Signing key',
23+
source?: string | undefined,
24+
) {
2025
const entropy = await snap.request({
2126
method: 'snap_getEntropy',
2227
params: {
2328
version: 1,
2429
salt,
30+
source,
2531
},
2632
});
2733

2834
return remove0x(entropy);
2935
}
36+
37+
/**
38+
* Get the name of an entropy source by its ID using the
39+
* `snap_listEntropySources` JSON-RPC method.
40+
*
41+
* If the ID is not provided, the name of the primary entropy source will be
42+
* returned.
43+
*
44+
* @param id - The ID of the entropy source.
45+
* @returns The name of the entropy source.
46+
*/
47+
export async function getEntropySourceName(id?: string | undefined) {
48+
if (id) {
49+
const sources = await snap.request({
50+
method: 'snap_listEntropySources',
51+
});
52+
53+
const source = sources.find((item) => item.id === id);
54+
if (source) {
55+
if (source.name.length > 0) {
56+
return source.name;
57+
}
58+
59+
return source.id;
60+
}
61+
62+
return 'unknown source';
63+
}
64+
65+
return 'primary source';
66+
}

packages/test-snaps/src/api.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ declare global {
1616

1717
export enum Tag {
1818
Accounts = 'Accounts',
19+
EntropySources = 'Entropy Sources',
1920
InstalledSnaps = 'Installed Snaps',
2021
TestState = 'Test State',
2122
UnencryptedTestState = 'Unencrypted Test State',
Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { FunctionComponent } from 'react';
2+
import { useState } from 'react';
23

3-
import { SignMessage } from './components';
4+
import { EntropySources, SignMessage } from './components';
45
import {
56
GET_ENTROPY_PORT,
67
GET_ENTROPY_SNAP_ID,
@@ -9,6 +10,8 @@ import {
910
import { Snap } from '../../../components';
1011

1112
export const GetEntropy: FunctionComponent = () => {
13+
const [source, setSource] = useState<string | undefined>(undefined);
14+
1215
return (
1316
<Snap
1417
name="Get Entropy Snap"
@@ -17,7 +20,8 @@ export const GetEntropy: FunctionComponent = () => {
1720
version={GET_ENTROPY_VERSION}
1821
testId="GetEntropySnap"
1922
>
20-
<SignMessage />
23+
<EntropySources onChange={setSource} />
24+
<SignMessage source={source} />
2125
</Snap>
2226
);
2327
};
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import type { EntropySource } from '@metamask/snaps-sdk';
2+
import type { ChangeEvent, FunctionComponent } from 'react';
3+
4+
import { Result } from '../../../../components';
5+
import { useEntropySources } from '../hooks';
6+
7+
export type EntropySourcesProps = {
8+
onChange: (source: string) => void;
9+
};
10+
11+
/**
12+
* Get the name of the source.
13+
*
14+
* @param source - The source.
15+
* @returns The name of the source.
16+
*/
17+
function getSourceName(source: EntropySource) {
18+
const name = source.name.length === 0 ? source.id : source.name;
19+
20+
if (source.primary) {
21+
return `${name} (primary)`;
22+
}
23+
24+
return name;
25+
}
26+
27+
export const EntropySources: FunctionComponent<EntropySourcesProps> = ({
28+
onChange,
29+
}) => {
30+
const entropySources = useEntropySources();
31+
32+
const handleChange = (event: ChangeEvent<HTMLSelectElement>) => {
33+
onChange(event.target.value);
34+
};
35+
36+
return (
37+
<>
38+
<h3 className="h5">Entropy source</h3>
39+
<select
40+
id="select-chain"
41+
className="form-select mb-3"
42+
onChange={handleChange}
43+
>
44+
<option value="" disabled={true}>
45+
Select an entropy source
46+
</option>
47+
{entropySources?.map((source) => (
48+
<option key={source.id} value={source.id}>
49+
{getSourceName(source)}
50+
</option>
51+
))}
52+
</select>
53+
<Result className="mb-3">
54+
<pre id="entropySourcesResult" style={{ margin: 0 }}>
55+
{JSON.stringify(entropySources, null, 2)}
56+
</pre>
57+
</Result>
58+
</>
59+
);
60+
};

packages/test-snaps/src/features/snaps/get-entropy/components/SignMessage.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,13 @@ import { Result } from '../../../../components';
88
import { getSnapId } from '../../../../utils';
99
import { GET_ENTROPY_SNAP_ID, GET_ENTROPY_PORT } from '../constants';
1010

11-
export const SignMessage: FunctionComponent = () => {
11+
export type SignMessageProps = {
12+
source: string | undefined;
13+
};
14+
15+
export const SignMessage: FunctionComponent<SignMessageProps> = ({
16+
source,
17+
}) => {
1218
const [message, setMessage] = useState('');
1319
const [invokeSnap, { isLoading, data, error }] = useInvokeMutation();
1420

@@ -24,6 +30,7 @@ export const SignMessage: FunctionComponent = () => {
2430
method: 'signMessage',
2531
params: {
2632
message,
33+
...(source !== undefined && { source }),
2734
},
2835
}).catch(logError);
2936
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
export * from './EntropySources';
12
export * from './SignMessage';

0 commit comments

Comments
 (0)