Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "Qbr3VIystF/tEKyXESJ3ugB8m7n+E0yH8FGCx8od0lA=",
"shasum": "ipeiQKWLZEg5Snm73jZkQ7VeusFnAMPd4ws5zWo7/NI=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/examples/packages/browserify/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "KiWycLXvfEReGjt8jp3UpL4OZj1o5sDKMO6IXtwOZIQ=",
"shasum": "uPtVkZpOxZzVcTSWX40MuXbED38DLAf9hNiNAR88+10=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
100 changes: 100 additions & 0 deletions packages/snaps-controllers/src/snaps/SnapController.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4431,6 +4431,106 @@ describe('SnapController', () => {

snapController.destroy();
});

it('returns the value when `onAssetsConversion` returns a valid response with market data', async () => {
const rootMessenger = getControllerMessenger();
const messenger = getSnapControllerMessenger(rootMessenger);
const snapController = getSnapController(
getSnapControllerOptions({
messenger,
state: {
snaps: getPersistedSnapsState(),
},
}),
);

rootMessenger.registerActionHandler(
'PermissionController:getPermissions',
() => ({
[SnapEndowments.Assets]: {
caveats: [
{
type: SnapCaveatType.ChainIds,
value: ['bip122:000000000019d6689c085ae165831e93'],
},
],
date: 1664187844588,
id: 'izn0WGUO8cvq_jqvLQuQP',
invoker: MOCK_SNAP_ID,
parentCapability: SnapEndowments.Assets,
},
}),
);

rootMessenger.registerActionHandler(
'SubjectMetadataController:getSubjectMetadata',
() => MOCK_SNAP_SUBJECT_METADATA,
);

rootMessenger.registerActionHandler(
'ExecutionService:handleRpcRequest',
async () =>
Promise.resolve({
conversionRates: {
'bip122:000000000019d6689c085ae165831e93/slip44:0': {
'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501': {
rate: '400',
conversionTime: 1737548790,
marketData: {
marketCap: '123',
totalVolume: '123',
circulatingSupply: '123',
allTimeHigh: '123',
allTimeLow: '123',
pricePercentChange: { all: 1.23 },
},
},
},
},
}),
);

expect(
await snapController.handleRequest({
snapId: MOCK_SNAP_ID,
origin: MOCK_ORIGIN,
handler: HandlerType.OnAssetsConversion,
request: {
jsonrpc: '2.0',
method: ' ',
params: {
conversions: [
{
from: 'bip122:000000000019d6689c085ae165831e93/slip44:0',
to: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501',
},
],
includeMarketData: true,
},
id: 1,
},
}),
).toStrictEqual({
conversionRates: {
'bip122:000000000019d6689c085ae165831e93/slip44:0': {
'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501': {
rate: '400',
conversionTime: 1737548790,
marketData: {
marketCap: '123',
totalVolume: '123',
circulatingSupply: '123',
allTimeHigh: '123',
allTimeLow: '123',
pricePercentChange: { all: 1.23 },
},
},
},
},
});

snapController.destroy();
});
});

