Skip to content

Commit 2e1513a

Browse files
committed
Add onAssetHistoricalPrice handler
1 parent 4cd200d commit 2e1513a

File tree

25 files changed

+645
-104
lines changed

25 files changed

+645
-104
lines changed

packages/snaps-controllers/src/snaps/SnapController.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ import {
103103
getLocalizedSnapManifest,
104104
MAX_FILE_SIZE,
105105
OnSettingsPageResponseStruct,
106+
OnAssetHistoricalPriceResponseStruct,
106107
} from '@metamask/snaps-utils';
107108
import type {
108109
Json,
@@ -3814,6 +3815,9 @@ export class SnapController extends BaseController<
38143815
case HandlerType.OnAssetsConversion:
38153816
assertStruct(result, OnAssetsConversionResponseStruct);
38163817
break;
3818+
case HandlerType.OnAssetHistoricalPrice:
3819+
assertStruct(result, OnAssetHistoricalPriceResponseStruct);
3820+
break;
38173821
default:
38183822
break;
38193823
}
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
2-
"branches": 80.53,
3-
"functions": 88.96,
4-
"lines": 90.69,
5-
"statements": 89.74
2+
"branches": 80,
3+
"functions": 89.1,
4+
"lines": 90.64,
5+
"statements": 89.59
66
}

packages/snaps-execution-environments/src/common/BaseSnapExecutor.test.browser.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1473,6 +1473,60 @@ describe('BaseSnapExecutor', () => {
14731473
});
14741474
});
14751475

