Skip to content

Commit a4bbcac

Browse files
committed
Add SIP-30 support to snaps-simulation
1 parent a6367db commit a4bbcac

File tree

13 files changed

+290
-90
lines changed

13 files changed

+290
-90
lines changed

packages/examples/packages/get-entropy/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,4 @@ JSON-RPC methods:
3232
`BLS12-381` elliptic curve to sign the message.
3333

3434
For more information, you can refer to
35-
[the end-to-end tests](./src/index.test.ts).
35+
[the end-to-end tests](./src/index.test.tsx).

packages/examples/packages/get-entropy/src/index.test.ts

Lines changed: 0 additions & 80 deletions
This file was deleted.
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import { expect } from '@jest/globals';
2+
import { installSnap } from '@metamask/snaps-jest';
3+
import { Box, Copyable, Heading, Text } from '@metamask/snaps-sdk/jsx';
4+
import { assert } from '@metamask/utils';
5+
6+
describe('onRpcRequest', () => {
7+
it('throws an error if the requested method does not exist', async () => {
8+
const { request } = await installSnap();
9+
10+
const response = await request({
11+
method: 'foo',
12+
});
13+
14+
expect(response).toRespondWithError({
15+
code: -32601,
16+
message: 'The method does not exist / is not available.',
17+
stack: expect.any(String),
18+
data: {
19+
method: 'foo',
20+
cause: null,
21+
},
22+
});
23+
});
24+
25+
describe('signMessage', () => {
26+
it('signs a message with the snap entropy', async () => {
27+
const { request } = await installSnap();
28+
29+
const response = request({
30+
method: 'signMessage',
31+
params: {
32+
message: 'Hello, world!',
33+
},
34+
});
35+
36+
const ui = await response.getInterface();
37+
expect(ui).toRender(
38+
<Box>
39+
<Heading>Signature request</Heading>
40+
<Text>
41+
Do you want to sign the following message with Snap entropy, and the
42+
entropy source "{'Primary source'}"?
43+
</Text>
44+
<Copyable value="Hello, world!" />
45+
</Box>,
46+
);
47+
48+
assert(ui.type === 'confirmation');
49+
await ui.ok();
50+
51+
expect(await response).toRespondWith(
52+
'0x8b3f38050fb60fffd2e0e2ef04504b09e8f0ff46e25896cfd87ced67a5a76ac75c534c9bafbf6f38b6e50b969e1239c80916040de30a3f9ee973d6a3281d39624e7d463b2a5bc0165764b0b4ce8ad009352076c54a202a8c63554b00a46872dc',
53+
);
54+
});
55+
56+
it('signs a message with a different salt', async () => {
57+
const { request } = await installSnap();
58+
59+
const response = request({
60+
method: 'signMessage',
61+
params: {
62+
message: 'Hello, world!',
63+
salt: 'Other salt',
64+
},
65+
});
66+
67+
const ui = await response.getInterface();
68+
expect(ui).toRender(
69+
<Box>
70+
<Heading>Signature request</Heading>
71+
<Text>
72+
Do you want to sign the following message with Snap entropy, and the
73+
entropy source "{'Primary source'}"?
74+
</Text>
75+
<Copyable value="Hello, world!" />
76+
</Box>,
77+
);
78+
79+
assert(ui.type === 'confirmation');
80+
await ui.ok();
81+
82+
expect(await response).toRespondWith(
83+
'0x877530880baa4d1fc1fca749f5a26123275ffaa617505cae8f3da4a58d06ea43b7123d4575331dd15ffd5103ed2091050af0aa715adc3b7e122c8e07a97b7fce76c34e8e2ef0037b36015795e0ae530fed264ffb4b33bd47149af192f4c51411',
84+
);
85+
});
86+
87+
it('signs a message with a different entropy source', async () => {
88+
const { request } = await installSnap();
89+
90+
const response = request({
91+
method: 'signMessage',
92+
params: {
93+
message: 'Hello, world!',
94+
source: 'alternative',
95+
},
96+
});
97+
98+
const ui = await response.getInterface();
99+
expect(ui).toRender(
100+
<Box>
101+
<Heading>Signature request</Heading>
102+
<Text>
103+
Do you want to sign the following message with Snap entropy, and the
104+
entropy source "{'Alternative Secret Recovery Phrase'}"?
105+
</Text>
106+
<Copyable value="Hello, world!" />
107+
</Box>,
108+
);
109+
110+
assert(ui.type === 'confirmation');
111+
await ui.ok();
112+
113+
expect(await response).toRespondWith(
114+
'0xad9bff2fc10e412b1dc3e2f88bc2c0da3c994c4a75cd59b1a92ef18bfd24af459aad5a6355d3030cf44cd52486dc274419177820fdc44b86842a043a3da5aa3a5c07990265891dc871a7cd341b1771282aa042a024810f17ecb6929d731a4013',
115+
);
116+
});
117+
});
118+
119+
describe('getEntropySources', () => {
120+
it('returns the entropy sources', async () => {
121+
const { request } = await installSnap();
122+
123+
const response = request({
124+
method: 'getEntropySources',
125+
});
126+
127+
expect(await response).toRespondWith([
128+
{
129+
id: 'default',
130+
name: 'Default Secret Recovery Phrase',
131+
type: 'mnemonic',
132+
primary: true,
133+
},
134+
{
135+
id: 'alternative',
136+
name: 'Alternative Secret Recovery Phrase',
137+
type: 'mnemonic',
138+
primary: false,
139+
},
140+
]);
141+
});
142+
});
143+
});

