@metamask/snaps-rpc-methods
Version:
MetaMask Snaps JSON-RPC method implementations
154 lines • 7.07 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getValidatedParams = exports.getImplementation = exports.notifyBuilder = exports.specificationBuilder = void 0;
const permission_controller_1 = require("@metamask/permission-controller");
const rpc_errors_1 = require("@metamask/rpc-errors");
const snaps_sdk_1 = require("@metamask/snaps-sdk");
const snaps_utils_1 = require("@metamask/snaps-utils");
const superstruct_1 = require("@metamask/superstruct");
const utils_1 = require("@metamask/utils");
const methodName = 'snap_notify';
const NativeNotificationStruct = (0, superstruct_1.object)({
type: (0, snaps_sdk_1.enumValue)(snaps_sdk_1.NotificationType.Native),
message: (0, superstruct_1.string)(),
});
const InAppNotificationStruct = (0, superstruct_1.object)({
type: (0, snaps_sdk_1.enumValue)(snaps_sdk_1.NotificationType.InApp),
message: (0, superstruct_1.string)(),
});
const InAppNotificationWithDetailsStruct = (0, superstruct_1.object)({
type: (0, snaps_sdk_1.enumValue)(snaps_sdk_1.NotificationType.InApp),
message: (0, superstruct_1.string)(),
content: snaps_sdk_1.ComponentOrElementStruct,
title: (0, superstruct_1.string)(),
footerLink: (0, superstruct_1.optional)((0, superstruct_1.object)({
href: (0, superstruct_1.string)(),
text: (0, superstruct_1.string)(),
})),
});
const NotificationParametersStruct = (0, snaps_sdk_1.union)([
InAppNotificationStruct,
InAppNotificationWithDetailsStruct,
NativeNotificationStruct,
]);
/**
* The specification builder for the `snap_notify` permission.
* `snap_notify` allows snaps to send multiple types of notifications to its users.
*
* @param options - The specification builder options.
* @param options.allowedCaveats - The optional allowed caveats for the permission.
* @param options.methodHooks - The RPC method hooks needed by the method implementation.
* @returns The specification for the `snap_notify` permission.
*/
const specificationBuilder = ({ allowedCaveats = null, methodHooks }) => {
return {
permissionType: permission_controller_1.PermissionType.RestrictedMethod,
targetName: methodName,
allowedCaveats,
methodImplementation: getImplementation(methodHooks),
subjectTypes: [permission_controller_1.SubjectType.Snap],
};
};
exports.specificationBuilder = specificationBuilder;
const methodHooks = {
showNativeNotification: true,
showInAppNotification: true,
isOnPhishingList: true,
maybeUpdatePhishingList: true,
createInterface: true,
getSnap: true,
};
exports.notifyBuilder = Object.freeze({
targetName: methodName,
specificationBuilder: exports.specificationBuilder,
methodHooks,
});
/**
* Builds the method implementation for `snap_notify`.
*
* @param hooks - The RPC method hooks.
* @param hooks.showNativeNotification - A function that shows a native browser notification.
* @param hooks.showInAppNotification - A function that shows a notification in the MetaMask UI.
* @param hooks.isOnPhishingList - A function that checks for links against the phishing list.
* @param hooks.maybeUpdatePhishingList - A function that updates the phishing list if needed.
* @param hooks.createInterface - A function that creates the interface in SnapInterfaceController.
* @param hooks.getSnap - A function that checks if a snap is installed.
* @returns The method implementation which returns `null` on success.
* @throws If the params are invalid.
*/
function getImplementation({ showNativeNotification, showInAppNotification, isOnPhishingList, maybeUpdatePhishingList, createInterface, getSnap, }) {
return async function implementation(args) {
const { params, context: { origin }, } = args;
await maybeUpdatePhishingList();
const validatedParams = getValidatedParams(params, isOnPhishingList, getSnap);
if ((0, utils_1.hasProperty)(validatedParams, 'content')) {
const id = await createInterface(origin, validatedParams.content, undefined, snaps_sdk_1.ContentType.Notification);
validatedParams.content = id;
}
switch (validatedParams.type) {
case snaps_sdk_1.NotificationType.Native:
return await showNativeNotification(origin, validatedParams);
case snaps_sdk_1.NotificationType.InApp:
return await showInAppNotification(origin, validatedParams);
default:
throw rpc_errors_1.rpcErrors.invalidParams({
message: 'Must specify a valid notification "type".',
});
}
};
}
exports.getImplementation = getImplementation;
/**
* Validates the notify method `params` and returns them cast to the correct
* type. Throws if validation fails.
*
* @param params - The unvalidated params object from the method request.
* @param isOnPhishingList - The function that checks for links against the phishing list.
* @param getSnap - A function that checks if a snap is installed.
* @returns The validated method parameter object.
* @throws If the params are invalid.
*/
function getValidatedParams(params, isOnPhishingList, getSnap) {
if (!(0, utils_1.isObject)(params)) {
throw rpc_errors_1.rpcErrors.invalidParams({
message: 'Expected params to be a single object.',
});
}
const { type, message } = params;
if (!type ||
typeof type !== 'string' ||
!Object.values(snaps_sdk_1.NotificationType).includes(type)) {
throw rpc_errors_1.rpcErrors.invalidParams({
message: 'Must specify a valid notification "type".',
});
}
const isNotString = !message || typeof message !== 'string';
// Set to the max message length on a Mac notification for now.
if (type === snaps_sdk_1.NotificationType.Native &&
(isNotString || message.length >= 50)) {
throw rpc_errors_1.rpcErrors.invalidParams({
message: 'Must specify a non-empty string "message" less than 50 characters long.',
});
}
if (type === snaps_sdk_1.NotificationType.InApp &&
(isNotString || message.length >= 500)) {
throw rpc_errors_1.rpcErrors.invalidParams({
message: 'Must specify a non-empty string "message" less than 500 characters long.',
});
}
try {
const validatedParams = (0, snaps_utils_1.createUnion)(params, NotificationParametersStruct, 'type');
(0, snaps_utils_1.validateTextLinks)(validatedParams.message, isOnPhishingList, getSnap);
if ((0, utils_1.hasProperty)(validatedParams, 'footerLink')) {
(0, snaps_utils_1.validateLink)(validatedParams.footerLink.href, isOnPhishingList, getSnap);
}
return validatedParams;
}
catch (error) {
throw rpc_errors_1.rpcErrors.invalidParams({
message: `Invalid params: ${(0, snaps_sdk_1.getErrorMessage)(error)}`,
});
}
}
exports.getValidatedParams = getValidatedParams;
//# sourceMappingURL=notify.cjs.map