describe('onAssetHistoricalPrice', () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/snaps-controllers/src/snaps/SnapController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ import type {
import {
AuxiliaryFileEncoding,
getErrorMessage,
OnAssetsConversionResponseStruct,
OnAssetsLookupResponseStruct,
} from '@metamask/snaps-sdk';
import type {
Expand Down Expand Up @@ -105,6 +104,7 @@ import {
OnSettingsPageResponseStruct,
isValidUrl,
OnAssetHistoricalPriceResponseStruct,
OnAssetsConversionResponseStruct,
} from '@metamask/snaps-utils';
import type {
Json,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1608,6 +1608,51 @@ describe('BaseSnapExecutor', () => {
});
});

it('supports `onAssetsConversion` export with the market data flag', async () => {
const CODE = `
module.exports.onAssetsConversion = () => ({ conversionRates: {} });
`;

const executor = new TestSnapExecutor();
await executor.executeSnap(1, MOCK_SNAP_ID, CODE, []);

expect(await executor.readCommand()).toStrictEqual({
jsonrpc: '2.0',
id: 1,
result: 'OK',
});

await executor.writeCommand({
jsonrpc: '2.0',
id: 2,
method: 'snapRpc',
params: [
MOCK_SNAP_ID,
HandlerType.OnAssetsConversion,
MOCK_ORIGIN,
{
jsonrpc: '2.0',
method: '',
params: {
conversions: [
{
from: 'bip122:000000000019d6689c085ae165831e93/slip44:0',
to: 'eip155:1/slip44:60',
},
],
includeMarketData: true,
},
},
],
});

expect(await executor.readCommand()).toStrictEqual({
id: 2,
jsonrpc: '2.0',
result: { conversionRates: {} },
});
});

it('supports onSignature export', async () => {
const CODE = `
module.exports.onSignature = ({ signature, signatureOrigin }) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,24 @@ describe('assertIsOnAssetsConversionRequestArguments', () => {
},
],
},
{
conversions: [
{
from: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501',
to: 'bip122:000000000019d6689c085ae165831e93/slip44:0',
},
],
includeMarketData: true,
},
{
conversions: [
{
from: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501',
to: 'bip122:000000000019d6689c085ae165831e93/slip44:0',
},
],
includeMarketData: false,
},
])('does not throw for a valid assets conversion param object', (value) => {
expect(() =>
assertIsOnAssetsConversionRequestArguments(value),
Expand All @@ -305,6 +323,8 @@ describe('assertIsOnAssetsConversionRequestArguments', () => {
{ conversions: [{}] },
{ conversions: [{ from: 'foo' }] },
{ conversions: [{ from: 'foo', to: 'foo' }] },
{ includeMarketData: true },
{ includeMarketData: false },
])(
'throws if the value is not a valid assets conversion params object',
(value) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
any,
array,
assign,
boolean,
enums,
is,
literal,
Expand Down Expand Up @@ -285,6 +286,7 @@ export const OnAssetsConversionRequestArgumentsStruct = object({
1,
Infinity,
),
includeMarketData: optional(boolean()),
});

export type OnAssetsConversionRequestArguments = Infer<
Expand Down
24 changes: 0 additions & 24 deletions packages/snaps-sdk/src/types/handlers/assets-conversion.test.ts

This file was deleted.

69 changes: 46 additions & 23 deletions packages/snaps-sdk/src/types/handlers/assets-conversion.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,59 @@
import type { Infer } from '@metamask/superstruct';
import {
number,
object,
string,
optional,
record,
nullable,
} from '@metamask/superstruct';
import { CaipAssetTypeStruct, type CaipAssetType } from '@metamask/utils';
import { type CaipAssetType } from '@metamask/utils';

export const AssetConversionStruct = object({
rate: string(),
conversionTime: number(),
expirationTime: optional(number()),
});

export const OnAssetsConversionResponseStruct = object({
conversionRates: record(
CaipAssetTypeStruct,
record(CaipAssetTypeStruct, nullable(AssetConversionStruct)),
),
});
/**
* The market data for an asset.
*
* @property marketCap - The market capitalization of the asset.
* @property totalVolume - The total volume of the asset.
* @property circulatingSupply - The circulating supply of the asset.
* @property allTimeHigh - The all-time high price of the asset.
* @property allTimeLow - The all-time low price of the asset.
* @property pricePercentChange - The percentage change in price over different intervals.
* @property pricePercentChange.interval - The time interval for the price change as a ISO 8601 duration
* or the string "all" to represent the all-time change.
*/
export type MarketData = {
marketCap: string;
totalVolume: string;
circulatingSupply: string;
allTimeHigh: string;
allTimeLow: string;
pricePercentChange: {
[interval: string]: number;
};
};

export type AssetConversion = Infer<typeof AssetConversionStruct>;
/**
* The conversion rate between two assets.
*
* @property rate - The conversion rate between the two assets.
* @property marketData - The market data for the asset, if requested.
* @property conversionTime - The time at which the conversion rate was calculated.
* @property expirationTime - The time at which the conversion rate expires.
*/
export type AssetConversion = {
rate: string;
marketData?: MarketData;
conversionTime: number;
expirationTime?: number;
};

/**
* The arguments for the `onAssetsConversion` handler.
*
* @property conversions - An array of objects containing the `from` and `to` asset types.
* @property includeMarketData - Whether to include market data in the response.
*/
export type OnAssetsConversionArguments = {
conversions: { from: CaipAssetType; to: CaipAssetType }[];
includeMarketData?: boolean;
};

/**
* The `onAssetsConversion` handler. This is called by MetaMask when querying about asset conversion on specific chains.
*
* @param args - The arguments for the handler.
* see {@link OnAssetsConversionArguments}.
* @returns The conversion for each asset. See
* {@link OnAssetsConversionResponse}.
*/
Expand Down
2 changes: 1 addition & 1 deletion packages/snaps-sdk/src/types/handlers/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export type * from './asset-historical-price';
export * from './assets-conversion';
export type * from './assets-conversion';
export * from './assets-lookup';
export type * from './cronjob';
export type * from './home-page';
Expand Down
4 changes: 2 additions & 2 deletions packages/snaps-utils/coverage.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"branches": 99.75,
"functions": 98.95,
"lines": 98.56,
"statements": 97.05
"lines": 98.57,
"statements": 97.07
}
Loading
Loading