@hashgraph/hedera-identify-snap
Version:
A snap for managing Decentralized Identifiers(DIDs)
289 lines (272 loc) • 8.96 kB
text/typescript
/*-
*
* Hedera Identify Snap
*
* Copyright (C) 2024 Hedera Hashgraph, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import type { DialogParams, OnUserInputHandler } from '@metamask/snaps-sdk';
import {
UserInputEventType,
copyable,
divider,
heading,
text,
} from '@metamask/snaps-sdk';
import { W3CVerifiableCredential } from '@veramo/core';
import _ from 'lodash';
import { ResolveDIDFacade } from '../facades/did/ResolveDIDFacade';
import { SwitchDIDMethodFacade } from '../facades/did/SwitchDIDMethodFacade';
import { GetVCsFacade } from '../facades/vc/GetVCsFacade';
import { SaveVCFacade } from '../facades/vc/SaveVCFacade';
import { CreateVPFacade } from '../facades/vp/CreateVPFacade';
import {
IDataManagerQueryArgs,
IDataManagerQueryResult,
} from '../plugins/veramo/verifiable-creds-manager';
import { SnapAccounts } from '../snap/SnapAccounts';
import { SnapState } from '../snap/SnapState';
import {
CreateVCRequestParams,
CreateVPRequestParams,
ProofInfo,
ResolveDIDRequestParams,
SwitchMethodRequestParams,
} from '../types/params';
import type { IdentifySnapParams } from '../types/state';
import { EvmUtils } from '../utils/EvmUtils';
import { ParamUtils } from '../utils/ParamUtils';
import { SnapUtils } from '../utils/SnapUtils';
export const onUserInputUI: OnUserInputHandler = async ({ event }) => {
// Ensure valid event structure
if (!event || !event.name) {
console.warn('Invalid event detected:', event);
return;
}
// Ignore InputChangeEvent explicitly
if (event.type === UserInputEventType.InputChangeEvent) {
console.warn('Ignoring InputChangeEvent:', event.name);
return;
}
// Set origin to be the current page
const origin = 'Identify Snap';
let state = await SnapState.getStateUnchecked();
if (_.isEmpty(state)) {
state = await SnapState.initState();
}
// Get network
const network = await EvmUtils.getChainId();
// Set current account
const snapAddress = await SnapAccounts.setCurrentAccount(
origin,
state,
null,
network,
false,
true,
);
const identifySnapParams: IdentifySnapParams = {
origin,
state,
};
let showDialog = true;
const panelToShow = SnapUtils.initializePanelToShow();
panelToShow.push(heading('Execution Details'), divider());
// Prevent duplicate handling by ensuring unique event execution
if (event.type === UserInputEventType.FormSubmitEvent) {
switch (event.name) {
case 'form-switch-did-method': {
try {
const params = {
didMethod: event.value.didMethod,
} as SwitchMethodRequestParams;
ParamUtils.isValidSwitchMethodRequest(params);
const switched = await SwitchDIDMethodFacade.switchDIDMethod(
identifySnapParams,
params.didMethod,
);
if (switched) {
panelToShow.push(text('Switched DID method successfully'));
} else {
panelToShow.push(text('Failed to switch DID method'));
}
} catch (error) {
console.error('Error switching DID method:', error);
panelToShow.push(text('Failed to switch DID method'));
}
break;
}
case 'form-resolve-did': {
try {
const params = {
did: event.value.did || state.currentAccount.identifier.did,
} as ResolveDIDRequestParams;
ParamUtils.isValidResolveDIDRequest(params);
const result = await ResolveDIDFacade.resolveDID(
identifySnapParams,
params.did,
);
if (!_.isEmpty(result)) {
panelToShow.push(
text('Result: '),
copyable({ value: JSON.stringify(result) }),
);
} else {
panelToShow.push(text('Failed to resolve DID'));
}
} catch (error) {
console.error('Error resolving DID:', error);
panelToShow.push(text('Failed to resolve DID'));
}
break;
}
case 'form-create-vc': {
try {
const params = {
vcKey: 'vcText',
vcValue: event.value.vcText,
credTypes: ['TextCredential'],
options: {
store: 'snap',
},
} as CreateVCRequestParams;
ParamUtils.isValidCreateVCRequest(params);
const result = await SaveVCFacade.createVC(
identifySnapParams,
params,
);
if (!_.isEmpty(result)) {
panelToShow.push(
text('Result: '),
copyable({ value: JSON.stringify(result.data) }),
);
} else {
panelToShow.push(text('Failed to create VC'));
}
} catch (error) {
console.error('Error creating VC:', error);
panelToShow.push(text('Failed to create VC'));
}
break;
}
case 'form-get-vcs': {
try {
const params = {
options: {
store: 'snap',
},
} as IDataManagerQueryArgs;
ParamUtils.isValidGetVCsRequest(params);
const result = await GetVCsFacade.getVCs(identifySnapParams, params);
if (!_.isEmpty(result)) {
result.forEach((vc: IDataManagerQueryResult, index: number) => {
panelToShow.push(
text(`VC #${index + 1}:`),
copyable({ value: JSON.stringify(vc.data) }),
divider(),
);
});
} else {
panelToShow.push(text('No VCs found.'));
}
} catch (error) {
console.error('Error getting VCs:', error);
panelToShow.push(text('Failed to get VCs'));
}
break;
}
case 'form-create-vp': {
try {
const proofInfo: ProofInfo = {
proofFormat: 'jwt',
};
const params = {
vcs: [
JSON.parse(
event.value.vcData as string,
) as W3CVerifiableCredential,
],
proofInfo,
options: {
store: 'snap',
},
} as CreateVPRequestParams;
ParamUtils.isValidCreateVPRequest(params);
const result = await CreateVPFacade.createVP(
identifySnapParams,
params,
);
if (!_.isEmpty(result)) {
panelToShow.push(
text('Result: '),
copyable({ value: JSON.stringify(result) }),
);
} else {
panelToShow.push(text('Failed to create VP'));
}
} catch (error) {
console.error('Error creating VP:', error);
panelToShow.push(text('Failed to create VP'));
}
break;
}
default: {
console.warn('No logic defined for this form name:', event.name);
showDialog = false;
}
}
} else if (event.type === UserInputEventType.ButtonClickEvent) {
switch (event.name) {
case 'btn-export-snap-account-private-key': {
const privateKey =
state.accountState[snapAddress]?.[network]?.keyStore?.privateKey;
if (privateKey) {
panelToShow.push(
text(
'Warning: Never disclose this key. Anyone with your private keys can steal any assets held in your account.',
),
copyable({
value: privateKey,
sensitive: true,
}),
);
// Safely overwrite the private key
const keyStore = state.accountState[snapAddress]?.[network]?.keyStore;
if (keyStore) {
keyStore.privateKey = ''; // Overwrite with an empty string
}
} else {
panelToShow.push(text('Private key unavailable or already cleared.'));
}
await SnapState.updateState(state); // Update the state after changes
break;
}
default: {
console.warn('No logic defined for this button name:', event.name);
showDialog = false;
}
}
} else {
console.warn('Unhandled event type:', event.type);
showDialog = false;
}
const dialogParams: DialogParams = {
type: 'alert',
content: await SnapUtils.generateCommonPanel(origin, network, panelToShow),
};
if (showDialog) {
await SnapUtils.snapDialog(dialogParams);
}
};