1476+
it('supports `onAssetHistoricalPrice` export', async () => {
1477+
const CODE = `
1478+
module.exports.onAssetHistoricalPrice = () => ({ historicalPrice: {
1479+
intervals: {
1480+
'P1D': [
1481+
[1635724800000, "1"],
1482+
]
1483+
},
1484+
updateTime: 1635724800000,
1485+
} });
1486+
`;
1487+
1488+
const executor = new TestSnapExecutor();
1489+
await executor.executeSnap(1, MOCK_SNAP_ID, CODE, []);
1490+
1491+
expect(await executor.readCommand()).toStrictEqual({
1492+
jsonrpc: '2.0',
1493+
id: 1,
1494+
result: 'OK',
1495+
});
1496+
1497+
await executor.writeCommand({
1498+
jsonrpc: '2.0',
1499+
id: 2,
1500+
method: 'snapRpc',
1501+
params: [
1502+
MOCK_SNAP_ID,
1503+
HandlerType.OnAssetHistoricalPrice,
1504+
MOCK_ORIGIN,
1505+
{
1506+
jsonrpc: '2.0',
1507+
method: '',
1508+
params: {
1509+
from: 'bip122:000000000019d6689c085ae165831e93/slip44:0',
1510+
to: 'swift:0/iso4217:USD',
1511+
},
1512+
},
1513+
],
1514+
});
1515+
1516+
expect(await executor.readCommand()).toStrictEqual({
1517+
id: 2,
1518+
jsonrpc: '2.0',
1519+
result: {
1520+
historicalPrice: {
1521+
intervals: {
1522+
P1D: [[1635724800000, '1']],
1523+
},
1524+
updateTime: 1635724800000,
1525+
},
1526+
},
1527+
});
1528+
});
1529+
14761530
it('supports `onAssetsLookup` export', async () => {
14771531
const CODE = `
14781532
module.exports.onAssetsLookup = () => ({ assets: {} });

packages/snaps-execution-environments/src/common/commands.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,17 @@ describe('getHandlerArguments', () => {
4141
).toThrow('Invalid request params');
4242
});
4343

44+
it('validates the request params for the OnAssetHistoricalPrice handler', () => {
45+
expect(() =>
46+
getHandlerArguments(MOCK_ORIGIN, HandlerType.OnAssetHistoricalPrice, {
47+
id: 1,
48+
jsonrpc: '2.0',
49+
method: 'foo',
50+
params: {},
51+
}),
52+
).toThrow('Invalid request params');
53+
});
54+
4455
it('throws for invalid handler types', () => {
4556
expect(() =>
4657
// @ts-expect-error Invalid handler type.

packages/snaps-execution-environments/src/common/commands.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
assertIsOnAssetsLookupRequestArguments,
1919
assertIsOnAssetsConversionRequestArguments,
2020
assertIsOnProtocolRequestArguments,
21+
assertIsOnAssetHistoricalPriceRequestArguments,
2122
} from './validation';
2223

2324
export type CommandMethodsMapping = {
@@ -59,6 +60,13 @@ export function getHandlerArguments(
5960
const { signature, signatureOrigin } = request.params;
6061
return { signature, signatureOrigin };
6162
}
63+
64+
case HandlerType.OnAssetHistoricalPrice: {
65+
assertIsOnAssetHistoricalPriceRequestArguments(request.params);
66+
const { from, to } = request.params;
67+
return { from, to };
68+
}
69+
6270
case HandlerType.OnAssetsLookup: {
6371
assertIsOnAssetsLookupRequestArguments(request.params);
6472
const { assets } = request.params;

packages/snaps-execution-environments/src/common/validation.test.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { UserInputEventType } from '@metamask/snaps-sdk';
22

33
import {
4+
assertIsOnAssetHistoricalPriceRequestArguments,
45
assertIsOnAssetsConversionRequestArguments,
56
assertIsOnAssetsLookupRequestArguments,
67
assertIsOnNameLookupRequestArguments,
@@ -378,3 +379,48 @@ describe('assertIsOnProtocolRequestArguments', () => {
378379
},
379380
);
380381
});
382+
383+
describe('assertIsOnAssetHistoricalPriceRequestArguments', () => {
384+
it.each([
385+
{
386+
from: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501',
387+
to: 'swift:0/iso4217:USD',
388+
},
389+
])(
390+
'does not throw for a valid asset historical price request object',
391+
(args) => {
392+
expect(() =>
393+
assertIsOnAssetHistoricalPriceRequestArguments(args),
394+
).not.toThrow();
395+
},
396+
);
397+
398+
it.each([
399+
true,
400+
false,
401+
null,
402+
undefined,
403+
0,
404+
1,
405+
'',
406+
'foo',
407+
[],
408+
{},
409+
{ from: [], to: 'swift:0/iso4217:USD' },
410+
{ to: 'swift:0/iso4217:USD', from: 'foo' },
411+
{ from: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501' },
412+
{ to: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501' },
413+
{
414+
from: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501',
415+
to: 'swift:0/iso4217:USD',
416+
foo: 'bar',
417+
},
418+
])(
419+
'throws if the value is not a valid asset historical price request object',
420+
(value) => {
421+
expect(() =>
422+
assertIsOnAssetHistoricalPriceRequestArguments(value as any),
423+
).toThrow('Invalid request params:');
424+
},
425+
);
426+
});

packages/snaps-execution-environments/src/common/validation.ts

Lines changed: 49 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,25 @@ export type RequestArguments =
124124
| ExecuteSnapRequestArguments
125125
| SnapRpcRequestArguments;
126126

127+
/**
128+
* Asserts that the given value is a valid request arguments object.
129+
*
130+
* @param value - The value to validate.
131+
* @param requestArgumentsStruct - The struct to validate the value against.
132+
* @throws If the value is not a valid request arguments object.
133+
*/
134+
function assertRequestArguments<Type, Schema>(
135+
value: unknown,
136+
requestArgumentsStruct: Struct<Type, Schema>,
137+
): asserts value is Struct<Type, Schema> {
138+
assertStruct(
139+
value,
140+
requestArgumentsStruct,
141+
'Invalid request params',
142+
rpcErrors.invalidParams,
143+
);
144+
}
145+
127146
export const OnTransactionRequestArgumentsStruct = object({
128147
// TODO: Improve `transaction` type.
129148
transaction: record(string(), JsonStruct),
@@ -146,12 +165,7 @@ export type OnTransactionRequestArguments = Infer<
146165
export function assertIsOnTransactionRequestArguments(
147166
value: unknown,
148167
): asserts value is OnTransactionRequestArguments {
149-
assertStruct(
150-
value,
151-
OnTransactionRequestArgumentsStruct,
152-
'Invalid request params',
153-
rpcErrors.invalidParams,
154-
);
168+
assertRequestArguments(value, OnTransactionRequestArgumentsStruct);
155169
}
156170

157171
export const OnSignatureRequestArgumentsStruct = object({
@@ -174,12 +188,7 @@ export type OnSignatureRequestArguments = Infer<
174188
export function assertIsOnSignatureRequestArguments(
175189
value: unknown,
176190
): asserts value is OnSignatureRequestArguments {
177-
assertStruct(
178-
value,
179-
OnSignatureRequestArgumentsStruct,
180-
'Invalid request params',
181-
rpcErrors.invalidParams,
182-
);
191+
assertRequestArguments(value, OnSignatureRequestArgumentsStruct);
183192
}
184193

185194
const baseNameLookupArgs = { chainId: CaipChainIdStruct };
@@ -217,12 +226,30 @@ export type PossibleLookupRequestArgs = typeof baseNameLookupArgs & {
217226
export function assertIsOnNameLookupRequestArguments(
218227
value: unknown,
219228
): asserts value is OnNameLookupRequestArguments {
220-
assertStruct(
221-
value,
222-
OnNameLookupRequestArgumentsStruct,
223-
'Invalid request params',
224-
rpcErrors.invalidParams,
225-
);
229+
assertRequestArguments(value, OnNameLookupRequestArgumentsStruct);
230+
}
231+
232+
export const OnAssetHistoricalPriceRequestArgumentsStruct = object({
233+
from: CaipAssetTypeStruct,
234+
to: CaipAssetTypeStruct,
235+
});
236+
237+
export type OnAssetHistoricalPriceRequestArguments = Infer<
238+
typeof OnAssetHistoricalPriceRequestArgumentsStruct
239+
>;
240+
241+
/**
242+
* Asserts that the given value is a valid {@link OnAssetHistoricalPriceRequestArguments}
243+
* object.
244+
*
245+
* @param value - The value to validate.
246+
* @throws If the value is not a valid {@link OnAssetHistoricalPriceRequestArguments}
247+
* object.
248+
*/
249+
export function assertIsOnAssetHistoricalPriceRequestArguments(
250+
value: unknown,
251+
): asserts value is OnAssetHistoricalPriceRequestArguments {
252+
assertRequestArguments(value, OnAssetHistoricalPriceRequestArgumentsStruct);
226253
}
227254

228255
export const OnAssetsLookupRequestArgumentsStruct = object({
@@ -244,12 +271,7 @@ export type OnAssetsLookupRequestArguments = Infer<
244271
export function assertIsOnAssetsLookupRequestArguments(
245272
value: unknown,
246273
): asserts value is OnAssetsLookupRequestArguments {
247-
assertStruct(
248-
value,
249-
OnAssetsLookupRequestArgumentsStruct,
250-
'Invalid request params',
251-
rpcErrors.invalidParams,
252-
);
274+
assertRequestArguments(value, OnAssetsLookupRequestArgumentsStruct);
253275
}
254276

255277
export const OnAssetsConversionRequestArgumentsStruct = object({
@@ -280,12 +302,7 @@ export type OnAssetsConversionRequestArguments = Infer<
280302
export function assertIsOnAssetsConversionRequestArguments(
281303
value: unknown,
282304
): asserts value is OnAssetsConversionRequestArguments {
283-
assertStruct(
284-
value,
285-
OnAssetsConversionRequestArgumentsStruct,
286-
'Invalid request params',
287-
rpcErrors.invalidParams,
288-
);
305+
assertRequestArguments(value, OnAssetsConversionRequestArgumentsStruct);
289306
}
290307

291308
export const OnUserInputArgumentsStruct = object({
@@ -307,12 +324,7 @@ export type OnUserInputArguments = Infer<typeof OnUserInputArgumentsStruct>;
307324
export function assertIsOnUserInputRequestArguments(
308325
value: unknown,
309326
): asserts value is OnUserInputArguments {
310-
assertStruct(
311-
value,
312-
OnUserInputArgumentsStruct,
313-
'Invalid request params',
314-
rpcErrors.invalidParams,
315-
);
327+
assertRequestArguments(value, OnUserInputArgumentsStruct);
316328
}
317329

318330
export const OnProtocolRequestArgumentsStruct = object({
@@ -335,12 +347,7 @@ export type OnProtocolRequestArguments = Infer<
335347
export function assertIsOnProtocolRequestArguments(
336348
value: unknown,
337349
): asserts value is OnProtocolRequestArguments {
338-
assertStruct(
339-
value,
340-
OnProtocolRequestArgumentsStruct,
341-
'Invalid request params',
342-
rpcErrors.invalidParams,
343-
);
350+
assertRequestArguments(value, OnProtocolRequestArgumentsStruct);
344351
}
345352

346353
// TODO: Either fix this lint violation or explain why it's necessary to ignore.

packages/snaps-rpc-methods/jest.config.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ module.exports = deepmerge(baseConfig, {
1010
],
1111
coverageThreshold: {
1212
global: {
13-
branches: 94.98,
14-
functions: 98.64,
15-
lines: 98.76,
16-
statements: 98.45,
13+
branches: 94.95,
14+
functions: 98.62,
15+
lines: 98.75,
16+
statements: 98.43,
1717
},
1818
},
1919
});

packages/snaps-rpc-methods/package.json

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,7 @@
6262
"@metamask/snaps-utils": "workspace:^",
6363
"@metamask/superstruct": "^3.1.0",
6464
"@metamask/utils": "^11.2.0",
65-
"@noble/hashes": "^1.7.1",
66-
"luxon": "^3.5.0"
65+
"@noble/hashes": "^1.7.1"
6766
},
6867
"devDependencies": {
6968
"@lavamoat/allow-scripts": "^3.0.4",
@@ -72,7 +71,6 @@
7271
"@swc/core": "1.3.78",
7372
"@swc/jest": "^0.2.26",
7473
"@ts-bridge/cli": "^0.6.1",
75-
"@types/luxon": "^3",
7674
"@types/node": "18.14.2",
7775
"deepmerge": "^4.2.2",
7876
"depcheck": "^1.4.7",

packages/snaps-rpc-methods/src/endowments/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ export const handlerEndowments: Record<HandlerType, string | null> = {
126126
[HandlerType.OnSettingsPage]: settingsPageEndowmentBuilder.targetName,
127127
[HandlerType.OnSignature]: signatureInsightEndowmentBuilder.targetName,
128128
[HandlerType.OnUserInput]: null,
129+
[HandlerType.OnAssetHistoricalPrice]: assetsEndowmentBuilder.targetName,
129130
[HandlerType.OnAssetsLookup]: assetsEndowmentBuilder.targetName,
130131
[HandlerType.OnAssetsConversion]: assetsEndowmentBuilder.targetName,
131132
[HandlerType.OnProtocolRequest]: protocolEndowmentBuilder.targetName,

0 commit comments

Comments
 (0)