packages/snaps-simulation/src/constants.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@
55
export const DEFAULT_SRP =
66
'test test test test test test test test test test test ball';
77

8+
/**
9+
* An alternative secret recovery phrase that is used for testing purposes. Do
10+
* not use this to store any real funds!
11+
*/
12+
export const DEFAULT_ALTERNATIVE_SRP =
13+
'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about';
14+
815
/**
916
* The default locale.
1017
*/
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { getGetEntropySourcesImplementation } from './get-entropy-sources';
2+
3+
describe('getGetEntropySourcesImplementation', () => {
4+
it('returns the implementation of the `getEntropySources` hook', async () => {
5+
const fn = getGetEntropySourcesImplementation();
6+
7+
expect(fn()).toStrictEqual([
8+
{
9+
id: 'entropy-source-1',
10+
name: 'Entropy Source 1',
11+
type: 'mnemonic',
12+
primary: true,
13+
},
14+
]);
15+
});
16+
});
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/**
2+
* Get the implementation of the `getEntropySources` hook.
3+
*
4+
* @returns The implementation of the `getEntropySources` hook. Right now, it
5+
* only returns a single hard coded entropy source. In the future, it could
6+
* return a configurable list of entropy sources.
7+
*/
8+
export function getGetEntropySourcesImplementation() {
9+
return () => {
10+
return [
11+
{
12+
id: 'default',
13+
name: 'Default Secret Recovery Phrase',
14+
type: 'mnemonic' as const,
15+
primary: true,
16+
},
17+
{
18+
id: 'alternative',
19+
name: 'Alternative Secret Recovery Phrase',
20+
type: 'mnemonic' as const,
21+
primary: false,
22+
},
23+
];
24+
};
25+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { mnemonicPhraseToBytes } from '@metamask/key-tree';
2+
3+
import { getGetMnemonicImplementation } from './get-mnemonic';
4+
import {
5+
DEFAULT_ALTERNATIVE_SRP,
6+
DEFAULT_SRP,
7+
} from '@metamask/snaps-simulation';
8+
9+
describe('getGetMnemonicImplementation', () => {
10+
it('returns the default mnemonic phrase', async () => {
11+
const getMnemonic = getGetMnemonicImplementation();
12+
expect(await getMnemonic()).toStrictEqual(
13+
mnemonicPhraseToBytes(DEFAULT_SRP),
14+
);
15+
16+
expect(await getMnemonic('default')).toStrictEqual(
17+
mnemonicPhraseToBytes(DEFAULT_SRP),
18+
);
19+
});
20+
21+
it('returns the provided default mnemonic phrase', async () => {
22+
const getMnemonic = getGetMnemonicImplementation(DEFAULT_ALTERNATIVE_SRP);
23+
expect(await getMnemonic()).toStrictEqual(
24+
mnemonicPhraseToBytes(DEFAULT_ALTERNATIVE_SRP),
25+
);
26+
27+
expect(await getMnemonic('default')).toStrictEqual(
28+
mnemonicPhraseToBytes(DEFAULT_ALTERNATIVE_SRP),
29+
);
30+
});
31+
32+
it('returns the alternative mnemonic phrase', async () => {
33+
const getMnemonic = getGetMnemonicImplementation();
34+
expect(await getMnemonic('alternative')).toStrictEqual(
35+
mnemonicPhraseToBytes(DEFAULT_ALTERNATIVE_SRP),
36+
);
37+
});
38+
39+
it('throws an error for an unknown entropy source', async () => {
40+
const getMnemonic = getGetMnemonicImplementation();
41+
await expect(getMnemonic('unknown')).rejects.toThrow(
42+
'Unknown entropy source: "unknown".',
43+
);
44+
});
45+
});
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { mnemonicPhraseToBytes } from '@metamask/key-tree';
2+
3+
import { DEFAULT_ALTERNATIVE_SRP, DEFAULT_SRP } from '../../constants';
4+
5+
/**
6+
* Get the implementation of the `getMnemonic` method.
7+
*
8+
* @param defaultSecretRecoveryPhrase - The default secret recovery phrase to
9+
* use.
10+
* @returns The implementation of the `getMnemonic` method.
11+
*/
12+
export function getGetMnemonicImplementation(
13+
defaultSecretRecoveryPhrase: string = DEFAULT_SRP,
14+
) {
15+
return async (source?: string | undefined): Promise<Uint8Array> => {
16+
if (!source) {
17+
return mnemonicPhraseToBytes(defaultSecretRecoveryPhrase);
18+
}
19+
20+
switch (source) {
21+
case 'default':
22+
return mnemonicPhraseToBytes(defaultSecretRecoveryPhrase);
23+
case 'alternative':
24+
return mnemonicPhraseToBytes(DEFAULT_ALTERNATIVE_SRP);
25+
default:
26+
throw new Error(`Unknown entropy source: "${source}".`);
27+
}
28+
};
29+
}

packages/snaps-simulation/src/methods/hooks/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
export * from './get-entropy-sources';
2+
export * from './get-mnemonic';
13
export * from './get-preferences';
24
export * from './interface';
35
export * from './notifications';

0 commit comments

Comments
 (0)