import deepFreeze from 'deep-freeze-strict';
///: BEGIN:ONLY_INCLUDE_IN(flask)
import React from 'react';
import { getRpcCaveatOrigins } from '@metamask/snaps-controllers/dist/snaps/endowments/rpc';
import { SnapCaveatType } from '@metamask/snaps-utils';
import { isNonEmptyArray } from '@metamask/controller-utils';
///: END:ONLY_INCLUDE_IN
import {
RestrictedMethods,
///: BEGIN:ONLY_INCLUDE_IN(flask)
EndowmentPermissions,
PermissionNamespaces,
///: END:ONLY_INCLUDE_IN
} from '../../../shared/constants/permissions';
///: BEGIN:ONLY_INCLUDE_IN(flask)
import { SNAPS_METADATA } from '../../../shared/constants/snaps';
import { Icon, ICON_NAMES } from '../../components/component-library';
import { coinTypeToProtocolName, getSnapDerivationPathName } from './util';
///: END:ONLY_INCLUDE_IN
const UNKNOWN_PERMISSION = Symbol('unknown');
const PERMISSION_DESCRIPTIONS = deepFreeze({
[RestrictedMethods.eth_accounts]: (t) => ({
label: t('permission_ethereumAccounts'),
leftIcon: 'fas fa-eye',
rightIcon: null,
weight: 2,
}),
///: BEGIN:ONLY_INCLUDE_IN(flask)
[RestrictedMethods.snap_confirm]: (t) => ({
label: t('permission_customConfirmation'),
leftIcon: 'fas fa-user-check',
rightIcon: null,
weight: 3,
}),
[RestrictedMethods.snap_dialog]: (t) => ({
label: t('permission_dialog'),
leftIcon: 'fas fa-user-check',
rightIcon: null,
weight: 3,
}),
[RestrictedMethods.snap_notify]: (t) => ({
leftIcon: ,
label: t('permission_notifications'),
rightIcon: null,
weight: 3,
}),
[RestrictedMethods.snap_getBip32PublicKey]: (t, _, permissionValue) =>
permissionValue.caveats[0].value.map(({ path, curve }) => {
const baseDescription = {
leftIcon: 'fas fa-eye',
rightIcon: null,
weight: 1,
};
const friendlyName = getSnapDerivationPathName(path, curve);
if (friendlyName) {
return {
...baseDescription,
label: t('permission_viewNamedBip32PublicKeys', [
{friendlyName}
,
path.join('/'),
]),
};
}
return {
...baseDescription,
label: t('permission_viewBip32PublicKeys', [
{path.join('/')}
,
curve,
]),
};
}),
[RestrictedMethods.snap_getBip32Entropy]: (t, _, permissionValue) =>
permissionValue.caveats[0].value.map(({ path, curve }) => {
const baseDescription = {
leftIcon: 'fas fa-door-open',
rightIcon: null,
weight: 1,
};
const friendlyName = getSnapDerivationPathName(path, curve);
if (friendlyName) {
return {
...baseDescription,
label: t('permission_manageNamedBip32Keys', [
{friendlyName}
,
path.join('/'),
]),
};
}
return {
...baseDescription,
label: t('permission_manageBip32Keys', [
{path.join('/')}
,
curve,
]),
};
}),
[RestrictedMethods.snap_getBip44Entropy]: (t, _, permissionValue) =>
permissionValue.caveats[0].value.map(({ coinType }) => ({
label: t('permission_manageBip44Keys', [
{coinTypeToProtocolName(coinType) ||
`${coinType} (Unrecognized protocol)`}
,
]),
leftIcon: 'fas fa-door-open',
rightIcon: null,
weight: 1,
})),
[RestrictedMethods.snap_getEntropy]: (t) => ({
label: t('permission_getEntropy'),
leftIcon: 'fas fa-key',
rightIcon: null,
weight: 3,
}),
[RestrictedMethods.snap_manageState]: (t) => ({
label: t('permission_manageState'),
leftIcon: 'fas fa-download',
rightIcon: null,
weight: 3,
}),
[RestrictedMethods['wallet_snap_*']]: (t, permissionName) => {
const baseDescription = {
leftIcon: 'fas fa-bolt',
rightIcon: null,
};
const snapId = permissionName.split('_').slice(-1);
const friendlyName = SNAPS_METADATA[snapId]?.name;
if (friendlyName) {
return {
...baseDescription,
label: t('permission_accessNamedSnap', [
{friendlyName}
,
]),
};
}
return {
...baseDescription,
label: t('permission_accessSnap', [snapId]),
};
},
[EndowmentPermissions['endowment:network-access']]: (t) => ({
label: t('permission_accessNetwork'),
leftIcon: 'fas fa-wifi',
rightIcon: null,
weight: 2,
}),
[EndowmentPermissions['endowment:long-running']]: (t) => ({
label: t('permission_longRunning'),
leftIcon: 'fas fa-infinity',
rightIcon: null,
weight: 3,
}),
[EndowmentPermissions['endowment:transaction-insight']]: (
t,
_,
permissionValue,
) => {
const baseDescription = {
leftIcon: 'fas fa-info',
rightIcon: null,
};
const result = [
{
...baseDescription,
label: t('permission_transactionInsight'),
},
];
if (
isNonEmptyArray(permissionValue.caveats) &&
permissionValue.caveats[0].type === SnapCaveatType.TransactionOrigin &&
permissionValue.caveats[0].value
) {
result.push({
...baseDescription,
label: t('permission_transactionInsightOrigin'),
leftIcon: 'fas fa-compass',
});
}
return result;
},
[EndowmentPermissions['endowment:cronjob']]: (t) => ({
label: t('permission_cronjob'),
leftIcon: 'fas fa-clock',
rightIcon: null,
weight: 2,
}),
[EndowmentPermissions['endowment:ethereum-provider']]: (t) => ({
label: t('permission_ethereumProvider'),
leftIcon: 'fab fa-ethereum',
rightIcon: null,
weight: 1,
}),
[EndowmentPermissions['endowment:rpc']]: (t, _, permissionValue) => {
const { snaps, dapps } = getRpcCaveatOrigins(permissionValue);
const labels = [];
if (snaps) {
labels.push(t('permission_rpc', [t('otherSnaps')]));
}
if (dapps) {
labels.push(t('permission_rpc', [t('websites')]));
}
return labels.map((label) => ({
label,
leftIcon: 'fas fa-plug',
rightIcon: null,
weight: 2,
}));
},
///: END:ONLY_INCLUDE_IN
[UNKNOWN_PERMISSION]: (t, permissionName) => ({
label: t('permission_unknown', [permissionName ?? 'undefined']),
leftIcon: 'fas fa-times-circle',
rightIcon: null,
weight: 4,
}),
});
/**
* @typedef {object} PermissionLabelObject
* @property {string} label - The text label.
* @property {string} leftIcon - The left icon.
* @property {string} rightIcon - The right icon.
* @property {number} weight - The weight of the permission.
* @property {string} permissionName - The name of the permission.
* @property {string} permissionValue - The raw value of the permission.
*/
/**
* @param {Function} t - The translation function
* @param {string} permissionName - The name of the permission to request
* @param {object} permissionValue - The value of the permission to request
* @returns {PermissionLabelObject[]}
*/
export const getPermissionDescription = (
t,
permissionName,
permissionValue,
) => {
let value = PERMISSION_DESCRIPTIONS[UNKNOWN_PERMISSION];
if (Object.hasOwnProperty.call(PERMISSION_DESCRIPTIONS, permissionName)) {
value = PERMISSION_DESCRIPTIONS[permissionName];
}
///: BEGIN:ONLY_INCLUDE_IN(flask)
for (const namespace of Object.keys(PermissionNamespaces)) {
if (permissionName.startsWith(namespace)) {
value = PERMISSION_DESCRIPTIONS[PermissionNamespaces[namespace]];
}
}
///: END:ONLY_INCLUDE_IN
const result = value(t, permissionName, permissionValue);
if (!Array.isArray(result)) {
return [{ ...result, permissionName, permissionValue }];
}
return result.map((item) => ({
...item,
permissionName,
permissionValue,
}));
};
/**
* Get the weighted permissions from a permissions object. The weight is used to
* sort the permissions in the UI.
*
* @param {Function} t - The translation function
* @param {object} permissions - The permissions object.
* @returns {PermissionLabelObject[]}
*/
export function getWeightedPermissions(t, permissions) {
return Object.entries(permissions)
.reduce(
(target, [permissionName, permissionValue]) =>
target.concat(
getPermissionDescription(t, permissionName, permissionValue),
),
[],
)
.sort((left, right) => left.weight - right.weight);
}