From 1831acd1842b9e3102d96f8045bbe71f360c74be Mon Sep 17 00:00:00 2001 From: Himalayan Dev <72465553+himalayan-dev@users.noreply.github.com> Date: Thu, 12 Dec 2024 16:24:28 -0500 Subject: [PATCH] Support hedera did (#852) Signed-off-by: Himalayan Dev --- packages/hedera-identify-snap/.eslintignore | 1 + .../packages/site/.env.example | 1 + .../packages/site/README.md | 8 +- .../packages/site/package.json | 2 +- .../packages/site/src/App.tsx | 5 + .../site/src/components/base/Buttons.tsx | 8 +- .../site/src/components/base/Card.tsx | 2 +- .../site/src/components/base/Header.tsx | 15 +- .../cards/ConfigureGoogleAccount.tsx | 3 +- ...entitySnap.tsx => ConnectIdentifySnap.tsx} | 0 .../site/src/components/cards/CreateVC.tsx | 1 - .../src/components/cards/DeleteAllVCs.tsx | 8 +- .../src/components/cards/GetAccountInfo.tsx | 5 +- .../site/src/components/cards/GetAllVCs.tsx | 1 - .../src/components/cards/GetSpecificVC.tsx | 1 - .../site/src/components/cards/GetVP.tsx | 1 - .../site/src/components/cards/RemoveVC.tsx | 1 - .../site/src/components/cards/ResolveDID.tsx | 2 +- .../src/components/cards/SendHelloHessage.tsx | 2 +- .../src/components/cards/SwitchDIDMethod.tsx | 2 +- .../src/components/cards/SyncGoogleVCs.tsx | 2 +- .../components/cards/ToggleMetamaskPopups.tsx | 2 +- .../site/src/components/cards/VerifyVC.tsx | 2 +- .../site/src/components/cards/VerifyVP.tsx | 2 +- .../site/src/components/cards/index.ts | 2 +- .../packages/site/src/utils/hedera.ts | 192 +++- .../packages/site/src/utils/snap.ts | 62 +- .../packages/snap/.eslintignore | 4 +- .../packages/snap/.eslintrc.js | 2 +- .../packages/snap/.prettierignore | 4 +- .../packages/snap/CHANGELOG.md | 9 +- .../packages/snap/README.md | 3 +- .../snap/{json-typings.d.ts => global.d.ts} | 3 + .../packages/snap/images/icon.svg | 0 .../packages/snap/package.json | 15 +- .../packages/snap/snap.config.ts | 15 +- .../packages/snap/snap.manifest.json | 2 +- .../src/client/HederaClientImplFactory.ts | 159 +++ .../snap/src/client/SimpleHederaClientImpl.ts | 58 + .../packages/snap/src/components/common.tsx | 6 +- .../packages/snap/src/components/hello.tsx | 2 +- .../src/components/showAccountPrivateKey.tsx | 20 +- .../{utils/network.ts => constants/crypto.ts} | 5 +- .../packages/snap/src/constants/index.ts | 2 + .../packages/snap/src/constants/network.ts | 206 ++++ .../packages/snap/src/custom-ui/onHome.ts | 166 +++ .../packages/snap/src/custom-ui/onInstall.ts | 49 + .../packages/snap/src/custom-ui/onUpdate.ts | 43 + .../snap/src/custom-ui/onUserInput.ts | 269 +++++ .../hederaDidProvider.ts} | 82 +- .../snap/src/did/hedera/hederaDidResolver.ts | 96 ++ .../snap/src/did/hedera/hederaDidUtils.ts | 105 ++ .../snap/src/did/key/keyDidResolver.ts | 121 -- .../packages/snap/src/did/key/keyDidUtils.ts | 26 +- .../src/{hedera => domain}/wallet/abstract.ts | 9 +- .../wallet/software-private-key.ts | 7 +- .../snap/src/facades/did/ResolveDIDFacade.ts | 47 + .../src/facades/did/SwitchDIDMethodFacade.ts | 82 ++ .../facades/gdrive/ConfigureGAccountFacade.ts | 93 ++ .../gdrive/SyncVCsInGDriveFacade.ts} | 129 +-- .../src/facades/snap/GetAccountInfoFacade.ts | 47 + .../src/facades/snap/TogglePopupsFacade.ts | 64 ++ .../snap/src/facades/vc/GetVCsFacade.ts | 84 ++ .../snap/src/facades/vc/RemoveVCsFacade.ts | 172 +++ .../snap/src/facades/vc/SaveVCFacade.ts | 248 ++++ .../vc/VerifyVCFacade.ts} | 47 +- .../snap/src/facades/vp/CreateVPFacade.ts | 151 +++ .../snap/src/facades/vp/VerifyVPFacade.ts | 52 + .../snap/src/hedera/client/create-account.ts | 70 -- .../packages/snap/src/hedera/client/index.ts | 137 --- .../packages/snap/src/hedera/config.ts | 60 - .../packages/snap/src/hedera/index.ts | 297 ----- .../packages/snap/src/hedera/service.ts | 125 -- .../packages/snap/src/index.tsx | 229 ++-- .../HederaClientFactory.ts} | 35 +- .../veramo/google-drive-data-store/index.ts | 1 - .../src/googleDriveVCStore.ts | 48 +- .../veramo/google-drive-data-store/src/jwt.ts | 40 - .../snap-data-store/src/snapDataStore.ts | 297 +++-- .../agent/dataManager.ts | 7 +- .../data-store/memoryDataStore.ts | 4 +- .../types/IDataManager.ts | 2 +- .../snap/src/rpc/account/getAccountInfo.ts | 46 - .../src/rpc/did/getAvailableDIDMethods.ts | 30 - .../snap/src/rpc/did/getCurrentDIDMethod.ts | 33 - .../packages/snap/src/rpc/did/resolveDID.ts | 45 - .../snap/src/rpc/did/switchDIDMethod.ts | 69 -- .../src/rpc/gdrive/configureGoogleAccount.ts | 77 -- .../snap/src/rpc/snap/togglePopups.ts | 51 - .../packages/snap/src/rpc/vc/createVC.ts | 158 --- .../packages/snap/src/rpc/vc/createVP.ts | 141 --- .../packages/snap/src/rpc/vc/deleteAllVCs.ts | 85 -- .../src/rpc/vc/getSupportedProofFormats.ts | 30 - .../packages/snap/src/rpc/vc/getVCs.ts | 82 -- .../packages/snap/src/rpc/vc/removeVC.ts | 108 -- .../packages/snap/src/rpc/vc/saveVC.ts | 117 -- .../packages/snap/src/rpc/vc/verifyVP.ts | 51 - .../packages/snap/src/snap/SnapAccounts.ts | 638 +++++++++++ .../packages/snap/src/snap/SnapState.ts | 149 +++ .../packages/snap/src/snap/account.ts | 262 ----- .../packages/snap/src/snap/dapp.ts | 78 -- .../packages/snap/src/snap/dialog.ts | 225 ---- .../packages/snap/src/snap/network.ts | 33 - .../packages/snap/src/snap/state.ts | 142 --- .../packages/snap/src/types/account.ts | 62 + .../packages/snap/src/types/constants.ts | 18 +- .../packages/snap/src/types/hedera.ts | 84 ++ .../packages/snap/src/types/params.ts | 35 +- .../src/{interfaces.ts => types/state.ts} | 99 +- .../packages/snap/src/utils/CryptoUtils.ts | 249 ++++ .../packages/snap/src/utils/EvmUtils.ts | 48 + .../packages/snap/src/utils/FetchUtils.ts | 56 + .../packages/snap/src/utils/HederaUtils.ts | 80 ++ .../packages/snap/src/utils/ParamUtils.ts | 1015 +++++++++++++++++ .../packages/snap/src/utils/SnapUtils.tsx | 274 +++++ .../packages/snap/src/utils/StateUtils.ts | 80 ++ .../packages/snap/src/utils/Utils.ts | 107 ++ .../packages/snap/src/utils/config.ts | 69 -- .../packages/snap/src/utils/init.ts | 48 - .../packages/snap/src/utils/jwt.ts | 40 - .../packages/snap/src/utils/keyPair.ts | 104 -- .../packages/snap/src/utils/params.ts | 978 ---------------- .../packages/snap/src/veramo/accountImport.ts | 210 ---- .../packages/snap/src/veramo/agent.ts | 39 +- .../account/getAccountInfo.spec.disabled.ts | 149 --- .../rpc/did/getAvailableDIDMethods.spec.ts | 39 - .../did/getCurrentDIDMethod.spec.disabled.ts | 61 - .../tests/rpc/did/resolveDID.spec.disabled.ts | 73 -- .../rpc/did/switchDIDMethod.spec.disabled.ts | 122 -- .../rpc/gdrive/configureGoogleAccount.spec.ts | 34 - .../rpc/hedera/connectHederaAccount.spec.ts | 34 - .../snap/tests/rpc/snap/togglePopups.spec.ts | 33 - .../tests/rpc/vc/createVC.spec.disabled.ts | 84 -- .../tests/rpc/vc/createVP.spec.disabled.ts | 117 -- .../rpc/vc/deleteAllVCs.spec.disabled.ts | 83 -- .../rpc/vc/getSupportedProofFormats.spec.ts | 34 - .../snap/tests/rpc/vc/getVCs.spec.disabled.ts | 141 --- .../tests/rpc/vc/removeVC.spec.disabled.ts | 120 -- .../snap/tests/rpc/vc/saveVC.spec.disabled.ts | 139 --- .../snap/tests/rpc/vc/syncGoogleVCs.spec.ts | 33 - .../tests/rpc/vc/verifyVC.spec.disabled.ts | 104 -- .../tests/rpc/vc/verifyVP.spec.disabled.ts | 119 -- .../snap/tests/testUtils/constants.ts | 94 -- .../packages/snap/tests/testUtils/helper.ts | 52 - .../snap/tests/testUtils/snap.mock.ts | 159 --- .../snap/tests/testUtils/wallet.mock.ts | 96 -- .../snap/tests/utils/init.spec.disabled.ts | 56 - .../tests/utils/snapUtils.spec.disabled.ts | 285 ----- .../tests/utils/stateUtils.spec.disabled.ts | 122 -- .../packages/snap/tsconfig.json | 10 +- packages/hedera-identify-snap/yarn.lock | 768 +++---------- 151 files changed, 6033 insertions(+), 7862 deletions(-) create mode 100644 packages/hedera-identify-snap/packages/site/.env.example rename packages/hedera-identify-snap/packages/site/src/components/cards/{ConnectIdentitySnap.tsx => ConnectIdentifySnap.tsx} (100%) rename packages/hedera-identify-snap/packages/snap/{json-typings.d.ts => global.d.ts} (93%) mode change 100755 => 100644 packages/hedera-identify-snap/packages/snap/images/icon.svg create mode 100644 packages/hedera-identify-snap/packages/snap/src/client/HederaClientImplFactory.ts create mode 100644 packages/hedera-identify-snap/packages/snap/src/client/SimpleHederaClientImpl.ts rename packages/hedera-identify-snap/packages/snap/src/{utils/network.ts => constants/crypto.ts} (84%) create mode 100644 packages/hedera-identify-snap/packages/snap/src/constants/network.ts create mode 100644 packages/hedera-identify-snap/packages/snap/src/custom-ui/onHome.ts create mode 100644 packages/hedera-identify-snap/packages/snap/src/custom-ui/onInstall.ts create mode 100644 packages/hedera-identify-snap/packages/snap/src/custom-ui/onUpdate.ts create mode 100644 packages/hedera-identify-snap/packages/snap/src/custom-ui/onUserInput.ts rename packages/hedera-identify-snap/packages/snap/src/did/{key/keyDidProvider.ts => hedera/hederaDidProvider.ts} (59%) create mode 100644 packages/hedera-identify-snap/packages/snap/src/did/hedera/hederaDidResolver.ts create mode 100644 packages/hedera-identify-snap/packages/snap/src/did/hedera/hederaDidUtils.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/src/did/key/keyDidResolver.ts rename packages/hedera-identify-snap/packages/snap/src/{hedera => domain}/wallet/abstract.ts (90%) rename packages/hedera-identify-snap/packages/snap/src/{hedera => domain}/wallet/software-private-key.ts (87%) create mode 100644 packages/hedera-identify-snap/packages/snap/src/facades/did/ResolveDIDFacade.ts create mode 100644 packages/hedera-identify-snap/packages/snap/src/facades/did/SwitchDIDMethodFacade.ts create mode 100644 packages/hedera-identify-snap/packages/snap/src/facades/gdrive/ConfigureGAccountFacade.ts rename packages/hedera-identify-snap/packages/snap/src/{rpc/vc/syncGoogleVCs.ts => facades/gdrive/SyncVCsInGDriveFacade.ts} (69%) create mode 100644 packages/hedera-identify-snap/packages/snap/src/facades/snap/GetAccountInfoFacade.ts create mode 100644 packages/hedera-identify-snap/packages/snap/src/facades/snap/TogglePopupsFacade.ts create mode 100644 packages/hedera-identify-snap/packages/snap/src/facades/vc/GetVCsFacade.ts create mode 100644 packages/hedera-identify-snap/packages/snap/src/facades/vc/RemoveVCsFacade.ts create mode 100644 packages/hedera-identify-snap/packages/snap/src/facades/vc/SaveVCFacade.ts rename packages/hedera-identify-snap/packages/snap/src/{rpc/vc/verifyVC.ts => facades/vc/VerifyVCFacade.ts} (50%) create mode 100644 packages/hedera-identify-snap/packages/snap/src/facades/vp/CreateVPFacade.ts create mode 100644 packages/hedera-identify-snap/packages/snap/src/facades/vp/VerifyVPFacade.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/src/hedera/client/create-account.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/src/hedera/client/index.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/src/hedera/config.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/src/hedera/index.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/src/hedera/service.ts rename packages/hedera-identify-snap/packages/snap/src/{utils/formatUtils.ts => interfaces/HederaClientFactory.ts} (54%) delete mode 100644 packages/hedera-identify-snap/packages/snap/src/plugins/veramo/google-drive-data-store/src/jwt.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/src/rpc/account/getAccountInfo.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/src/rpc/did/getAvailableDIDMethods.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/src/rpc/did/getCurrentDIDMethod.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/src/rpc/did/resolveDID.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/src/rpc/did/switchDIDMethod.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/src/rpc/gdrive/configureGoogleAccount.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/src/rpc/snap/togglePopups.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/src/rpc/vc/createVC.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/src/rpc/vc/createVP.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/src/rpc/vc/deleteAllVCs.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/src/rpc/vc/getSupportedProofFormats.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/src/rpc/vc/getVCs.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/src/rpc/vc/removeVC.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/src/rpc/vc/saveVC.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/src/rpc/vc/verifyVP.ts create mode 100644 packages/hedera-identify-snap/packages/snap/src/snap/SnapAccounts.ts create mode 100644 packages/hedera-identify-snap/packages/snap/src/snap/SnapState.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/src/snap/account.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/src/snap/dapp.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/src/snap/dialog.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/src/snap/network.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/src/snap/state.ts create mode 100644 packages/hedera-identify-snap/packages/snap/src/types/account.ts create mode 100644 packages/hedera-identify-snap/packages/snap/src/types/hedera.ts rename packages/hedera-identify-snap/packages/snap/src/{interfaces.ts => types/state.ts} (51%) create mode 100644 packages/hedera-identify-snap/packages/snap/src/utils/CryptoUtils.ts create mode 100644 packages/hedera-identify-snap/packages/snap/src/utils/EvmUtils.ts create mode 100644 packages/hedera-identify-snap/packages/snap/src/utils/FetchUtils.ts create mode 100644 packages/hedera-identify-snap/packages/snap/src/utils/HederaUtils.ts create mode 100644 packages/hedera-identify-snap/packages/snap/src/utils/ParamUtils.ts create mode 100644 packages/hedera-identify-snap/packages/snap/src/utils/SnapUtils.tsx create mode 100644 packages/hedera-identify-snap/packages/snap/src/utils/StateUtils.ts create mode 100644 packages/hedera-identify-snap/packages/snap/src/utils/Utils.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/src/utils/config.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/src/utils/init.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/src/utils/jwt.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/src/utils/keyPair.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/src/utils/params.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/src/veramo/accountImport.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/tests/rpc/account/getAccountInfo.spec.disabled.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/tests/rpc/did/getAvailableDIDMethods.spec.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/tests/rpc/did/getCurrentDIDMethod.spec.disabled.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/tests/rpc/did/resolveDID.spec.disabled.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/tests/rpc/did/switchDIDMethod.spec.disabled.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/tests/rpc/gdrive/configureGoogleAccount.spec.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/tests/rpc/hedera/connectHederaAccount.spec.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/tests/rpc/snap/togglePopups.spec.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/tests/rpc/vc/createVC.spec.disabled.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/tests/rpc/vc/createVP.spec.disabled.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/tests/rpc/vc/deleteAllVCs.spec.disabled.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/tests/rpc/vc/getSupportedProofFormats.spec.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/tests/rpc/vc/getVCs.spec.disabled.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/tests/rpc/vc/removeVC.spec.disabled.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/tests/rpc/vc/saveVC.spec.disabled.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/tests/rpc/vc/syncGoogleVCs.spec.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/tests/rpc/vc/verifyVC.spec.disabled.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/tests/rpc/vc/verifyVP.spec.disabled.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/tests/testUtils/constants.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/tests/testUtils/helper.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/tests/testUtils/snap.mock.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/tests/testUtils/wallet.mock.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/tests/utils/init.spec.disabled.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/tests/utils/snapUtils.spec.disabled.ts delete mode 100644 packages/hedera-identify-snap/packages/snap/tests/utils/stateUtils.spec.disabled.ts diff --git a/packages/hedera-identify-snap/.eslintignore b/packages/hedera-identify-snap/.eslintignore index 4004b86f..53a8584b 100644 --- a/packages/hedera-identify-snap/.eslintignore +++ b/packages/hedera-identify-snap/.eslintignore @@ -1,3 +1,4 @@ node_modules .eslintrc.js packages/snap/** +packages/site/** \ No newline at end of file diff --git a/packages/hedera-identify-snap/packages/site/.env.example b/packages/hedera-identify-snap/packages/site/.env.example new file mode 100644 index 00000000..6f178dff --- /dev/null +++ b/packages/hedera-identify-snap/packages/site/.env.example @@ -0,0 +1 @@ +GATSBY_GOOGLE_DRIVE_CLIENT_ID="" \ No newline at end of file diff --git a/packages/hedera-identify-snap/packages/site/README.md b/packages/hedera-identify-snap/packages/site/README.md index 7efd801e..23d871b5 100644 --- a/packages/hedera-identify-snap/packages/site/README.md +++ b/packages/hedera-identify-snap/packages/site/README.md @@ -4,6 +4,12 @@ This project was bootstrapped with [Gatsby](https://www.gatsbyjs.com/). ## Available Scripts +Copy the .env.example file and update the value of `GATSBY_GOOGLE_DRIVE_CLIENT_ID` if you're testing `Configure Google Account API` from the example site: + +```sh +cp .env.example .env.development +``` + In the project directory, you can run: ### `yarn start` @@ -40,4 +46,4 @@ To learn more visit [Gatsby documentation](https://www.gatsbyjs.com/docs/how-to/ You can learn more in the [Gatsby documentation](https://www.gatsbyjs.com/docs/). -To learn React, check out the [React documentation](https://reactjs.org/). \ No newline at end of file +To learn React, check out the [React documentation](https://reactjs.org/). diff --git a/packages/hedera-identify-snap/packages/site/package.json b/packages/hedera-identify-snap/packages/site/package.json index 1f06f4af..a0cbba96 100644 --- a/packages/hedera-identify-snap/packages/site/package.json +++ b/packages/hedera-identify-snap/packages/site/package.json @@ -26,7 +26,7 @@ }, "dependencies": { "@metamask/providers": "^17.2.0", - "@react-oauth/google": "^0.7.0", + "@react-oauth/google": "^0.12.1", "@veramo/core": "5.1.2", "axios": "^1.2.4", "bootstrap": "^5.3.3", diff --git a/packages/hedera-identify-snap/packages/site/src/App.tsx b/packages/hedera-identify-snap/packages/site/src/App.tsx index 7a5c950b..b0b08690 100644 --- a/packages/hedera-identify-snap/packages/site/src/App.tsx +++ b/packages/hedera-identify-snap/packages/site/src/App.tsx @@ -44,6 +44,11 @@ export type AppProps = { export const App: FunctionComponent = ({ children }) => { const toggleTheme = useContext(ToggleThemeContext); + console.log( + 'process.env.GATSBY_GOOGLE_DRIVE_CLIENT_ID', + process.env.GATSBY_GOOGLE_DRIVE_CLIENT_ID, + ); + return ( <> diff --git a/packages/hedera-identify-snap/packages/site/src/components/base/Buttons.tsx b/packages/hedera-identify-snap/packages/site/src/components/base/Buttons.tsx index 6d7dd2fa..4408b046 100644 --- a/packages/hedera-identify-snap/packages/site/src/components/base/Buttons.tsx +++ b/packages/hedera-identify-snap/packages/site/src/components/base/Buttons.tsx @@ -138,15 +138,11 @@ type ButtonProps = ComponentProps & { buttonText: string; }; -export const SendHelloButton = ({ - loading, - buttonText, - ...props -}: ButtonProps) => { +export const SendHelloButton = ({ loading, ...props }: ButtonProps) => { return ( ); }; diff --git a/packages/hedera-identify-snap/packages/site/src/components/base/Card.tsx b/packages/hedera-identify-snap/packages/site/src/components/base/Card.tsx index 20edf704..6344855a 100644 --- a/packages/hedera-identify-snap/packages/site/src/components/base/Card.tsx +++ b/packages/hedera-identify-snap/packages/site/src/components/base/Card.tsx @@ -73,7 +73,7 @@ export const Card = ({ content, disabled = false, fullWidth }: CardProps) => { const { title, description, button, form } = content; return ( - {title} + {title && {title}} {description} {form} {button} diff --git a/packages/hedera-identify-snap/packages/site/src/components/base/Header.tsx b/packages/hedera-identify-snap/packages/site/src/components/base/Header.tsx index 11c9b0f4..e7719b8c 100644 --- a/packages/hedera-identify-snap/packages/site/src/components/base/Header.tsx +++ b/packages/hedera-identify-snap/packages/site/src/components/base/Header.tsx @@ -24,10 +24,9 @@ import { MetamaskActions, MetaMaskContext, } from '../../contexts/MetamaskContext'; -import { connectSnap, getSnap, getThemePreference } from '../../utils'; +import { connectSnap, getSnap } from '../../utils'; import { HeaderButtons } from './Buttons'; import { SnapLogo } from './SnapLogo'; -import { Toggle } from './Toggle'; const HeaderWrapper = styled.header` display: flex; @@ -88,13 +87,15 @@ export const Header = ({ - identify-snap + hedera-identify-snap - + {/* { + + } */} diff --git a/packages/hedera-identify-snap/packages/site/src/components/cards/ConfigureGoogleAccount.tsx b/packages/hedera-identify-snap/packages/site/src/components/cards/ConfigureGoogleAccount.tsx index 0233cced..5c2a5525 100644 --- a/packages/hedera-identify-snap/packages/site/src/components/cards/ConfigureGoogleAccount.tsx +++ b/packages/hedera-identify-snap/packages/site/src/components/cards/ConfigureGoogleAccount.tsx @@ -63,7 +63,6 @@ const ConfigureGoogleAccount: FC = ({ externalAccountRef.current?.handleGetAccountParams(); const configured = await configureGoogleAccount( - metamaskAddress, tokenResponse.access_token, externalAccountParams, ); @@ -91,7 +90,7 @@ const ConfigureGoogleAccount: FC = ({ button: ( handleConfigureGoogleAccount()} disabled={!state.installedSnap} loading={loading} /> diff --git a/packages/hedera-identify-snap/packages/site/src/components/cards/ConnectIdentitySnap.tsx b/packages/hedera-identify-snap/packages/site/src/components/cards/ConnectIdentifySnap.tsx similarity index 100% rename from packages/hedera-identify-snap/packages/site/src/components/cards/ConnectIdentitySnap.tsx rename to packages/hedera-identify-snap/packages/site/src/components/cards/ConnectIdentifySnap.tsx diff --git a/packages/hedera-identify-snap/packages/site/src/components/cards/CreateVC.tsx b/packages/hedera-identify-snap/packages/site/src/components/cards/CreateVC.tsx index 177b2264..1443823d 100644 --- a/packages/hedera-identify-snap/packages/site/src/components/cards/CreateVC.tsx +++ b/packages/hedera-identify-snap/packages/site/src/components/cards/CreateVC.tsx @@ -84,7 +84,6 @@ const CreateVC: FC = ({ setMetamaskAddress, setCurrentChainId }) => { }; const credTypes = ['ProfileNamesCredential']; const saved: CreateVCResponseResult = (await createVC( - metamaskAddress, vcKey, vcValue, options, diff --git a/packages/hedera-identify-snap/packages/site/src/components/cards/DeleteAllVCs.tsx b/packages/hedera-identify-snap/packages/site/src/components/cards/DeleteAllVCs.tsx index d398b6f9..26a8490d 100644 --- a/packages/hedera-identify-snap/packages/site/src/components/cards/DeleteAllVCs.tsx +++ b/packages/hedera-identify-snap/packages/site/src/components/cards/DeleteAllVCs.tsx @@ -27,7 +27,10 @@ import { } from '../../contexts/MetamaskContext'; import { VcContext } from '../../contexts/VcContext'; import useModal from '../../hooks/useModal'; -import { IDataManagerClearResult } from '../../types/veramo'; +import { + IDataManagerClearArgs, + IDataManagerClearResult, +} from '../../types/veramo'; import { deleteAllVCs, getCurrentMetamaskAccount, @@ -72,9 +75,8 @@ const DeleteAllVCs: FC = ({ setMetamaskAddress, setCurrentChainId }) => { // If you want to remove the VCs from multiple stores, you can pass an array like so: // store: ['snap', 'googleDrive'], ...(selectedStore.length ? { store: selectedStore } : {}), - }; + } as IDataManagerClearArgs; const isRemoved = (await deleteAllVCs( - metamaskAddress, options, externalAccountParams, )) as IDataManagerClearResult[]; diff --git a/packages/hedera-identify-snap/packages/site/src/components/cards/GetAccountInfo.tsx b/packages/hedera-identify-snap/packages/site/src/components/cards/GetAccountInfo.tsx index 1069e68d..eaba3801 100644 --- a/packages/hedera-identify-snap/packages/site/src/components/cards/GetAccountInfo.tsx +++ b/packages/hedera-identify-snap/packages/site/src/components/cards/GetAccountInfo.tsx @@ -63,10 +63,7 @@ const GetAccountInfo: FC = ({ const externalAccountParams = externalAccountRef.current?.handleGetAccountParams(); - const accountInfo = await getAccountInfo( - metamaskAddress, - externalAccountParams, - ); + const accountInfo = await getAccountInfo(externalAccountParams); console.log(`Your account info:`, accountInfo); setAccountInfo(accountInfo as PublicAccountInfo); showModal({ diff --git a/packages/hedera-identify-snap/packages/site/src/components/cards/GetAllVCs.tsx b/packages/hedera-identify-snap/packages/site/src/components/cards/GetAllVCs.tsx index e9de7194..ffc7f31a 100644 --- a/packages/hedera-identify-snap/packages/site/src/components/cards/GetAllVCs.tsx +++ b/packages/hedera-identify-snap/packages/site/src/components/cards/GetAllVCs.tsx @@ -75,7 +75,6 @@ const GetAllVCs: FC = ({ setMetamaskAddress, setCurrentChainId }) => { returnStore: true, }; const vcs = (await getVCs( - metamaskAddress, undefined, options, externalAccountParams, diff --git a/packages/hedera-identify-snap/packages/site/src/components/cards/GetSpecificVC.tsx b/packages/hedera-identify-snap/packages/site/src/components/cards/GetSpecificVC.tsx index 52e7f347..c4a0db71 100644 --- a/packages/hedera-identify-snap/packages/site/src/components/cards/GetSpecificVC.tsx +++ b/packages/hedera-identify-snap/packages/site/src/components/cards/GetSpecificVC.tsx @@ -80,7 +80,6 @@ const GetSpecificVC: FC = ({ returnStore: true, }; const vcs = (await getVCs( - metamaskAddress, filter, options, externalAccountParams, diff --git a/packages/hedera-identify-snap/packages/site/src/components/cards/GetVP.tsx b/packages/hedera-identify-snap/packages/site/src/components/cards/GetVP.tsx index af683afc..f2d888a4 100644 --- a/packages/hedera-identify-snap/packages/site/src/components/cards/GetVP.tsx +++ b/packages/hedera-identify-snap/packages/site/src/components/cards/GetVP.tsx @@ -69,7 +69,6 @@ const GetVP: FC = ({ setMetamaskAddress, setCurrentChainId }) => { console.log('vcIds: ', vcId); // console.log('vc: ', vc); const vp = (await createVP( - metamaskAddress, { // vcIds: vcId.trim().split(','), vcs: [vc as W3CVerifiableCredential], diff --git a/packages/hedera-identify-snap/packages/site/src/components/cards/RemoveVC.tsx b/packages/hedera-identify-snap/packages/site/src/components/cards/RemoveVC.tsx index f9059ce3..2053ae73 100644 --- a/packages/hedera-identify-snap/packages/site/src/components/cards/RemoveVC.tsx +++ b/packages/hedera-identify-snap/packages/site/src/components/cards/RemoveVC.tsx @@ -80,7 +80,6 @@ const RemoveVC: FC = ({ setMetamaskAddress, setCurrentChainId }) => { } as IDataManagerDeleteArgs; console.log('vcIdsToBeRemoved: ', vcIdsToBeRemoved); const isRemoved = (await removeVC( - metamaskAddress, id, options, externalAccountParams, diff --git a/packages/hedera-identify-snap/packages/site/src/components/cards/ResolveDID.tsx b/packages/hedera-identify-snap/packages/site/src/components/cards/ResolveDID.tsx index c512226b..c5c06cc9 100644 --- a/packages/hedera-identify-snap/packages/site/src/components/cards/ResolveDID.tsx +++ b/packages/hedera-identify-snap/packages/site/src/components/cards/ResolveDID.tsx @@ -58,7 +58,7 @@ const ResolveDID: FC = ({ setMetamaskAddress, setCurrentChainId }) => { const externalAccountParams = externalAccountRef.current?.handleGetAccountParams(); - const doc = await resolveDID(metamaskAddress, did, externalAccountParams); + const doc = await resolveDID(did, externalAccountParams); console.log(`Your DID document is : ${JSON.stringify(doc, null, 4)}`); showModal({ title: 'Resolve DID', diff --git a/packages/hedera-identify-snap/packages/site/src/components/cards/SendHelloHessage.tsx b/packages/hedera-identify-snap/packages/site/src/components/cards/SendHelloHessage.tsx index a632fcfa..4128afc6 100644 --- a/packages/hedera-identify-snap/packages/site/src/components/cards/SendHelloHessage.tsx +++ b/packages/hedera-identify-snap/packages/site/src/components/cards/SendHelloHessage.tsx @@ -48,7 +48,7 @@ const SendHelloHessage: FC = ({ setMetamaskAddress(metamaskAddress); setCurrentChainId(await getCurrentNetwork()); - await sendHello(metamaskAddress); + await sendHello(); } catch (e) { console.error(e); dispatch({ type: MetamaskActions.SetError, payload: e }); diff --git a/packages/hedera-identify-snap/packages/site/src/components/cards/SwitchDIDMethod.tsx b/packages/hedera-identify-snap/packages/site/src/components/cards/SwitchDIDMethod.tsx index 1c08dc2e..4072c484 100644 --- a/packages/hedera-identify-snap/packages/site/src/components/cards/SwitchDIDMethod.tsx +++ b/packages/hedera-identify-snap/packages/site/src/components/cards/SwitchDIDMethod.tsx @@ -52,7 +52,7 @@ const SwitchDIDMethod: FC = ({ setMetamaskAddress(metamaskAddress); setCurrentChainId(await getCurrentNetwork()); - const switched = await switchDIDMethod(metamaskAddress, didMethod); + const switched = await switchDIDMethod(didMethod); console.log(`DID Method switched : ${switched}`); } catch (e) { console.error(e); diff --git a/packages/hedera-identify-snap/packages/site/src/components/cards/SyncGoogleVCs.tsx b/packages/hedera-identify-snap/packages/site/src/components/cards/SyncGoogleVCs.tsx index 5138bacd..6d7e592c 100644 --- a/packages/hedera-identify-snap/packages/site/src/components/cards/SyncGoogleVCs.tsx +++ b/packages/hedera-identify-snap/packages/site/src/components/cards/SyncGoogleVCs.tsx @@ -58,7 +58,7 @@ const SyncGoogleVCs: FC = ({ const externalAccountParams = externalAccountRef.current?.handleGetAccountParams(); - const resp = await syncGoogleVCs(metamaskAddress, externalAccountParams); + const resp = await syncGoogleVCs(externalAccountParams); console.log('Synced with google drive: ', resp); } catch (e) { console.error(e); diff --git a/packages/hedera-identify-snap/packages/site/src/components/cards/ToggleMetamaskPopups.tsx b/packages/hedera-identify-snap/packages/site/src/components/cards/ToggleMetamaskPopups.tsx index a1803488..98a72461 100644 --- a/packages/hedera-identify-snap/packages/site/src/components/cards/ToggleMetamaskPopups.tsx +++ b/packages/hedera-identify-snap/packages/site/src/components/cards/ToggleMetamaskPopups.tsx @@ -48,7 +48,7 @@ const ToggleMetamaskPopups: FC = ({ setMetamaskAddress(metamaskAddress); setCurrentChainId(await getCurrentNetwork()); - await togglePopups(metamaskAddress); + await togglePopups(); } catch (e) { console.error(e); dispatch({ type: MetamaskActions.SetError, payload: e }); diff --git a/packages/hedera-identify-snap/packages/site/src/components/cards/VerifyVC.tsx b/packages/hedera-identify-snap/packages/site/src/components/cards/VerifyVC.tsx index b2cd86cf..95428744 100644 --- a/packages/hedera-identify-snap/packages/site/src/components/cards/VerifyVC.tsx +++ b/packages/hedera-identify-snap/packages/site/src/components/cards/VerifyVC.tsx @@ -51,7 +51,7 @@ const VerifyVC: FC = ({ setMetamaskAddress, setCurrentChainId }) => { setMetamaskAddress(metamaskAddress); setCurrentChainId(await getCurrentNetwork()); - const verified = await verifyVC(metamaskAddress, vc); + const verified = await verifyVC(vc); console.log('VC Verified: ', verified); showModal({ title: 'Verify VC', content: `VC Verified: ${verified}` }); } catch (e) { diff --git a/packages/hedera-identify-snap/packages/site/src/components/cards/VerifyVP.tsx b/packages/hedera-identify-snap/packages/site/src/components/cards/VerifyVP.tsx index 6fcfb078..8f801b6b 100644 --- a/packages/hedera-identify-snap/packages/site/src/components/cards/VerifyVP.tsx +++ b/packages/hedera-identify-snap/packages/site/src/components/cards/VerifyVP.tsx @@ -51,7 +51,7 @@ const VerifyVP: FC = ({ setMetamaskAddress, setCurrentChainId }) => { setMetamaskAddress(metamaskAddress); setCurrentChainId(await getCurrentNetwork()); - const verified = await verifyVP(metamaskAddress, vp); + const verified = await verifyVP(vp); console.log('VP Verified: ', verified); showModal({ title: 'Verify VP', diff --git a/packages/hedera-identify-snap/packages/site/src/components/cards/index.ts b/packages/hedera-identify-snap/packages/site/src/components/cards/index.ts index af21f59e..5fdeef83 100644 --- a/packages/hedera-identify-snap/packages/site/src/components/cards/index.ts +++ b/packages/hedera-identify-snap/packages/site/src/components/cards/index.ts @@ -19,7 +19,7 @@ */ export { ConfigureGoogleAccount } from './ConfigureGoogleAccount'; -export { ConnectIdentitySnap } from './ConnectIdentitySnap'; +export { ConnectIdentitySnap } from './ConnectIdentifySnap'; export { CreateVC } from './CreateVC'; export { DeleteAllVCs } from './DeleteAllVCs'; export { GetAccountInfo } from './GetAccountInfo'; diff --git a/packages/hedera-identify-snap/packages/site/src/utils/hedera.ts b/packages/hedera-identify-snap/packages/site/src/utils/hedera.ts index cdf74155..d1c5e9f9 100644 --- a/packages/hedera-identify-snap/packages/site/src/utils/hedera.ts +++ b/packages/hedera-identify-snap/packages/site/src/utils/hedera.ts @@ -26,7 +26,6 @@ const hederaChainIDs = new Map([ ['0x127', 'mainnet'], ['0x128', 'testnet'], ['0x129', 'previewnet'], - ['0x12a', 'localnet'], ]); export const getHederaNetwork = (chainId: string): string => { @@ -37,30 +36,175 @@ export const getHederaNetwork = (chainId: string): string => { export const validHederaChainID = (x: string) => isIn(Array.from(hederaChainIDs.keys()), x); -const evmChainIDs = new Map([ - ['0x1', 'Ethereum Mainnet'], - ['0x89', 'Polygon Mainnet'], - ['0xa4b1', 'Arbitrum One'], - ['0xa', 'Optimism'], - ['0x38', 'Binance Smart Chain Mainnet'], - ['0xe', 'Flare Mainnet'], - ['0x13', 'Songbird Canary-Network'], - ['0x13a', 'Filecoin - Mainnet'], - ['0x2329', 'Evmos'], - ['0x14', 'Elastos Smart Chain'], - ['0x5', 'Goerli Testnet'], -]); - -const validEVMChainID = (x: string) => isIn(Array.from(evmChainIDs.keys()), x); +const networks = { + '1': 'ethereum', + '8': 'ubiq', + '10': 'optimism', + '14': 'flare', + '19': 'songbird', + '20': 'elastos', + '24': 'kardia', + '25': 'cronos', + '30': 'rsk', + '40': 'telos', + '42': 'lukso', + '44': 'crab', + '50': 'xdc', + '52': 'csc', + '55': 'zyx', + '56': 'binance', + '57': 'syscoin', + '60': 'gochain', + '61': 'ethereumclassic', + '66': 'okexchain', + '70': 'hoo', + '82': 'meter', + '87': 'nova network', + '88': 'tomochain', + '96': 'bitkub', + '100': 'xdai', + '106': 'velas', + '108': 'thundercore', + '119': 'enuls', + '122': 'fuse', + '128': 'heco', + '137': 'polygon', + '148': 'shimmer_evm', + '151': 'rbn', + '169': 'manta', + '196': 'xlayer', + '200': 'xdaiarb', + '204': 'op_bnb', + '246': 'energyweb', + '248': 'oasys', + '250': 'fantom', + '252': 'fraxtal', + '269': 'hpb', + '288': 'boba', + '295': 'hedera mainnet', + '296': 'hedera testnet', + '297': 'hedera previewnet', + '311': 'omax', + '314': 'filecoin', + '321': 'kucoin', + '324': 'zksync era', + '336': 'shiden', + '361': 'theta', + '369': 'pulse', + '388': 'cronos zkevm', + '416': 'sx', + '463': 'areon', + '480': 'wc', + '534': 'candle', + '570': 'rollux', + '592': 'astar', + '698': 'matchain', + '820': 'callisto', + '888': 'wanchain', + '957': 'lyra chain', + '996': 'bifrost', + '1030': 'conflux', + '1088': 'metis', + '1100': 'dymension', + '1101': 'polygon zkevm', + '1116': 'core', + '1135': 'lisk', + '1231': 'ultron', + '1234': 'step', + '1284': 'moonbeam', + '1285': 'moonriver', + '1440': 'living assets mainnet', + '1559': 'tenet', + '1625': 'gravity', + '1729': 'reya network', + '1975': 'onus', + '1992': 'hubblenet', + '1996': 'sanko', + '2000': 'dogechain', + '2001': 'milkomeda', + '2002': 'milkomeda_a1', + '2222': 'kava', + '2332': 'soma', + '2818': 'morph', + '4337': 'beam', + '4689': 'iotex', + '5000': 'mantle', + '5050': 'xlc', + '5551': 'nahmii', + '6001': 'bouncebit', + '6969': 'tombchain', + '7000': 'zetachain', + '7070': 'planq', + '7171': 'bitrock', + '7560': 'cyeth', + '7700': 'canto', + '8217': 'klaytn', + '8428': 'that', + '8453': 'base', + '8668': 'hela', + '8822': 'iotaevm', + '8899': 'jbc', + '9001': 'evmos', + '9790': 'carbon', + '10000': 'smartbch', + '13371': 'immutable zkevm', + '15551': 'loop', + '16507': 'genesys', + '17777': 'eos evm', + '22776': 'map protocol', + '23294': 'oasis_sapphire', + '32520': 'bitgert', + '32659': 'fusion', + '32769': 'zilliqa', + '33139': 'apechain', + '42161': 'arbitrum', + '42170': 'arbitrum nova', + '42220': 'celo', + '42262': 'oasis', + '42793': 'etherlink', + '43114': 'avalanche', + '47805': 'rei', + '48900': 'zircuit', + '52014': 'etn', + '55555': 'reichain', + '56288': 'boba_bnb', + '59144': 'linea', + '60808': 'bob', + '71402': 'godwoken', + '81457': 'blast', + '88888': 'chiliz', + '111188': 'real', + '167000': 'taiko', + '200901': 'bitlayer', + '333999': 'polis', + '534352': 'scroll', + '420420': 'kekchain', + '810180': 'zklink nova', + '888888': 'vision', + '7225878': 'saakuru', + '7777777': 'zora', + '245022934': 'neon', + '1313161554': 'aurora', + '1666600000': 'harmony', + '11297108109': 'palm', + '383414847825': 'zeniq', + '836542336838601': 'curio', +} as Record; export const getNetwork = (chainId: string): string => { - if (chainId) { - if (validHederaChainID(chainId)) { - return `Hedera - ${getHederaNetwork(chainId)}`; - } else if (validEVMChainID(chainId)) { - return `EVM Chain - ${evmChainIDs.get(chainId)}`; - } - return `EVM Chain - Chain Id: ${chainId}`; + // Convert hexadecimal chainId to decimal + const chainIdDecimal = parseInt(chainId, 16).toString(); + + // Find the network name + const networkName = networks[chainIdDecimal]; + + // Capitalize each word in the network name + if (networkName) { + return networkName + .split(' ') + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' '); + } else { + return `EVM Network: ${chainIdDecimal}`; } - return ''; }; diff --git a/packages/hedera-identify-snap/packages/site/src/utils/snap.ts b/packages/hedera-identify-snap/packages/site/src/utils/snap.ts index c714580f..784627b4 100644 --- a/packages/hedera-identify-snap/packages/site/src/utils/snap.ts +++ b/packages/hedera-identify-snap/packages/site/src/utils/snap.ts @@ -101,14 +101,14 @@ export const getCurrentNetwork = async (): Promise => { * Invoke the "hello" method from the snap. */ -export const sendHello = async (metamaskAddress: string) => { +export const sendHello = async () => { return await window.ethereum.request({ method: 'wallet_invokeSnap', params: { snapId: defaultSnapId, request: { method: 'hello', - params: { metamaskAddress }, + params: {}, }, }, }); @@ -118,12 +118,12 @@ export const sendHello = async (metamaskAddress: string) => { * Invoke the "togglePopups" method from the snap. */ -export const togglePopups = async (metamaskAddress: string) => { +export const togglePopups = async () => { return await window.ethereum.request({ method: 'wallet_invokeSnap', params: { snapId: defaultSnapId, - request: { method: 'togglePopups', params: { metamaskAddress } }, + request: { method: 'togglePopups', params: {} }, }, }); }; @@ -131,17 +131,14 @@ export const togglePopups = async (metamaskAddress: string) => { /** * Invoke the "switchDIDMethod" method from the snap. */ -export const switchDIDMethod = async ( - metamaskAddress: string, - didMethod: string, -) => { +export const switchDIDMethod = async (didMethod: string) => { return await window.ethereum.request({ method: 'wallet_invokeSnap', params: { snapId: defaultSnapId, request: { method: 'switchDIDMethod', - params: { metamaskAddress, didMethod }, + params: { didMethod }, }, }, }); @@ -151,7 +148,6 @@ export const switchDIDMethod = async ( * Invoke the "getAccountInfo" method from the snap. */ export const getAccountInfo = async ( - metamaskAddress: string, externalAccountparams?: ExternalAccountParams, ) => { return await window.ethereum.request({ @@ -160,7 +156,7 @@ export const getAccountInfo = async ( snapId: defaultSnapId, request: { method: 'getAccountInfo', - params: { metamaskAddress, ...externalAccountparams }, + params: { ...externalAccountparams }, }, }, }); @@ -170,7 +166,6 @@ export const getAccountInfo = async ( * Invoke the "resolveDID" method from the snap. */ export const resolveDID = async ( - metamaskAddress: string, did?: string, externalAccountparams?: ExternalAccountParams, ) => { @@ -180,7 +175,10 @@ export const resolveDID = async ( snapId: defaultSnapId, request: { method: 'resolveDID', - params: { metamaskAddress, did, ...externalAccountparams }, + params: { + ...(did ? { did } : {}), + ...externalAccountparams, + }, }, }, }); @@ -191,7 +189,6 @@ export const resolveDID = async ( */ export const getVCs = async ( - metamaskAddress: string, filter: Filter | undefined, options: any, externalAccountparams?: ExternalAccountParams, @@ -202,7 +199,7 @@ export const getVCs = async ( snapId: defaultSnapId, request: { method: 'getVCs', - params: { metamaskAddress, filter, options, ...externalAccountparams }, + params: { filter, options, ...externalAccountparams }, }, }, }); @@ -213,7 +210,6 @@ export const getVCs = async ( */ export const saveVC = async ( - metamaskAddress: string, data: unknown, externalAccountparams?: ExternalAccountParams, ) => { @@ -224,7 +220,6 @@ export const saveVC = async ( request: { method: 'saveVC', params: { - metamaskAddress, data, ...externalAccountparams, }, @@ -243,7 +238,6 @@ export type ExampleVCValue = { */ export const createVC = async ( - metamaskAddress: string, vcKey: string, vcValue: object, options: any, @@ -257,7 +251,6 @@ export const createVC = async ( request: { method: 'createVC', params: { - metamaskAddress, vcKey, vcValue, options, @@ -273,17 +266,14 @@ export const createVC = async ( * Invoke the "verifyVC" method from the snap. */ -export const verifyVC = async ( - metamaskAddress: string, - vc: VerifiableCredential | {}, -) => { +export const verifyVC = async (vc: VerifiableCredential | {}) => { return await window.ethereum.request({ method: 'wallet_invokeSnap', params: { snapId: defaultSnapId, request: { method: 'verifyVC', - params: { metamaskAddress, verifiableCredential: vc }, + params: { verifiableCredential: vc }, }, }, }); @@ -294,7 +284,6 @@ export const verifyVC = async ( */ export const removeVC = async ( - metamaskAddress: string, id: string | string[], options: IDataManagerDeleteArgs, externalAccountparams?: ExternalAccountParams, @@ -305,7 +294,7 @@ export const removeVC = async ( snapId: defaultSnapId, request: { method: 'removeVC', - params: { metamaskAddress, id, options, ...externalAccountparams }, + params: { id, options, ...externalAccountparams }, }, }, }); @@ -316,7 +305,6 @@ export const removeVC = async ( */ export const deleteAllVCs = async ( - metamaskAddress: string, options: IDataManagerClearArgs, externalAccountparams?: ExternalAccountParams, ) => { @@ -326,7 +314,7 @@ export const deleteAllVCs = async ( snapId: defaultSnapId, request: { method: 'deleteAllVCs', - params: { metamaskAddress, options, ...externalAccountparams }, + params: { options, ...externalAccountparams }, }, }, }); @@ -337,7 +325,6 @@ export const deleteAllVCs = async ( */ export const createVP = async ( - metamaskAddress: string, { vcIds, vcs, proofInfo }: CreateVPRequestParams, externalAccountparams?: ExternalAccountParams, ) => { @@ -348,7 +335,6 @@ export const createVP = async ( request: { method: 'createVP', params: { - metamaskAddress, vcIds, vcs, proofInfo, @@ -363,17 +349,14 @@ export const createVP = async ( * Invoke the "verifyVP" method from the snap. */ -export const verifyVP = async ( - metamaskAddress: string, - vp: VerifiablePresentation | {}, -) => { +export const verifyVP = async (vp: VerifiablePresentation | {}) => { return await window.ethereum.request({ method: 'wallet_invokeSnap', params: { snapId: defaultSnapId, request: { method: 'verifyVP', - params: { metamaskAddress, verifiablePresentation: vp }, + params: { verifiablePresentation: vp }, }, }, }); @@ -384,7 +367,6 @@ export const verifyVP = async ( */ export const getCurrentDIDMethod = async ( - metamaskAddress: string, externalAccountparams?: ExternalAccountParams, ) => { return await window.ethereum.request({ @@ -393,7 +375,7 @@ export const getCurrentDIDMethod = async ( snapId: defaultSnapId, request: { method: 'getCurrentDIDMethod', - params: { metamaskAddress, ...externalAccountparams }, + params: { ...externalAccountparams }, }, }, }); @@ -404,7 +386,6 @@ export const getCurrentDIDMethod = async ( */ export const configureGoogleAccount = async ( - metamaskAddress: string, accessToken: string, externalAccountparams?: ExternalAccountParams, ) => { @@ -414,7 +395,7 @@ export const configureGoogleAccount = async ( snapId: defaultSnapId, request: { method: 'configureGoogleAccount', - params: { metamaskAddress, accessToken, ...externalAccountparams }, + params: { accessToken, ...externalAccountparams }, }, }, }); @@ -425,7 +406,6 @@ export const configureGoogleAccount = async ( */ export const syncGoogleVCs = async ( - metamaskAddress: string, externalAccountparams?: ExternalAccountParams, ) => { return await window.ethereum.request({ @@ -434,7 +414,7 @@ export const syncGoogleVCs = async ( snapId: defaultSnapId, request: { method: 'syncGoogleVCs', - params: { metamaskAddress, ...externalAccountparams }, + params: { ...externalAccountparams }, }, }, }); diff --git a/packages/hedera-identify-snap/packages/snap/.eslintignore b/packages/hedera-identify-snap/packages/snap/.eslintignore index 471e4a81..03547d80 100644 --- a/packages/hedera-identify-snap/packages/snap/.eslintignore +++ b/packages/hedera-identify-snap/packages/snap/.eslintignore @@ -1,7 +1,7 @@ build +coverage dist node_modules .eslintrc.js -coverage src/veramo -src/** +src/** \ No newline at end of file diff --git a/packages/hedera-identify-snap/packages/snap/.eslintrc.js b/packages/hedera-identify-snap/packages/snap/.eslintrc.js index 59b0855f..aaf84cc7 100644 --- a/packages/hedera-identify-snap/packages/snap/.eslintrc.js +++ b/packages/hedera-identify-snap/packages/snap/.eslintrc.js @@ -42,7 +42,7 @@ module.exports = { '!.eslintrc.js', 'coverage/', 'dist/', - './post-process.js', + './postBuild.js', 'jest.config.js', ], }; diff --git a/packages/hedera-identify-snap/packages/snap/.prettierignore b/packages/hedera-identify-snap/packages/snap/.prettierignore index 74f8164b..d444c864 100644 --- a/packages/hedera-identify-snap/packages/snap/.prettierignore +++ b/packages/hedera-identify-snap/packages/snap/.prettierignore @@ -1,4 +1,4 @@ dist coverage -post-process.js -node_modules \ No newline at end of file +postBuild.js +node_modules diff --git a/packages/hedera-identify-snap/packages/snap/CHANGELOG.md b/packages/hedera-identify-snap/packages/snap/CHANGELOG.md index cd1d32a3..bce45e9d 100644 --- a/packages/hedera-identify-snap/packages/snap/CHANGELOG.md +++ b/packages/hedera-identify-snap/packages/snap/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## [0.2.0](https://github.com/hashgraph/hedera-metamask-snaps/compare/v0.2.0...v0.2.0) (2024-10-11) +## 0.2.0 (2024-12-12) ### :page_with_curl: Documentation @@ -8,4 +8,11 @@ ### :rocket: Features +- Added support for did:key method +- Added support for did:hedera method +- Added snap home page +- Updated some dialog boxes +- Upgraded dependencies and optimized the code +- Refactored the entire codebase + ### :bug: Bug Fixes diff --git a/packages/hedera-identify-snap/packages/snap/README.md b/packages/hedera-identify-snap/packages/snap/README.md index e8cce866..108f7077 100644 --- a/packages/hedera-identify-snap/packages/snap/README.md +++ b/packages/hedera-identify-snap/packages/snap/README.md @@ -1,4 +1,4 @@ -# @tuum-tech/identify +# @hashgraph/identify This repository contains code for Identify Snap that offers various features such as configuring hedera account, getting current did method, getting DID, resolving DID, getting Verifiable Credentials, creating Verifiable Credentials out of some arbitary JSON object, generating Verifiable Presentations from Verifiable Credentials and verifying VCs and verifying VPs. Refer to the [Identify Snap Wiki](https://docs.tuum.tech/identify/) for more info on how the snap works and how to @@ -91,6 +91,7 @@ process; see those repositories for more information about how they work. - Babel is used for transpiling TypeScript to JavaScript, so when building with the CLI, `transpilationMode` must be set to `localOnly` (default) or `localAndDeps`. - For the global `wallet` type to work, you have to add the following to your `tsconfig.json`: + ```json { "files": ["./node_modules/@metamask/snap-types/global.d.ts"] diff --git a/packages/hedera-identify-snap/packages/snap/json-typings.d.ts b/packages/hedera-identify-snap/packages/snap/global.d.ts similarity index 93% rename from packages/hedera-identify-snap/packages/snap/json-typings.d.ts rename to packages/hedera-identify-snap/packages/snap/global.d.ts index 36b4a3a6..2f991f68 100644 --- a/packages/hedera-identify-snap/packages/snap/json-typings.d.ts +++ b/packages/hedera-identify-snap/packages/snap/global.d.ts @@ -23,3 +23,6 @@ declare module '*.json' { const value: any; export default value; } + +declare module 'secp256k1'; +declare module 'jsonpath'; diff --git a/packages/hedera-identify-snap/packages/snap/images/icon.svg b/packages/hedera-identify-snap/packages/snap/images/icon.svg old mode 100755 new mode 100644 diff --git a/packages/hedera-identify-snap/packages/snap/package.json b/packages/hedera-identify-snap/packages/snap/package.json index ec84edc1..ace4178e 100644 --- a/packages/hedera-identify-snap/packages/snap/package.json +++ b/packages/hedera-identify-snap/packages/snap/package.json @@ -53,37 +53,33 @@ "test:ci": "yarn test --silent" }, "dependencies": { - "@ethersproject/transactions": "^5.7.0", - "@hashgraph/sdk": "^2.51.0", - "@ipld/dag-pb": "^4.1.2", + "@hashgraph/sdk": "^2.50.0", "@metamask/key-tree": "^9.1.2", "@metamask/providers": "^17.2.0", "@metamask/rpc-errors": "^6.3.1", "@metamask/snaps-sdk": "^6.3.0", + "@tuum-tech/hedera-did-sdk-js": "^0.1.3", "@veramo/core": "5.1.2", - "@veramo/credential-eip712": "^6.0.0", "@veramo/credential-w3c": "5.1.2", "@veramo/did-jwt": "5.1.2", "@veramo/did-manager": "5.1.2", + "@veramo/did-provider-key": "6.0.0", "@veramo/did-provider-pkh": "5.1.2", "@veramo/did-resolver": "5.1.2", "@veramo/key-manager": "5.1.2", "@veramo/kms-local": "5.1.2", "@veramo/message-handler": "5.1.2", "bignumber.js": "^9.1.2", - "caip": "^1.1.1", - "did-jwt-vc": "^3.1.3", + "did-jwt-vc": "^4.0.4", "did-resolver": "^4.1.0", "ethers": "^6.13.2", "js-sha256": "^0.11.0", "jsonpath": "^1.1.1", "lodash": "^4.17.21", - "secp256k1": "^5.0.0", - "uuid": "^10.0.0" + "secp256k1": "^5.0.1" }, "devDependencies": { "@jest/globals": "^29.7.0", - "@lavamoat/allow-scripts": "^2.0.3", "@metamask/auto-changelog": "^3.4.4", "@metamask/eslint-config": "^12.2.0", "@metamask/eslint-config-jest": "^12.1.0", @@ -95,7 +91,6 @@ "@types/jsonpath": "^0.2.4", "@types/lodash.clonedeep": "^4.5.9", "@types/secp256k1": "^4.0.6", - "@types/uuid": "^10.0.0", "@typescript-eslint/eslint-plugin": "5.62.0", "@typescript-eslint/parser": "5.62.0", "eslint": "8.38.0", diff --git a/packages/hedera-identify-snap/packages/snap/snap.config.ts b/packages/hedera-identify-snap/packages/snap/snap.config.ts index eded522c..5f703f3e 100644 --- a/packages/hedera-identify-snap/packages/snap/snap.config.ts +++ b/packages/hedera-identify-snap/packages/snap/snap.config.ts @@ -29,20 +29,7 @@ const config: SnapConfig = { server: { port: 9002, }, - polyfills: { - buffer: true, - crypto: true, - http: true, - https: true, - zlib: true, - util: true, - url: true, - punycode: true, - events: true, - stream: true, - assert: true, - string_decoder: true, - }, + polyfills: true, }; export default config; diff --git a/packages/hedera-identify-snap/packages/snap/snap.manifest.json b/packages/hedera-identify-snap/packages/snap/snap.manifest.json index 70cb97a0..9dda4d4b 100644 --- a/packages/hedera-identify-snap/packages/snap/snap.manifest.json +++ b/packages/hedera-identify-snap/packages/snap/snap.manifest.json @@ -7,7 +7,7 @@ "url": "git+https://github.com/hashgraph/hedera-metamask-snaps.git" }, "source": { - "shasum": "DHyq8nTsEjIkJc/37mafBVLK/Sy8kxGsHx/1RKzDiU4=", + "shasum": "xNK4lj1zDx0oOllgSNRNSTjt3DEZVSgYLzEB6EJq5O8=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/hedera-identify-snap/packages/snap/src/client/HederaClientImplFactory.ts b/packages/hedera-identify-snap/packages/snap/src/client/HederaClientImplFactory.ts new file mode 100644 index 00000000..3b947867 --- /dev/null +++ b/packages/hedera-identify-snap/packages/snap/src/client/HederaClientImplFactory.ts @@ -0,0 +1,159 @@ +/*- + * + * 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 { + Client, + Hbar, + PrivateKey, + Status, + StatusError, + TransferTransaction, +} from '@hashgraph/sdk'; +import type { Wallet } from '../domain/wallet/abstract'; +import { PrivateKeySoftwareWallet } from '../domain/wallet/software-private-key'; +import type { HederaClientFactory } from '../interfaces/HederaClientFactory'; +import { SimpleHederaClientImpl } from './SimpleHederaClientImpl'; + +/** + * To HederaAccountInfo. + * @param _curve - Curve that was used to derive the keys('ECDSA_SECP256K1' | 'ED25519'). + * @param _privateKey - Private Key. + * @param _accountId - Account Id. + * @param _network - Network. + */ + +export class HederaClientImplFactory implements HederaClientFactory { + readonly #wallet: Wallet | null; + + readonly #keyIndex: number; + + readonly #accountId: string; + + readonly #network: string; + + readonly #curve: string; + + readonly #privateKey: string; + + constructor( + accountId: string, + network: string, + curve: string, + privateKey: string, + keyIndex = 0, // note that 0 is default here for the first key in a HD wallet + ) { + this.#keyIndex = keyIndex; + this.#accountId = accountId; + this.#network = network; + this.#curve = curve; + this.#privateKey = privateKey; + this.#wallet = this.walletFromPrivateKeyString(); + } + + walletFromPrivateKeyString(): Wallet | null { + let myPrivateKey: PrivateKey; + + if (this.#curve === 'Secp256k1') { + myPrivateKey = PrivateKey.fromStringECDSA(this.#privateKey); + } else if (this.#curve === 'Ed25519') { + myPrivateKey = PrivateKey.fromStringED25519(this.#privateKey); + } else { + console.error('Invalid curve type'); + return null; + } + + return new PrivateKeySoftwareWallet(myPrivateKey); + } + + public async createClient(): Promise { + if (this.#wallet === null) { + return null; + } + + let client: Client; + + if (this.#network === 'testnet') { + client = Client.forTestnet(); + } else if (this.#network === 'previewnet') { + client = Client.forPreviewnet(); + } else { + client = Client.forMainnet(); + } + + client.setNetworkUpdatePeriod(2000); + + const transactionSigner = await this.#wallet.getTransactionSigner( + this.#keyIndex, + ); + + const privateKey = await this.#wallet.getPrivateKey(this.#keyIndex); + const publicKey = await this.#wallet.getPublicKey(this.#keyIndex); + + if (publicKey === null) { + return null; + } + + // TODO: Fix + client.setOperatorWith(this.#accountId, publicKey ?? '', transactionSigner); + + if (!(await this.testClientOperatorMatch(client))) { + return null; + } + + return new SimpleHederaClientImpl(client, privateKey); + } + + /** + * Does the operator key belong to the operator account. + * @param client - Hedera Client. + * @returns True if the operator key belongs to the operator account. + */ + async testClientOperatorMatch(client: Client) { + const tx = new TransferTransaction() + /* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */ + .addHbarTransfer(client.operatorAccountId!, Hbar.fromTinybars(0)) + .setMaxTransactionFee(Hbar.fromTinybars(1)); + + try { + await tx.execute(client); + } catch (error: any) { + if (error instanceof StatusError) { + // If the transaction fails with Insufficient Tx Fee, this means + // that the account ID verification succeeded before this point + // Same for Insufficient Payer Balance + return ( + error.status === Status.InsufficientTxFee || + error.status === Status.InsufficientPayerBalance + ); + } + + const errMessage = + 'The account id does not belong to the associated private key'; + console.log(errMessage, String(error)); + throw new Error(errMessage); + } + + // under *no* cirumstances should this transaction succeed + const errMessage = + 'Unexpected success of intentionally-erroneous transaction to confirm account ID'; + console.log(errMessage); + throw new Error(errMessage); + } +} diff --git a/packages/hedera-identify-snap/packages/snap/src/client/SimpleHederaClientImpl.ts b/packages/hedera-identify-snap/packages/snap/src/client/SimpleHederaClientImpl.ts new file mode 100644 index 00000000..3d23c4b6 --- /dev/null +++ b/packages/hedera-identify-snap/packages/snap/src/client/SimpleHederaClientImpl.ts @@ -0,0 +1,58 @@ +/*- + * + * 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 { AccountId, Client, PrivateKey, PublicKey } from '@hashgraph/sdk'; + +import type { SimpleHederaClient } from '../types/hedera'; + +export class SimpleHederaClientImpl implements SimpleHederaClient { + // eslint-disable-next-line no-restricted-syntax + readonly #client: Client; + + // eslint-disable-next-line no-restricted-syntax + readonly #privateKey: PrivateKey | null; + + constructor(client: Client, privateKey: PrivateKey | null) { + this.#client = client; + this.#privateKey = privateKey; + } + + close() { + this.#client.close(); + } + + getClient(): Client { + return this.#client; + } + + getPrivateKey(): PrivateKey | null { + return this.#privateKey; + } + + getPublicKey(): PublicKey { + /* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */ + return this.#client.operatorPublicKey!; + } + + getAccountId(): AccountId { + /* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */ + return this.#client.operatorAccountId!; + } +} diff --git a/packages/hedera-identify-snap/packages/snap/src/components/common.tsx b/packages/hedera-identify-snap/packages/snap/src/components/common.tsx index b71052b8..1b62370b 100644 --- a/packages/hedera-identify-snap/packages/snap/src/components/common.tsx +++ b/packages/hedera-identify-snap/packages/snap/src/components/common.tsx @@ -1,6 +1,6 @@ /*- * - * Hedera Wallet Snap + * Hedera Identify Snap * * Copyright (C) 2024 Hedera Hashgraph, LLC * @@ -30,6 +30,7 @@ import { Row as MetamaskRow, Text as MetamaskText, } from '@metamask/snaps-sdk/jsx'; +import { getNetworkNameFromChainId } from '../constants'; export const Box = MetamaskBox as any; export const Heading = MetamaskHeading as any; @@ -44,7 +45,6 @@ export const Bold = MetamaskBold as any; export type CommonProps = { origin: string; network: string; - mirrorNodeUrl: string; [key: string]: any; }; @@ -56,7 +56,7 @@ const MetamaskUI: SnapComponent = ({ origin, network }) => { {origin} - {network} + {getNetworkNameFromChainId(network)} diff --git a/packages/hedera-identify-snap/packages/snap/src/components/hello.tsx b/packages/hedera-identify-snap/packages/snap/src/components/hello.tsx index 4ba1d24e..781f33fc 100644 --- a/packages/hedera-identify-snap/packages/snap/src/components/hello.tsx +++ b/packages/hedera-identify-snap/packages/snap/src/components/hello.tsx @@ -1,6 +1,6 @@ /*- * - * Hedera Wallet Snap + * Hedera Identify Snap * * Copyright (C) 2024 Hedera Hashgraph, LLC * diff --git a/packages/hedera-identify-snap/packages/snap/src/components/showAccountPrivateKey.tsx b/packages/hedera-identify-snap/packages/snap/src/components/showAccountPrivateKey.tsx index 414042c1..139a3b31 100644 --- a/packages/hedera-identify-snap/packages/snap/src/components/showAccountPrivateKey.tsx +++ b/packages/hedera-identify-snap/packages/snap/src/components/showAccountPrivateKey.tsx @@ -1,6 +1,6 @@ /*- * - * Hedera Wallet Snap + * Hedera Identify Snap * * Copyright (C) 2024 Hedera Hashgraph, LLC * @@ -33,35 +33,21 @@ import { type Props = CommonProps & { privateKey: string; publicKey: string; - accountID: string; evmAddress: string; }; const MetamaskUI: SnapComponent = ({ origin, network, - mirrorNodeUrl, privateKey, publicKey, - accountID, evmAddress, }) => { return ( - + Show Account Private Key - - Account ID - - - +
diff --git a/packages/hedera-identify-snap/packages/snap/src/utils/network.ts b/packages/hedera-identify-snap/packages/snap/src/constants/crypto.ts similarity index 84% rename from packages/hedera-identify-snap/packages/snap/src/utils/network.ts rename to packages/hedera-identify-snap/packages/snap/src/constants/crypto.ts index 0f89c617..1a24f873 100644 --- a/packages/hedera-identify-snap/packages/snap/src/utils/network.ts +++ b/packages/hedera-identify-snap/packages/snap/src/constants/crypto.ts @@ -18,6 +18,5 @@ * */ -export const convertChainIdFromHex = (chainId: string): string => { - return parseInt(chainId, 16).toString(); -}; +export const ECDSA_SECP256K1_KEY_TYPE = 'Secp256k1'; +export const ED25519_KEY_TYPE = 'Ed25519'; diff --git a/packages/hedera-identify-snap/packages/snap/src/constants/index.ts b/packages/hedera-identify-snap/packages/snap/src/constants/index.ts index 1c871549..4803285c 100644 --- a/packages/hedera-identify-snap/packages/snap/src/constants/index.ts +++ b/packages/hedera-identify-snap/packages/snap/src/constants/index.ts @@ -1 +1,3 @@ +export * from './crypto'; export * from './multicodec'; +export * from './network'; diff --git a/packages/hedera-identify-snap/packages/snap/src/constants/network.ts b/packages/hedera-identify-snap/packages/snap/src/constants/network.ts new file mode 100644 index 00000000..c1e23142 --- /dev/null +++ b/packages/hedera-identify-snap/packages/snap/src/constants/network.ts @@ -0,0 +1,206 @@ +/*- + * + * 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. + * + */ + +export const HEDERA_NETWORKS: string[] = ['mainnet', 'testnet', 'previewnet']; + +export const HEDERA_CHAIN_IDS: Record = { + '0x127': 'mainnet', + '0x128': 'testnet', + '0x129': 'previewnet', +}; + +export const DEFAULT_HEDERA_MIRRORNODES: Record = { + mainnet: 'https://mainnet-public.mirrornode.hedera.com', + testnet: 'https://testnet.mirrornode.hedera.com', + previewnet: 'https://previewnet.mirrornode.hedera.com', +}; + +const networks = { + '1': 'ethereum', + '8': 'ubiq', + '10': 'optimism', + '14': 'flare', + '19': 'songbird', + '20': 'elastos', + '24': 'kardia', + '25': 'cronos', + '30': 'rsk', + '40': 'telos', + '42': 'lukso', + '44': 'crab', + '50': 'xdc', + '52': 'csc', + '55': 'zyx', + '56': 'binance', + '57': 'syscoin', + '60': 'gochain', + '61': 'ethereumclassic', + '66': 'okexchain', + '70': 'hoo', + '82': 'meter', + '87': 'nova network', + '88': 'tomochain', + '96': 'bitkub', + '100': 'xdai', + '106': 'velas', + '108': 'thundercore', + '119': 'enuls', + '122': 'fuse', + '128': 'heco', + '137': 'polygon', + '148': 'shimmer_evm', + '151': 'rbn', + '169': 'manta', + '196': 'xlayer', + '200': 'xdaiarb', + '204': 'op_bnb', + '246': 'energyweb', + '248': 'oasys', + '250': 'fantom', + '252': 'fraxtal', + '269': 'hpb', + '288': 'boba', + '295': 'hedera mainnet', + '296': 'hedera testnet', + '297': 'hedera previewnet', + '311': 'omax', + '314': 'filecoin', + '321': 'kucoin', + '324': 'zksync era', + '336': 'shiden', + '361': 'theta', + '369': 'pulse', + '388': 'cronos zkevm', + '416': 'sx', + '463': 'areon', + '480': 'wc', + '534': 'candle', + '570': 'rollux', + '592': 'astar', + '698': 'matchain', + '820': 'callisto', + '888': 'wanchain', + '957': 'lyra chain', + '996': 'bifrost', + '1030': 'conflux', + '1088': 'metis', + '1100': 'dymension', + '1101': 'polygon zkevm', + '1116': 'core', + '1135': 'lisk', + '1231': 'ultron', + '1234': 'step', + '1284': 'moonbeam', + '1285': 'moonriver', + '1440': 'living assets mainnet', + '1559': 'tenet', + '1625': 'gravity', + '1729': 'reya network', + '1975': 'onus', + '1992': 'hubblenet', + '1996': 'sanko', + '2000': 'dogechain', + '2001': 'milkomeda', + '2002': 'milkomeda_a1', + '2222': 'kava', + '2332': 'soma', + '2818': 'morph', + '4337': 'beam', + '4689': 'iotex', + '5000': 'mantle', + '5050': 'xlc', + '5551': 'nahmii', + '6001': 'bouncebit', + '6969': 'tombchain', + '7000': 'zetachain', + '7070': 'planq', + '7171': 'bitrock', + '7560': 'cyeth', + '7700': 'canto', + '8217': 'klaytn', + '8428': 'that', + '8453': 'base', + '8668': 'hela', + '8822': 'iotaevm', + '8899': 'jbc', + '9001': 'evmos', + '9790': 'carbon', + '10000': 'smartbch', + '13371': 'immutable zkevm', + '15551': 'loop', + '16507': 'genesys', + '17777': 'eos evm', + '22776': 'map protocol', + '23294': 'oasis_sapphire', + '32520': 'bitgert', + '32659': 'fusion', + '32769': 'zilliqa', + '33139': 'apechain', + '42161': 'arbitrum', + '42170': 'arbitrum nova', + '42220': 'celo', + '42262': 'oasis', + '42793': 'etherlink', + '43114': 'avalanche', + '47805': 'rei', + '48900': 'zircuit', + '52014': 'etn', + '55555': 'reichain', + '56288': 'boba_bnb', + '59144': 'linea', + '60808': 'bob', + '71402': 'godwoken', + '81457': 'blast', + '88888': 'chiliz', + '111188': 'real', + '167000': 'taiko', + '200901': 'bitlayer', + '333999': 'polis', + '534352': 'scroll', + '420420': 'kekchain', + '810180': 'zklink nova', + '888888': 'vision', + '7225878': 'saakuru', + '7777777': 'zora', + '245022934': 'neon', + '1313161554': 'aurora', + '1666600000': 'harmony', + '11297108109': 'palm', + '383414847825': 'zeniq', + '836542336838601': 'curio', +} as Record; + +export function getNetworkNameFromChainId(chainIdHex: string): string { + // Convert hexadecimal chainId to decimal + const chainIdDecimal = parseInt(chainIdHex, 16).toString(); + + // Find the network name + const networkName = networks[chainIdDecimal]; + + // Capitalize each word in the network name + if (networkName) { + return networkName + .split(' ') + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' '); + } else { + return `EVM Network: ${chainIdDecimal}`; + } +} diff --git a/packages/hedera-identify-snap/packages/snap/src/custom-ui/onHome.ts b/packages/hedera-identify-snap/packages/snap/src/custom-ui/onHome.ts new file mode 100644 index 00000000..a6b392e1 --- /dev/null +++ b/packages/hedera-identify-snap/packages/snap/src/custom-ui/onHome.ts @@ -0,0 +1,166 @@ +/*- + * + * 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 { OnHomePageHandler } from '@metamask/snaps-sdk'; +import { + button, + copyable, + divider, + form, + heading, + input, + panel, + text, +} from '@metamask/snaps-sdk'; +import _ from 'lodash'; +import { getNetworkNameFromChainId } from '../constants'; +import { SnapAccounts } from '../snap/SnapAccounts'; +import { SnapState } from '../snap/SnapState'; +import { EvmUtils } from '../utils/EvmUtils'; + +export const OnHomePageUI: OnHomePageHandler = async () => { + const origin = 'Identify Snap'; + + let state = await SnapState.getStateUnchecked(); + if (_.isEmpty(state)) { + state = await SnapState.initState(); + } + + const network = await EvmUtils.getChainId(); + const connectedAddress = await SnapAccounts.getCurrentMetamaskAccount(); + + const snapAddress = await SnapAccounts.setCurrentAccount( + origin, + state, + null, + network, + false, + true, + ); + + return { + content: panel([ + heading('Identify Snap'), + text('Welcome to Identify Snap home page!'), + divider(), + text(`**Network**: ${getNetworkNameFromChainId(network)}`), + divider(), + text(`**Snap Account DID**:`), + copyable(state.currentAccount.identifier.did || 'Unavailable'), + text(`**Snap Account EVM Address**:`), + copyable(snapAddress || 'Unavailable'), + text(`**Snap Hedera Account ID**:`), + copyable(state.currentAccount.hederaAccountId || 'Unavailable'), + text(`**Associated Metamask Address**:`), + copyable(connectedAddress || 'Unavailable'), + text(`**Current DID Method**: ${state.currentAccount.method || 'None'}`), + divider(), + text(`**Switch DID Method**:`), + form({ + name: 'form-switch-did-method', + children: [ + input({ + name: 'didMethod', + placeholder: 'Methods: [did:pkh, did:key, did:hedera]', + value: state.currentAccount.method || '', + }), + button({ + value: 'Switch DID Method', + buttonType: 'submit', + }), + ], + }), + divider(), + text(`**Resolve DID**:`), + form({ + name: 'form-resolve-did', + children: [ + input({ + name: 'did', + placeholder: 'Enter DID string', + value: state.currentAccount.identifier.did || '', + }), + button({ + value: 'Resolve DID', + buttonType: 'submit', + }), + ], + }), + divider(), + text(`**Create a new Verifiable Credential**:`), + form({ + name: 'form-create-vc', + children: [ + input({ + name: 'vcText', + placeholder: 'Enter some text', + value: 'Sample text', + }), + button({ + value: 'Create VC', + buttonType: 'submit', + }), + ], + }), + divider(), + text(`**Retrieve Verifiable Credentials**:`), + form({ + name: 'form-get-vcs', + children: [ + button({ + value: 'Get VCs', + buttonType: 'submit', + }), + ], + }), + divider(), + text(`**Create Verifiable Presentation**:`), + form({ + name: 'form-create-vp', + children: [ + input({ + name: 'vcData', + placeholder: 'Enter your Verifiable Credential', + value: '', + }), + button({ + value: 'Create VP', + buttonType: 'submit', + }), + ], + }), + divider(), + button({ + value: 'Export Snap Account Private Key', + name: 'btn-export-snap-account-private-key', + }), + divider(), + text(`**FAQs**:`), + text( + `**Why is my MetaMask address different from my Snap EVM address?**: Since MetaMask does not allow Snaps to access the private keys of MetaMask accounts, Identify Snap creates a new account with its own private key. However, this new account is still associated with the MetaMask account so you can easily get back to your account on any device as long as you're connected to the same MetaMask account.`, + ), + text( + `**Is the Snap safe to use?**: Yes, the Snap is safe to use as it is run in a sandboxed environment and uses a permissions model to protect your data and respect your consent. The Snap does not have access to your MetaMask account data.`, + ), + divider(), + text(`[View Snap documentation](https://docs.tuum.tech/identify)`), + ]), + }; +}; diff --git a/packages/hedera-identify-snap/packages/snap/src/custom-ui/onInstall.ts b/packages/hedera-identify-snap/packages/snap/src/custom-ui/onInstall.ts new file mode 100644 index 00000000..84a95709 --- /dev/null +++ b/packages/hedera-identify-snap/packages/snap/src/custom-ui/onInstall.ts @@ -0,0 +1,49 @@ +/*- + * + * 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 { OnInstallHandler } from '@metamask/snaps-sdk'; +import { divider, heading, panel, text } from '@metamask/snaps-sdk'; + +export const onInstallUI: OnInstallHandler = async () => { + await snap.request({ + method: 'snap_dialog', + params: { + type: 'alert', + content: panel([ + heading('Thank you for installing Identify Snap'), + text( + 'To learn about the Snap, refer to [Identify Snap Documentation](https://docs.tuum.tech/identify/basics/introduction).', + ), + divider(), + text( + '🔑 Applications do NOT have access to your private keys. Everything is stored inside the sandbox environment of Identify inside MetaMask', + ), + divider(), + text( + '⦿ Note that Identify Snap does not have direct access to the private key of the MetaMask accounts so it generates a new snap account that is associated with the currently connected MetaMask account so the account created by the snap will have a different address compared to your MetaMask account address.', + ), + divider(), + text( + '😭 If you add a new account in MetaMask after you have already approved existing accounts on your application, you will need to reinstall the snap and reconnect to approve the newly added account. This is only temporary and in the future, you will not need to do the reinstall once MetaMask Snaps support account change events.', + ), + ]), + }, + }); +}; diff --git a/packages/hedera-identify-snap/packages/snap/src/custom-ui/onUpdate.ts b/packages/hedera-identify-snap/packages/snap/src/custom-ui/onUpdate.ts new file mode 100644 index 00000000..180a0226 --- /dev/null +++ b/packages/hedera-identify-snap/packages/snap/src/custom-ui/onUpdate.ts @@ -0,0 +1,43 @@ +/*- + * + * 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 { OnUpdateHandler } from '@metamask/snaps-sdk'; + +import { heading, panel, text } from '@metamask/snaps-sdk'; + +export const onUpdateUI: OnUpdateHandler = async () => { + await snap.request({ + method: 'snap_dialog', + params: { + type: 'alert', + content: panel([ + heading('Thank you for updating Identify Snap'), + text('New features added in this version:'), + text('🚀 Added support for did:key method'), + text('🚀 Added support for did:hedera method'), + text('🚀 Added snap home page'), + text('🚀 Updated some dialog boxes'), + text( + '🚀 Send notifications for certain API calls whenever snap errors out', + ), + ]), + }, + }); +}; diff --git a/packages/hedera-identify-snap/packages/snap/src/custom-ui/onUserInput.ts b/packages/hedera-identify-snap/packages/snap/src/custom-ui/onUserInput.ts new file mode 100644 index 00000000..241d2e33 --- /dev/null +++ b/packages/hedera-identify-snap/packages/snap/src/custom-ui/onUserInput.ts @@ -0,0 +1,269 @@ +/*- + * + * 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(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(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(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(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': { + panelToShow.push( + text( + 'Warning: Never disclose this key. Anyone with your private keys can steal any assets held in your account.', + ), + copyable( + state.accountState[snapAddress][network].keyStore.privateKey, + ), + ); + 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); + } +}; diff --git a/packages/hedera-identify-snap/packages/snap/src/did/key/keyDidProvider.ts b/packages/hedera-identify-snap/packages/snap/src/did/hedera/hederaDidProvider.ts similarity index 59% rename from packages/hedera-identify-snap/packages/snap/src/did/key/keyDidProvider.ts rename to packages/hedera-identify-snap/packages/snap/src/did/hedera/hederaDidProvider.ts index 38973c57..6eaf5839 100644 --- a/packages/hedera-identify-snap/packages/snap/src/did/key/keyDidProvider.ts +++ b/packages/hedera-identify-snap/packages/snap/src/did/hedera/hederaDidProvider.ts @@ -32,45 +32,71 @@ import { IService, } from '@veramo/core'; import { AbstractIdentifierProvider } from '@veramo/did-manager'; -import { base58btc } from 'multiformats/bases/base58'; -import { addMulticodecPrefix } from '../../utils/formatUtils'; +import _ from 'lodash'; +import { IdentifySnapState } from '../../types/state'; +import { getHcsDidClient } from './hederaDidUtils'; type IContext = IAgentContext; +type CreateHederaDidOptions = { + keyType?: keyof typeof keyCodecs; +}; + +const keyCodecs = { + Ed25519: 'ed25519-pub', + Secp256k1: 'secp256k1-pub', +} as const; /** - * {@link @veramo/did-manager#DIDManager} identifier provider for `did:key` identifiers + * {@link @veramo/did-manager#DIDManager} identifier provider for `did:hedera` identifiers * * @beta This API may change without a BREAKING CHANGE notice. */ -export class KeyDIDProvider extends AbstractIdentifierProvider { +export class HederaDIDProvider extends AbstractIdentifierProvider { private defaultKms: string; + private state: IdentifySnapState; - constructor(options: { defaultKms: string }) { + constructor(options: { defaultKms: string; state: IdentifySnapState }) { super(); this.defaultKms = options.defaultKms; + this.state = options.state; } async createIdentifier( // eslint-disable-next-line @typescript-eslint/no-unused-vars - { kms, options }: { kms?: string; options?: any }, + { kms, options }: { kms?: string; options?: CreateHederaDidOptions }, context: IContext, ): Promise> { + const keyType = + (options?.keyType && keyCodecs[options?.keyType] && options.keyType) || + 'Secp256k1'; + + const hederaDidClient = await getHcsDidClient(this.state); + if (!hederaDidClient) { + console.error('Failed to create HcsDid client'); + throw new Error('Failed to create HcsDid client'); + } + + let did = ''; + // Try registering the DID + try { + const registeredDid = await hederaDidClient.register(); + did = registeredDid.getIdentifier() || ''; + } catch (e: any) { + console.error(`Failed to register DID: ${e}`); + throw new Error(`Failed to register DID: ${e}`); + } + + if (_.isEmpty(did)) { + throw new Error('Failed to create Hedera DID'); + } + const key = await context.agent.keyManagerCreate({ kms: kms || this.defaultKms, - type: 'Ed25519', + type: keyType, }); - const methodSpecificId = Buffer.from( - base58btc.encode( - addMulticodecPrefix( - 'ed25519-pub', - Buffer.from(key.publicKeyHex, 'hex'), - ), - ), - ).toString(); - const identifier: Omit = { - did: `did:key:${methodSpecificId}`, + did, controllerKeyId: key.kid, keys: [key], services: [], @@ -87,13 +113,25 @@ export class KeyDIDProvider extends AbstractIdentifierProvider { }, context: IAgentContext, ): Promise { - throw new Error('KeyDIDProvider updateIdentifier not supported yet.'); + throw new Error('HederaDIDProvider updateIdentifier not supported yet.'); } async deleteIdentifier( identifier: IIdentifier, context: IContext, ): Promise { + try { + const hederaDidClient = await getHcsDidClient(this.state); + if (!hederaDidClient) { + console.error('Failed to create HcsDid client'); + throw new Error('Failed to create HcsDid client'); + } + await hederaDidClient.delete(); + } catch (e: any) { + console.error(`Failed to delete DID: ${e}`); + throw new Error(`Failed to delete DID: ${e}`); + } + // eslint-disable-next-line no-restricted-syntax for (const { kid } of identifier.keys) { // eslint-disable-next-line no-await-in-loop @@ -110,7 +148,7 @@ export class KeyDIDProvider extends AbstractIdentifierProvider { }: { identifier: IIdentifier; key: IKey; options?: any }, context: IContext, ): Promise { - throw Error('KeyDIDProvider addKey not supported'); + throw Error('HederaDIDProvider addKey not supported'); } async addService( @@ -121,20 +159,20 @@ export class KeyDIDProvider extends AbstractIdentifierProvider { }: { identifier: IIdentifier; service: IService; options?: any }, context: IContext, ): Promise { - throw Error('KeyDIDProvider addService not supported'); + throw Error('HederaDIDProvider addService not supported'); } async removeKey( args: { identifier: IIdentifier; kid: string; options?: any }, context: IContext, ): Promise { - throw Error('KeyDIDProvider removeKey not supported'); + throw Error('HederaDIDProvider removeKey not supported'); } async removeService( args: { identifier: IIdentifier; id: string; options?: any }, context: IContext, ): Promise { - throw Error('KeyDIDProvider removeService not supported'); + throw Error('HederaDIDProvider removeService not supported'); } } diff --git a/packages/hedera-identify-snap/packages/snap/src/did/hedera/hederaDidResolver.ts b/packages/hedera-identify-snap/packages/snap/src/did/hedera/hederaDidResolver.ts new file mode 100644 index 00000000..73fa8019 --- /dev/null +++ b/packages/hedera-identify-snap/packages/snap/src/did/hedera/hederaDidResolver.ts @@ -0,0 +1,96 @@ +/*- + * + * 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 { HederaDidResolver } from '@tuum-tech/hedera-did-sdk-js'; +import { + DIDResolutionOptions, + DIDResolutionResult, + DIDResolver, + ParsedDID, + Resolvable, +} from 'did-resolver'; +import { SnapState } from '../../snap/SnapState'; +import { getHederaClient } from './hederaDidUtils'; + +enum SupportedVerificationMethods { + 'JsonWebKey2020', + 'EcdsaSecp256k1VerificationKey2020', + 'Ed25519VerificationKey2020', +} + +export type DIDHederaResolverOptions = DIDResolutionOptions & { + publicKeyFormat?: keyof typeof SupportedVerificationMethods; // defaults to 'JsonWebKey2020' +}; + +/** + * Resolves a DID using the HederaDidResolver. + * + * @param didUrl - The DID URL to resolve. + * @param parsed - The parsed DID structure. + * @param resolver - The Resolvable instance. + * @param options - DID resolution options. + * @param state - The IdentifySnapState. + * @returns A DID resolution result. + */ +export const resolveDidHedera: DIDResolver = async ( + didUrl: string, + parsed: ParsedDID, + resolver: Resolvable, + options: DIDHederaResolverOptions, +): Promise => { + try { + const state = await SnapState.getState(); + const hederaClient = await getHederaClient(state); + if (!hederaClient) { + return { + didDocumentMetadata: {}, + didResolutionMetadata: { + error: 'invalidDid', + message: 'Hedera client is not provided for resolving the DID', + }, + didDocument: null, + }; + } + const client = hederaClient.getClient(); // Retrieve the Hedera client + const hederaResolver = new HederaDidResolver(client); // Create the resolver + options.publicKeyFormat = options.publicKeyFormat || 'JsonWebKey2020'; + return await hederaResolver.resolve(didUrl, parsed, resolver, options); // Resolve the DID + } catch (err: unknown) { + return { + didDocumentMetadata: {}, + didResolutionMetadata: { + error: 'invalidDid', + message: (err as Error).message, + }, + didDocument: null, + }; + } +}; + +/** + * Provides a mapping to a did:hedera resolver, usable by {@link did-resolver#Resolver}. + * + * @param state - The IdentifySnapState. + * @returns A record with the DID method and its resolver. + * @public + */ +export function getDidHederaResolver() { + return { hedera: resolveDidHedera }; +} diff --git a/packages/hedera-identify-snap/packages/snap/src/did/hedera/hederaDidUtils.ts b/packages/hedera-identify-snap/packages/snap/src/did/hedera/hederaDidUtils.ts new file mode 100644 index 00000000..b5c32dca --- /dev/null +++ b/packages/hedera-identify-snap/packages/snap/src/did/hedera/hederaDidUtils.ts @@ -0,0 +1,105 @@ +/*- + * + * 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 { PrivateKey } from '@hashgraph/sdk'; +import { HcsDid } from '@tuum-tech/hedera-did-sdk-js'; +import { HederaClientImplFactory } from '../../client/HederaClientImplFactory'; +import { SimpleHederaClientImpl } from '../../client/SimpleHederaClientImpl'; +import { ECDSA_SECP256K1_KEY_TYPE } from '../../constants'; +import { IdentifyAccountState, IdentifySnapState } from '../../types/state'; +import { HederaUtils } from '../../utils/HederaUtils'; + +export function getDidHederaIdentifier( + accountState: IdentifyAccountState, + didMethod: string, +): string { + // Check if the 'identifiers' key exists in the accountState object + if ('identifiers' in accountState) { + const identifiers = accountState.identifiers; // Access the 'identifiers' object + + // Iterate over the keys in 'identifiers' + for (const key in identifiers) { + const identifier = identifiers[key]; + if (identifier.provider === didMethod) { + // Extract the part after the last colon + const strippedKey = key.split(':').pop() || ''; // Get the last part of the key + return strippedKey; + } + } + } + return ''; +} + +export async function getHederaClient( + state: IdentifySnapState, +): Promise { + const { currentAccount } = state; + + const accountState = + state.accountState[currentAccount.snapEvmAddress][currentAccount.network]; + if (!accountState) { + throw new Error('Account state is missing or invalid'); + } + + const { hederaNetwork } = HederaUtils.getHederaNetworkInfo( + currentAccount.network, + ); + + const hederaClientFactory = new HederaClientImplFactory( + accountState.keyStore.hederaAccountId, + hederaNetwork, + accountState.keyStore.curve, + accountState.keyStore.privateKey, + ); + const client = await hederaClientFactory.createClient(); + if (!client) { + throw new Error('Failed to create Hedera client'); + } + return client; +} + +export async function getHcsDidClient( + state: IdentifySnapState, +): Promise { + try { + const client = await getHederaClient(state); + + const { currentAccount } = state; + + const accountState = + state.accountState[currentAccount.snapEvmAddress][currentAccount.network]; + const { hederaNetwork } = HederaUtils.getHederaNetworkInfo( + currentAccount.network, + ); + + return new HcsDid({ + network: hederaNetwork, + privateKey: + accountState.keyStore.curve === ECDSA_SECP256K1_KEY_TYPE + ? PrivateKey.fromStringECDSA(accountState.keyStore.privateKey) + : PrivateKey.fromStringED25519(accountState.keyStore.privateKey), + client: client.getClient(), + publicKeyFormat: 'JsonWebKey2020', + }); + } catch (e: any) { + console.error(`Failed to setup HcsDid client: ${e.message}`, e); + return null; + } +} diff --git a/packages/hedera-identify-snap/packages/snap/src/did/key/keyDidResolver.ts b/packages/hedera-identify-snap/packages/snap/src/did/key/keyDidResolver.ts deleted file mode 100644 index 61efc579..00000000 --- a/packages/hedera-identify-snap/packages/snap/src/did/key/keyDidResolver.ts +++ /dev/null @@ -1,121 +0,0 @@ -/*- - * - * 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 { - DIDDocument, - DIDResolutionOptions, - DIDResolutionResult, - DIDResolver, - ParsedDID, - Resolvable, -} from 'did-resolver'; -import { getAccountStateByCoinType, getState } from '../../snap/state'; -import { getCurrentMetamaskAccount } from '../../veramo/accountImport'; - -export const resolveSecp256k1 = async ( - account: string, - did: string, -): Promise => { - const accountState = await getAccountStateByCoinType( - await getState(), - account, - ); - const controllerKeyId = `metamask-${account}`; - const publicKey = accountState.snapKeyStore[controllerKeyId].publicKeyHex; - - // TODO: Change id ? - const didDocument: DIDDocument = { - id: `did:key:${did}#${did}`, - '@context': [ - 'https://www.w3.org/ns/did/v1', - 'https://w3id.org/security/suites/secp256k1-2019/v1', - ], - assertionMethod: [`did:key:${did}#${did}`], - authentication: [`did:key:${did}#${did}`], - capabilityInvocation: [`did:key:${did}#${did}`], - capabilityDelegation: [`did:key:${did}#${did}`], - keyAgreement: [`did:key:${did}#${did}`], - verificationMethod: [ - { - id: `did:key:${did}#${did}`, - type: 'EcdsaSecp256k1RecoveryMethod2020', - controller: `did:key:${did}#${did}`, - publicKeyHex: publicKey.split('0x')[1], - }, - ], - }; - return didDocument; -}; - -type ResolutionFunction = ( - account: string, - did: string, -) => Promise; - -const startsWithMap: Record = { - 'did:key:zQ3s': resolveSecp256k1, -}; - -export const resolveDidKey: DIDResolver = async ( - didUrl: string, - parsed: ParsedDID, - resolver: Resolvable, - options: DIDResolutionOptions, -): Promise => { - try { - const account = await getCurrentMetamaskAccount(); - const startsWith = parsed.did.substring(0, 12); - if (startsWithMap[startsWith] !== undefined) { - const didDocument = await startsWithMap[startsWith](account, didUrl); - return { - didDocumentMetadata: {}, - didResolutionMetadata: {}, - didDocument, - } as DIDResolutionResult; - } - - return { - didDocumentMetadata: {}, - didResolutionMetadata: { - error: 'invalidDid', - message: 'unsupported key type for did:key', - }, - didDocument: null, - }; - } catch (err: unknown) { - return { - didDocumentMetadata: {}, - didResolutionMetadata: { - error: 'invalidDid', - message: (err as string).toString(), - }, - didDocument: null, - }; - } -}; - -/** - * Provides a mapping to a did:key resolver, usable by {@link did-resolver#Resolver}. - * - * @public - */ -export function getDidKeyResolver() { - return { key: resolveDidKey }; -} diff --git a/packages/hedera-identify-snap/packages/snap/src/did/key/keyDidUtils.ts b/packages/hedera-identify-snap/packages/snap/src/did/key/keyDidUtils.ts index 29b92aaa..a8063f63 100644 --- a/packages/hedera-identify-snap/packages/snap/src/did/key/keyDidUtils.ts +++ b/packages/hedera-identify-snap/packages/snap/src/did/key/keyDidUtils.ts @@ -18,17 +18,21 @@ * */ -import { base58btc } from 'multiformats/bases/base58'; +import { bytesToMultibase, hexToBytes } from '@veramo/utils'; -import { addMulticodecPrefix } from '../../utils/formatUtils'; -import { getCompressedPublicKey } from '../../utils/keyPair'; +const keyCodecs = { + Ed25519: 'ed25519-pub', + X25519: 'x25519-pub', + Secp256k1: 'secp256k1-pub', +} as const; -export async function getDidKeyIdentifier(publicKey: string): Promise { - const compressedKey = getCompressedPublicKey(publicKey); - - return Buffer.from( - base58btc.encode( - addMulticodecPrefix('secp256k1-pub', Buffer.from(compressedKey, 'hex')), - ), - ).toString(); +export function getDidKeyIdentifier( + publicKey: string, + keyType: string, +): string { + return bytesToMultibase( + hexToBytes(publicKey), + 'base58btc', + keyCodecs[keyType as keyof typeof keyCodecs], + ); } diff --git a/packages/hedera-identify-snap/packages/snap/src/hedera/wallet/abstract.ts b/packages/hedera-identify-snap/packages/snap/src/domain/wallet/abstract.ts similarity index 90% rename from packages/hedera-identify-snap/packages/snap/src/hedera/wallet/abstract.ts rename to packages/hedera-identify-snap/packages/snap/src/domain/wallet/abstract.ts index b4a6b54e..4b15d479 100644 --- a/packages/hedera-identify-snap/packages/snap/src/hedera/wallet/abstract.ts +++ b/packages/hedera-identify-snap/packages/snap/src/domain/wallet/abstract.ts @@ -31,7 +31,7 @@ import type { PrivateKey, PublicKey } from '@hashgraph/sdk'; // - ledger, index: N // - others, not supported -export abstract class WalletHedera { +export abstract class Wallet { // produce a transaction signer // that can be used to sign transactions abstract getTransactionSigner( @@ -41,12 +41,7 @@ export abstract class WalletHedera { // get the public key associated with the wallet abstract getPublicKey(index: number): Promise; - /** - * Get the private key associated with the wallet (if avaialble). - * - * @param _index - Index. - * @returns Private key. - */ + // Get the private key associated with the wallet (if avaialble) // eslint-disable-next-line @typescript-eslint/no-unused-vars getPrivateKey(_index: number): Promise { return Promise.resolve(null); diff --git a/packages/hedera-identify-snap/packages/snap/src/hedera/wallet/software-private-key.ts b/packages/hedera-identify-snap/packages/snap/src/domain/wallet/software-private-key.ts similarity index 87% rename from packages/hedera-identify-snap/packages/snap/src/hedera/wallet/software-private-key.ts rename to packages/hedera-identify-snap/packages/snap/src/domain/wallet/software-private-key.ts index a3d14082..b7ac7b24 100644 --- a/packages/hedera-identify-snap/packages/snap/src/hedera/wallet/software-private-key.ts +++ b/packages/hedera-identify-snap/packages/snap/src/domain/wallet/software-private-key.ts @@ -20,10 +20,11 @@ import type { PrivateKey, PublicKey } from '@hashgraph/sdk'; -import { WalletHedera } from './abstract'; +import { Wallet } from './abstract'; -export class PrivateKeySoftwareWallet extends WalletHedera { - private _privateKey: PrivateKey; +export class PrivateKeySoftwareWallet extends Wallet { + // eslint-disable-next-line no-restricted-syntax + private readonly _privateKey: PrivateKey; constructor(privateKey: PrivateKey) { super(); diff --git a/packages/hedera-identify-snap/packages/snap/src/facades/did/ResolveDIDFacade.ts b/packages/hedera-identify-snap/packages/snap/src/facades/did/ResolveDIDFacade.ts new file mode 100644 index 00000000..a6fad069 --- /dev/null +++ b/packages/hedera-identify-snap/packages/snap/src/facades/did/ResolveDIDFacade.ts @@ -0,0 +1,47 @@ +/*- + * + * 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 { IdentifySnapParams } from '../../types/state'; +import { getVeramoAgent } from '../../veramo/agent'; + +export class ResolveDIDFacade { + /** + * Function to toggle popups. + * + * @param identifySnapParams - Identify snap params. + */ + public static async resolveDID( + identitySnapParams: IdentifySnapParams, + didUrl?: string, + ): Promise { + const { state } = identitySnapParams; + + const agent = await getVeramoAgent(state); + + let did = didUrl; + // GET DID if not exists + if (!did) { + did = state.currentAccount.identifier.did; + } + const result = await agent.resolveDid({ didUrl: did }); + + return JSON.parse(JSON.stringify(result)); + } +} diff --git a/packages/hedera-identify-snap/packages/snap/src/facades/did/SwitchDIDMethodFacade.ts b/packages/hedera-identify-snap/packages/snap/src/facades/did/SwitchDIDMethodFacade.ts new file mode 100644 index 00000000..8a838b94 --- /dev/null +++ b/packages/hedera-identify-snap/packages/snap/src/facades/did/SwitchDIDMethodFacade.ts @@ -0,0 +1,82 @@ +/*- + * + * 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 { DialogParams, divider, heading, text } from '@metamask/snaps-sdk'; +import { SnapState } from '../../snap/SnapState'; +import { availableMethods, isValidMethod } from '../../types/constants'; +import { IdentifySnapParams } from '../../types/state'; +import { SnapUtils } from '../../utils/SnapUtils'; + +export class SwitchDIDMethodFacade { + /** + * Function to switch method. + * + * @param identifySnapParams - Identify snap params. + * @param didMethod - DID method. + */ + public static async switchDIDMethod( + identitySnapParams: IdentifySnapParams, + didMethod: string, + ): Promise { + const { origin, state } = identitySnapParams; + const { network } = state.currentAccount; + + const method = state.snapConfig.dApp.didMethod; + if (!isValidMethod(didMethod)) { + console.error( + `did method '${didMethod}' not supported. Supported methods are: ${availableMethods}`, + ); + throw new Error( + `did method ${didMethod}'not supported. Supported methods are: ${availableMethods}`, + ); + } + + let result = true; + if (method !== didMethod) { + const panelToShow = SnapUtils.initializePanelToShow(); + panelToShow.push( + heading('Switch to a different DID method to use'), + text('Would you like to change did method to the following?'), + divider(), + text(`Current DID method: ${method}`), + text(`New DID method: ${didMethod}`), + ); + const dialogParams: DialogParams = { + type: 'confirmation', + content: await SnapUtils.generateCommonPanel( + origin, + network, + panelToShow, + ), + }; + + if ( + state.snapConfig.dApp.disablePopups || + (await SnapUtils.snapDialog(dialogParams)) + ) { + await SnapState.updateDIDMethod(state, didMethod); + result = true; + } else { + result = false; + } + } + return result; + } +} diff --git a/packages/hedera-identify-snap/packages/snap/src/facades/gdrive/ConfigureGAccountFacade.ts b/packages/hedera-identify-snap/packages/snap/src/facades/gdrive/ConfigureGAccountFacade.ts new file mode 100644 index 00000000..3f925cb4 --- /dev/null +++ b/packages/hedera-identify-snap/packages/snap/src/facades/gdrive/ConfigureGAccountFacade.ts @@ -0,0 +1,93 @@ +/*- + * + * 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 { rpcErrors } from '@metamask/rpc-errors'; +import { DialogParams, divider, heading, text } from '@metamask/snaps-sdk'; +import { verifyToken } from '../../plugins/veramo/google-drive-data-store'; +import { SnapState } from '../../snap/SnapState'; +import { GoogleToken } from '../../types/params'; +import { IdentifySnapParams } from '../../types/state'; +import { SnapUtils } from '../../utils/SnapUtils'; + +export class ConfigureGAccountFacde { + /** + * Function to configure google account + * + * @param identifySnapParams - Identify snap params. + */ + public static async configureGoogleAccount( + identifySnapParams: IdentifySnapParams, + { accessToken }: GoogleToken, + ): Promise { + const { state } = identifySnapParams; + const { snapEvmAddress, network } = state.currentAccount; + + let newGUserEmail; + try { + newGUserEmail = await verifyToken(accessToken); + } catch (error) { + console.error(`Failed to verify google access token: ${error}`); + return false; + } + + const currentGUserInfo = + state.accountState[snapEvmAddress][network].accountConfig.identity + .googleUserInfo; + + const panelToShow = SnapUtils.initializePanelToShow(); + panelToShow.push( + heading('Configure Google Drive'), + text('Would you like to change your Google account to the following?'), + divider(), + text( + `Current Gdrive account: ${ + currentGUserInfo.email ? currentGUserInfo.email : 'Not yet set' + }`, + ), + text(`New Gdrive account: ${newGUserEmail}`), + ); + const dialogParams: DialogParams = { + type: 'confirmation', + content: await SnapUtils.generateCommonPanel( + origin, + network, + panelToShow, + ), + }; + + const confirmed = await SnapUtils.snapDialog(dialogParams); + if (!confirmed) { + const errMessage = 'User rejected the transaction'; + console.error(errMessage); + throw rpcErrors.transactionRejected(errMessage); + } + + state.accountState[snapEvmAddress][ + network + ].accountConfig.identity.googleUserInfo.accessToken = accessToken; + + state.accountState[snapEvmAddress][ + network + ].accountConfig.identity.googleUserInfo.email = newGUserEmail; + + await SnapState.updatePopups(state); + return true; + } +} diff --git a/packages/hedera-identify-snap/packages/snap/src/rpc/vc/syncGoogleVCs.ts b/packages/hedera-identify-snap/packages/snap/src/facades/gdrive/SyncVCsInGDriveFacade.ts similarity index 69% rename from packages/hedera-identify-snap/packages/snap/src/rpc/vc/syncGoogleVCs.ts rename to packages/hedera-identify-snap/packages/snap/src/facades/gdrive/SyncVCsInGDriveFacade.ts index 9ff27031..309a6d46 100644 --- a/packages/hedera-identify-snap/packages/snap/src/rpc/vc/syncGoogleVCs.ts +++ b/packages/hedera-identify-snap/packages/snap/src/facades/gdrive/SyncVCsInGDriveFacade.ts @@ -18,12 +18,8 @@ * */ +import { rpcErrors } from '@metamask/rpc-errors'; import { DialogParams } from '@metamask/snaps-sdk'; -import { - Account, - IdentitySnapParams, - IdentitySnapState, -} from '../../interfaces'; import { verifyToken } from '../../plugins/veramo/google-drive-data-store'; import { IDataManagerQueryResult, @@ -32,32 +28,38 @@ import { QueryOptions, SaveOptions, } from '../../plugins/veramo/verifiable-creds-manager'; -import { generateVCPanel, snapDialog } from '../../snap/dialog'; -import { getAccountStateByCoinType } from '../../snap/state'; +import { IdentifyAccountState, IdentifySnapParams } from '../../types/state'; +import { SnapUtils } from '../../utils/SnapUtils'; import { Agent, getVeramoAgent } from '../../veramo/agent'; -/** - * Function to sync Google VCs with snap. - * - * @param identitySnapParams - Identity snap params. - */ -export async function syncGoogleVCs( - identitySnapParams: IdentitySnapParams, -): Promise { - const { origin, network, state, account } = identitySnapParams; +export class SyncVCsInGDriveFacade { + /** + * Function to sync VCs in Google Drive. + * + * @param identifySnapParams - Identify snap params. + */ + public static async syncVCsBetweenSnapAndGDrive( + identifySnapParams: IdentifySnapParams, + ): Promise { + const { state } = identifySnapParams; + const { snapEvmAddress, network } = state.currentAccount; - try { - // Get Veramo agent - const agent = await getVeramoAgent(state); + const accountState = state.accountState[snapEvmAddress][network]; - const accountState = await getAccountStateByCoinType( - state, - account.metamaskAddress, - ); - const gUserEmail = await verifyToken( - accountState.accountConfig.identity.googleUserInfo.accessToken, - ); + let gUserEmail; + try { + gUserEmail = await verifyToken( + accountState.accountConfig.identity.googleUserInfo.accessToken, + ); + } catch (error) { + console.error( + `Failed to verify google access token: ${error}. Please reconfigure your Google account with "configureGoogleAccount" API`, + ); + return false; + } + // Get Veramo agent + const agent = await getVeramoAgent(state); const options: QueryOptions = { store: 'snap', returnStore: true }; // Get VCs from the snap state storage const snapVCs = (await agent.queryVC({ @@ -96,8 +98,7 @@ export async function syncGoogleVCs( if (vcsNotInSnap.length > 0) { vcsNotInSnapSync = await handleSync( network, - state, - account, + accountState, agent, origin, `${header} - Import VCs from Google drive: ${gUserEmail}`, @@ -111,8 +112,7 @@ export async function syncGoogleVCs( if (vcsNotInGDrive.length > 0) { vcsNotInGDriveSync = await handleSync( network, - state, - account, + accountState, agent, origin, `${header} - Export VCs to Google drive: ${gUserEmail}`, @@ -126,28 +126,16 @@ export async function syncGoogleVCs( if (vcsNotInSnapSync && vcsNotInGDriveSync) { return true; } - } catch (error) { - console.error( - 'Could not sync Verifiable Credentials between the Metamask snap and Google Drive properly. Please try again', - JSON.stringify(error, null, 4), - ); - throw error; - } - console.log( - 'Could not sync Verifiable Credentials between the Metamask snap and Google drive properly. Please try again', - ); - throw new Error( - 'Could not sync Verifiable Credentials between the Metamask snap and Google drive properly. Please try again', - ); + return false; + } } /** * Function to handle the snap dialog and import/export each VC. * * @param network- Network name. - * @param state - Identity state. - * @param account - Currently connected account. + * @param accountState: Account state. * @param agent - Veramo. * @param origin - The origin of where the call is being made from. * @param header - Header text of the metamask dialog box(eg. 'Retrieve Verifiable Credentials'). @@ -158,8 +146,7 @@ export async function syncGoogleVCs( */ async function handleSync( network: string, - state: IdentitySnapState, - account: Account, + accountState: IdentifyAccountState, agent: Agent, origin: string, header: string, @@ -170,7 +157,7 @@ async function handleSync( ): Promise { const dialogParams: DialogParams = { type: 'confirmation', - content: await generateVCPanel( + content: await SnapUtils.generateVCPanel( origin, network, header, @@ -179,29 +166,27 @@ async function handleSync( vcs, ), }; - if (await snapDialog(dialogParams)) { - const options = { - store, - } as SaveOptions; - const accountState = await getAccountStateByCoinType( - state, - account.metamaskAddress, - ); - const data = vcs.map((x) => { - return { vc: x.data, id: x.metadata.id } as ISaveVC; - }) as ISaveVC[]; - const result: IDataManagerSaveResult[] = await agent.saveVC({ - data, - options, - accessToken: - accountState.accountConfig.identity.googleUserInfo.accessToken, - }); - if (!(result.length > 0 && result[0].id !== '')) { - console.log('Could not sync the vc: ', JSON.stringify(data, null, 4)); - return false; - } - return true; + const confirmed = await SnapUtils.snapDialog(dialogParams); + if (!confirmed) { + const errMessage = 'User rejected the transaction'; + console.error(errMessage); + throw rpcErrors.transactionRejected(errMessage); + } + + const options = { + store, + } as SaveOptions; + const data = vcs.map((x) => { + return { vc: x.data, id: x.metadata.id } as ISaveVC; + }) as ISaveVC[]; + const result: IDataManagerSaveResult[] = await agent.saveVC({ + data, + options, + accessToken: accountState.accountConfig.identity.googleUserInfo.accessToken, + }); + if (!(result.length > 0 && result[0].id !== '')) { + console.log('Could not sync the vc: ', JSON.stringify(data, null, 4)); + return false; } - console.log('User rejected the sync operation'); - return false; + return true; } diff --git a/packages/hedera-identify-snap/packages/snap/src/facades/snap/GetAccountInfoFacade.ts b/packages/hedera-identify-snap/packages/snap/src/facades/snap/GetAccountInfoFacade.ts new file mode 100644 index 00000000..0f3ca6be --- /dev/null +++ b/packages/hedera-identify-snap/packages/snap/src/facades/snap/GetAccountInfoFacade.ts @@ -0,0 +1,47 @@ +/*- + * + * 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 { PublicAccountInfo } from '../../types/account'; +import { IdentifySnapParams } from '../../types/state'; + +export class GetAccountInfoFacade { + /** + * Get account info such as address, did, public key, etc. + * + * @param identifySnapParams - Identify snap params. + * @returns Public Account Info. + */ + public static async getAccountInfo( + identitySnapParams: IdentifySnapParams, + ): Promise { + const { state } = identitySnapParams; + + const publicAccountInfo: PublicAccountInfo = { + metamaskAddress: state.currentAccount.metamaskEvmAddress, + snapAddress: state.currentAccount.snapEvmAddress, + snapPublicKey: state.currentAccount.publicKey, + did: state.currentAccount.identifier.did, + method: state.currentAccount.method, + hederaAccountId: state.currentAccount.hederaAccountId, + }; + + return publicAccountInfo; + } +} diff --git a/packages/hedera-identify-snap/packages/snap/src/facades/snap/TogglePopupsFacade.ts b/packages/hedera-identify-snap/packages/snap/src/facades/snap/TogglePopupsFacade.ts new file mode 100644 index 00000000..b65c7135 --- /dev/null +++ b/packages/hedera-identify-snap/packages/snap/src/facades/snap/TogglePopupsFacade.ts @@ -0,0 +1,64 @@ +/*- + * + * 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 { rpcErrors } from '@metamask/rpc-errors'; +import { DialogParams, heading, text } from '@metamask/snaps-sdk'; +import { SnapState } from '../../snap/SnapState'; +import { IdentifySnapParams } from '../../types/state'; +import { SnapUtils } from '../../utils/SnapUtils'; + +export class TogglePopupsFacade { + /** + * Function to toggle popups. + * + * @param identifySnapParams - Identify snap params. + */ + public static async togglePopups( + identitySnapParams: IdentifySnapParams, + ): Promise { + const { origin, state } = identitySnapParams; + const { network } = state.currentAccount; + + const { disablePopups } = state.snapConfig.dApp; + + const toggleTextToShow = disablePopups ? 'enable' : 'disable'; + const panelToShow = [ + heading('Toggle Popups'), + text(`Would you like to ${toggleTextToShow} the popups?`), + ]; + const dialogParams: DialogParams = { + type: 'confirmation', + content: await SnapUtils.generateCommonPanel( + origin, + network, + panelToShow, + ), + }; + const confirmed = await SnapUtils.snapDialog(dialogParams); + if (!confirmed) { + const errMessage = 'User rejected the transaction'; + console.error(errMessage); + throw rpcErrors.transactionRejected(errMessage); + } + + await SnapState.updatePopups(state); + return true; + } +} diff --git a/packages/hedera-identify-snap/packages/snap/src/facades/vc/GetVCsFacade.ts b/packages/hedera-identify-snap/packages/snap/src/facades/vc/GetVCsFacade.ts new file mode 100644 index 00000000..3103095e --- /dev/null +++ b/packages/hedera-identify-snap/packages/snap/src/facades/vc/GetVCsFacade.ts @@ -0,0 +1,84 @@ +/*- + * + * 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 { DialogParams } from '@metamask/snaps-sdk'; +import { + IDataManagerQueryArgs, + IDataManagerQueryResult, + QueryOptions, +} from '../../plugins/veramo/verifiable-creds-manager'; +import { IdentifySnapParams } from '../../types/state'; +import { SnapUtils } from '../../utils/SnapUtils'; +import { getVeramoAgent } from '../../veramo/agent'; + +export class GetVCsFacade { + /** + * Function to get VCs. + * + * @param identifySnapParams - Identify snap params. + */ + public static async getVCs( + identifySnapParams: IdentifySnapParams, + vcRequestParams: IDataManagerQueryArgs, + ): Promise { + const { state } = identifySnapParams; + const { snapEvmAddress, network, identifier } = state.currentAccount; + + const { filter, options } = vcRequestParams || {}; + const { store = 'snap', returnStore = true } = options || {}; + + // Get Veramo agent + const agent = await getVeramoAgent(state); + + // Get VCs + const accountState = state.accountState[snapEvmAddress][network]; + const optionsFiltered = { store, returnStore } as QueryOptions; + const vcs = (await agent.queryVC({ + filter, + options: optionsFiltered, + accessToken: + accountState.accountConfig.identity.googleUserInfo.accessToken, + })) as IDataManagerQueryResult[]; + + const header = 'Retrieve Verifiable Credentials'; + const prompt = 'Are you sure you want to send VCs to the dApp?'; + const description = `Some dApps are less secure than others and could save data from VCs against your will. Be careful where you send your private VCs! Number of VCs submitted is ${vcs.length.toString()}`; + const dialogParams: DialogParams = { + type: 'confirmation', + content: await SnapUtils.generateVCPanel( + origin, + network, + header, + prompt, + description, + vcs, + ), + }; + + if ( + state.snapConfig.dApp.disablePopups || + (await SnapUtils.snapDialog(dialogParams)) + ) { + return vcs; + } + + return []; + } +} diff --git a/packages/hedera-identify-snap/packages/snap/src/facades/vc/RemoveVCsFacade.ts b/packages/hedera-identify-snap/packages/snap/src/facades/vc/RemoveVCsFacade.ts new file mode 100644 index 00000000..87f89a90 --- /dev/null +++ b/packages/hedera-identify-snap/packages/snap/src/facades/vc/RemoveVCsFacade.ts @@ -0,0 +1,172 @@ +/*- + * + * 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 { rpcErrors } from '@metamask/rpc-errors'; +import { DialogParams } from '@metamask/snaps-sdk'; +import { + ClearOptions, + DeleteOptions, + IDataManagerClearArgs, + IDataManagerClearResult, + IDataManagerDeleteArgs, + IDataManagerDeleteResult, + IDataManagerQueryResult, +} from '../../plugins/veramo/verifiable-creds-manager'; +import { IdentifySnapParams } from '../../types/state'; +import { SnapUtils } from '../../utils/SnapUtils'; +import { getVeramoAgent } from '../../veramo/agent'; + +export class RemoveVCsFacade { + /** + * Function to remove VCs. + * + * @param identifySnapParams - Identify snap params. + */ + public static async removeSpecificVC( + identifySnapParams: IdentifySnapParams, + vcRequestParams: IDataManagerDeleteArgs, + ): Promise { + const { state } = identifySnapParams; + const { snapEvmAddress, network } = state.currentAccount; + + const { id = '', options } = vcRequestParams || {}; + const { store = 'snap' } = options || {}; + const optionsFiltered = { store } as DeleteOptions; + + const ids = typeof id === 'string' ? [id] : id; + if (ids.length === 0) { + console.error('No VC IDs provided'); + return [] as IDataManagerDeleteResult[]; + } + + // Get Veramo agent + const agent = await getVeramoAgent(state); + + const accountState = state.accountState[snapEvmAddress][network]; + const vcsToBeRemoved: IDataManagerQueryResult[] = []; + for (const vcId of ids) { + const vcs = (await agent.queryVC({ + filter: { + type: 'id', + filter: vcId, + }, + options: optionsFiltered, + accessToken: + accountState.accountConfig.identity.googleUserInfo.accessToken, + })) as IDataManagerQueryResult[]; + if (vcs.length > 0) { + vcsToBeRemoved.push(vcs[0]); + } + } + + const header = 'Remove specific Verifiable Credentials'; + const prompt = 'Are you sure you want to remove the following VCs?'; + const description = `Note that this action cannot be reversed and you will need to recreate your VCs if you go through with it. Number of VCs to be removed is ${vcsToBeRemoved.length.toString()}`; + const dialogParams: DialogParams = { + type: 'confirmation', + content: await SnapUtils.generateVCPanel( + origin, + network, + header, + prompt, + description, + vcsToBeRemoved, + ), + }; + + const confirmed = await SnapUtils.snapDialog(dialogParams); + if (!confirmed) { + const errMessage = 'User rejected the transaction'; + console.error(errMessage); + throw rpcErrors.transactionRejected(errMessage); + } + + // Remove the specified Verifiable Credentials from the store based on their IDs + return Promise.all( + ids.map(async (_id: string) => { + return await agent.deleteVC({ + id: _id, + options: optionsFiltered, + accessToken: + accountState.accountConfig.identity.googleUserInfo.accessToken, + }); + }), + ).then((data: IDataManagerDeleteResult[][]) => { + return data.flat(); + }); + } + + /** + * Function to remove all the VCs. + * + * @param identifySnapParams - Identify snap params. + */ + public static async removeAllVCs( + identifySnapParams: IdentifySnapParams, + vcRequestParams: IDataManagerClearArgs, + ): Promise { + const { state } = identifySnapParams; + const { snapEvmAddress, network } = state.currentAccount; + + const { options } = vcRequestParams || {}; + const { store = 'snap' } = options || {}; + const optionsFiltered = { store } as ClearOptions; + + // Get Veramo agent + const agent = await getVeramoAgent(state); + + const accountState = state.accountState[snapEvmAddress][network]; + const vcsToBeRemoved = (await agent.queryVC({ + filter: undefined, + options: optionsFiltered, + accessToken: + accountState.accountConfig.identity.googleUserInfo.accessToken, + })) as IDataManagerQueryResult[]; + + const header = 'Delete all Verifiable Credentials'; + const prompt = `Are you sure you want to remove all your VCs from the store '${store}'?`; + const description = `Note that this action cannot be reversed and you will need to recreate your VCs if you go through with it. Number of VCs to be removed is ${vcsToBeRemoved.length.toString()}`; + const dialogParams: DialogParams = { + type: 'confirmation', + content: await SnapUtils.generateVCPanel( + origin, + network, + header, + prompt, + description, + vcsToBeRemoved, + ), + }; + + const confirmed = await SnapUtils.snapDialog(dialogParams); + if (!confirmed) { + const errMessage = 'User rejected the transaction'; + console.error(errMessage); + throw rpcErrors.transactionRejected(errMessage); + } + + // Remove all the Verifiable Credentials from the store + return await agent.clearVCs({ + options: optionsFiltered, + accessToken: + accountState.accountConfig.identity.googleUserInfo.accessToken, + }); + } +} diff --git a/packages/hedera-identify-snap/packages/snap/src/facades/vc/SaveVCFacade.ts b/packages/hedera-identify-snap/packages/snap/src/facades/vc/SaveVCFacade.ts new file mode 100644 index 00000000..3105c8cc --- /dev/null +++ b/packages/hedera-identify-snap/packages/snap/src/facades/vc/SaveVCFacade.ts @@ -0,0 +1,248 @@ +/*- + * + * 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 { rpcErrors } from '@metamask/rpc-errors'; +import { DialogParams, divider, heading, text } from '@metamask/snaps-sdk'; +import { ProofFormat, W3CVerifiableCredential } from '@veramo/core'; +import { sha256 } from 'js-sha256'; +import cloneDeep from 'lodash.clonedeep'; +import { + IDataManagerQueryResult, + IDataManagerSaveArgs, + IDataManagerSaveResult, + ISaveVC, + QueryMetadata, + SaveOptions, +} from '../../plugins/veramo/verifiable-creds-manager'; +import { + CreateVCRequestParams, + CreateVCResponseResult, +} from '../../types/params'; +import { IdentifySnapParams } from '../../types/state'; +import { HederaUtils } from '../../utils/HederaUtils'; +import { SnapUtils } from '../../utils/SnapUtils'; +import { getVeramoAgent } from '../../veramo/agent'; + +export class SaveVCFacade { + /** + * Function to create vc. + * + * @param identifySnapParams - Identify snap params. + */ + public static async createVC( + identitySnapParams: IdentifySnapParams, + vcRequestParams: CreateVCRequestParams, + ): Promise { + const { state } = identitySnapParams; + const { snapEvmAddress, network, identifier, method } = + state.currentAccount; + + // Get Veramo agent + const agent = await getVeramoAgent(state); + + // GET DID + const { did } = identifier; + + const { + vcKey = 'vcData', + vcValue, + credTypes = [], + options, + } = vcRequestParams || {}; + const { store = 'snap' } = options || {}; + const optionsFiltered = { store } as SaveOptions; + + const panelToShow = [ + heading('Create Verifiable Credential'), + text('Would you like to create and save the following VC in the snap?'), + divider(), + text( + JSON.stringify({ + [vcKey]: vcValue, + }), + ), + ]; + const dialogParams: DialogParams = { + type: 'confirmation', + content: await SnapUtils.generateCommonPanel( + origin, + network, + panelToShow, + ), + }; + + const confirmed = await SnapUtils.snapDialog(dialogParams); + if (!confirmed) { + const errMessage = 'User rejected the transaction'; + console.error(errMessage); + throw rpcErrors.transactionRejected(errMessage); + } + + const issuanceDate = new Date(); + // Set the expiration date to be 1 year from the date it's issued + const expirationDate = cloneDeep(issuanceDate); + expirationDate.setFullYear( + issuanceDate.getFullYear() + 1, + issuanceDate.getMonth(), + issuanceDate.getDate(), + ); + + const credential = new Map(); + credential.set('issuanceDate', issuanceDate.toISOString()); // the entity that issued the credential+ + credential.set('expirationDate', expirationDate.toISOString()); // when the credential was issued + credential.set('type', credTypes); + + const issuer: { id: string; hederaAccountId?: string } = { id: did }; + const credentialSubject: { id: string; hederaAccountId?: string } = { + id: did, // identifier for the only subject of the credential + [vcKey]: vcValue, // assertion about the only subject of the credential + }; + const accountState = state.accountState[snapEvmAddress][network]; + if (HederaUtils.validHederaNetwork(network) || method === 'did:hedera') { + const hederaAccountId = accountState.accountInfo.accountId; + issuer.hederaAccountId = hederaAccountId; + credentialSubject.hederaAccountId = hederaAccountId; + } + credential.set('issuer', issuer); // the entity that issued the credential + credential.set('credentialSubject', credentialSubject); + + // Generate a Verifiable Credential + const verifiableCredential: W3CVerifiableCredential = + await agent.createVerifiableCredential({ + credential: JSON.parse(JSON.stringify(Object.fromEntries(credential))), + // digital proof that makes the credential tamper-evident + proofFormat: 'jwt' as ProofFormat, + }); + + // Save the Verifiable Credential to all the stores the user requested for + const saved: IDataManagerSaveResult[] = await agent.saveVC({ + data: [ + { + vc: verifiableCredential, + id: sha256(JSON.stringify(verifiableCredential)), + }, + ] as ISaveVC[], + options: optionsFiltered, + accessToken: + accountState.accountConfig.identity.googleUserInfo.accessToken, + }); + if (saved.length === 0) { + throw new Error('Failed to save the VC'); + } + + // Retrieve the created Verifiable Credential + const result: CreateVCResponseResult = { + data: verifiableCredential as W3CVerifiableCredential, + metadata: { + id: saved[0].id, + store: saved.map((res) => res.store), + } as QueryMetadata, + }; + return result; + } + + /** + * Function to save vc. + * + * @param identifySnapParams - Identify snap params. + */ + public static async saveVC( + identitySnapParams: IdentifySnapParams, + vcRequestParams: IDataManagerSaveArgs, + ): Promise { + const { state } = identitySnapParams; + const { snapEvmAddress, network } = state.currentAccount; + + const { data: verifiableCredentials, options } = vcRequestParams || {}; + const { store = 'snap' } = options || {}; + const optionsFiltered = { store } as SaveOptions; + + const header = 'Save Verifiable Credentials'; + const prompt = `Are you sure you want to save the following VCs in ${ + typeof store === 'string' ? store : store.join(', ') + }?`; + const description = `Number of VCs submitted is ${( + verifiableCredentials as W3CVerifiableCredential[] + ).length.toString()}`; + + const vcsWithMetadata: IDataManagerQueryResult[] = []; + // Iterate through vcs + (verifiableCredentials as W3CVerifiableCredential[]).forEach( + function (vc, index) { + vcsWithMetadata.push({ + data: vc, + metadata: { + id: `External VC #${(index + 1).toString()}`, + store: 'snap', + }, + }); + }, + ); + const dialogParams: DialogParams = { + type: 'confirmation', + content: await SnapUtils.generateVCPanel( + origin, + network, + header, + prompt, + description, + vcsWithMetadata, + ), + }; + + const confirmed = await SnapUtils.snapDialog(dialogParams); + if (!confirmed) { + const errMessage = 'User rejected the transaction'; + console.error(errMessage); + throw rpcErrors.transactionRejected(errMessage); + } + + // Save the Verifiable Credential + const accountState = state.accountState[snapEvmAddress][network]; + const filteredCredentials: W3CVerifiableCredential[] = ( + verifiableCredentials as W3CVerifiableCredential[] + ).filter((x: W3CVerifiableCredential) => { + const vcObj = JSON.parse(JSON.stringify(x)); + + const subjectDid: string = vcObj.credentialSubject.id; + const subjectAccount = subjectDid.split(':')[4]; + return snapEvmAddress === subjectAccount; + }); + + // Save the Verifiable Credential to all the stores the user requested for + const agent = await getVeramoAgent(state); + const saved: IDataManagerSaveResult[] = await agent.saveVC({ + data: (filteredCredentials as W3CVerifiableCredential[]).map( + (x: W3CVerifiableCredential) => { + return { vc: x } as ISaveVC; + }, + ) as ISaveVC[], + options: optionsFiltered, + accessToken: + accountState.accountConfig.identity.googleUserInfo.accessToken, + }); + + if (saved.length === 0) { + throw new Error('Failed to save the VC'); + } + + return saved; + } +} diff --git a/packages/hedera-identify-snap/packages/snap/src/rpc/vc/verifyVC.ts b/packages/hedera-identify-snap/packages/snap/src/facades/vc/VerifyVCFacade.ts similarity index 50% rename from packages/hedera-identify-snap/packages/snap/src/rpc/vc/verifyVC.ts rename to packages/hedera-identify-snap/packages/snap/src/facades/vc/VerifyVCFacade.ts index 266cb2ba..c32f0f44 100644 --- a/packages/hedera-identify-snap/packages/snap/src/rpc/vc/verifyVC.ts +++ b/packages/hedera-identify-snap/packages/snap/src/facades/vc/VerifyVCFacade.ts @@ -19,31 +19,32 @@ */ import { W3CVerifiableCredential } from '@veramo/core'; -import { IdentitySnapParams } from '../../interfaces'; +import { IdentifySnapParams } from '../../types/state'; import { getVeramoAgent } from '../../veramo/agent'; -/** - * Function to verify VC. - * - * @param identitySnapParams - Identity snap params. - * @param vc - Verifiable Credential. - */ -export async function verifyVC( - identitySnapParams: IdentitySnapParams, - vc: W3CVerifiableCredential, -): Promise { - const { state } = identitySnapParams; - // Get Veramo agent - const agent = await getVeramoAgent(state); +export class VerifyVCFacade { + /** + * Function to verify VC. + * + * @param identifySnapParams - Identify snap params. + */ + public static async verifyVC( + identifySnapParams: IdentifySnapParams, + vc: W3CVerifiableCredential, + ): Promise { + const { state } = identifySnapParams; + + // Get Veramo agent + const agent = await getVeramoAgent(state); - // Verify the verifiable credential(VC) - const result = await agent.verifyCredential({ credential: vc }); - if (result.verified === false) { - console.log('result: ', JSON.stringify(result, null, 4)); - console.log( - 'VC Verification Error: ', - JSON.stringify(result.error, null, 4), - ); + // Verify the verifiable credential(VC) + const result = await agent.verifyCredential({ credential: vc }); + if (result.verified === false) { + console.log( + 'VC Verification Error: ', + JSON.stringify(result.error, null, 4), + ); + } + return result.verified; } - return result.verified; } diff --git a/packages/hedera-identify-snap/packages/snap/src/facades/vp/CreateVPFacade.ts b/packages/hedera-identify-snap/packages/snap/src/facades/vp/CreateVPFacade.ts new file mode 100644 index 00000000..6c4a42e9 --- /dev/null +++ b/packages/hedera-identify-snap/packages/snap/src/facades/vp/CreateVPFacade.ts @@ -0,0 +1,151 @@ +/*- + * + * 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 { DialogParams } from '@metamask/snaps-sdk'; +import { + ProofFormat, + VerifiableCredential, + VerifiablePresentation, +} from '@veramo/core'; +import { + IDataManagerQueryResult, + QueryOptions, +} from '../../plugins/veramo/verifiable-creds-manager'; +import { + CreateVPOptions, + CreateVPRequestParams, + ProofInfo, +} from '../../types/params'; +import { IdentifySnapParams } from '../../types/state'; +import { SnapUtils } from '../../utils/SnapUtils'; +import { getVeramoAgent } from '../../veramo/agent'; + +export class CreateVPFacade { + /** + * Function to create vp. + * + * @param identifySnapParams - Identify snap params. + */ + public static async createVP( + identitySnapParams: IdentifySnapParams, + vpRequestParams: CreateVPRequestParams, + ): Promise { + const { state } = identitySnapParams; + const { network, identifier } = state.currentAccount; + + const { + vcIds = [], + vcs = [], + proofInfo = {} as ProofInfo, + options, + } = vpRequestParams || {}; + const { store = 'snap' } = options || {}; + const optionsFiltered = { store } as CreateVPOptions; + + const proofFormat = proofInfo?.proofFormat + ? proofInfo.proofFormat + : ('jwt' as ProofFormat); + const type = proofInfo?.type ? proofInfo.type : 'Custom'; + const domain = proofInfo?.domain; + const challenge = proofInfo?.challenge; + + // Get Veramo agent + const agent = await getVeramoAgent(state); + + // GET DID + const { did } = identifier; + + const vcsRes: VerifiableCredential[] = []; + const vcsWithMetadata: IDataManagerQueryResult[] = []; + + // Iterate through vcIds + for (const vcId of vcIds) { + const vcObj = (await agent.queryVC({ + filter: { + type: 'id', + filter: vcId, + }, + options: optionsFiltered as QueryOptions, + })) as IDataManagerQueryResult[]; + + if (vcObj.length > 0) { + const { data, metadata } = vcObj[0]; + vcsRes.push(data as VerifiableCredential); + vcsWithMetadata.push({ + data, + metadata, + }); + } + } + + // Iterate through vcs + vcs.forEach(function (vc, index) { + vcsRes.push(vc as VerifiableCredential); + vcsWithMetadata.push({ + data: vc, + metadata: { + id: `External VC #${(index + 1).toString()}`, + store: 'snap', + }, + }); + }); + + if (vcsRes.length === 0) { + console.error('No VCs found to create VP'); + return {} as VerifiablePresentation; + } + + const header = 'Create Verifiable Presentation'; + const prompt = 'Do you wish to create a VP from the following VCs?'; + const description = + 'A Verifiable Presentation is a secure way for someone to present information about themselves or their identity to someone else while ensuring that the information is accureate and trustworthy'; + const dialogParams: DialogParams = { + type: 'confirmation', + content: await SnapUtils.generateVCPanel( + origin, + network, + header, + prompt, + description, + vcsWithMetadata, + ), + }; + + if ( + state.snapConfig.dApp.disablePopups || + (await SnapUtils.snapDialog(dialogParams)) + ) { + // Generate a Verifiable Presentation from VCs + return await agent.createVerifiablePresentation({ + presentation: { + holder: did, // + type: ['VerifiablePresentation', type], + verifiableCredential: vcsRes, + }, + proofFormat, // The desired format for the VerifiablePresentation to be created + domain, // Optional string domain parameter to add to the verifiable presentation + challenge, // Optional (only JWT) string challenge parameter to add to the verifiable presentation + }); + } + + console.error('User rejected the transaction'); + return {} as VerifiablePresentation; + } +} diff --git a/packages/hedera-identify-snap/packages/snap/src/facades/vp/VerifyVPFacade.ts b/packages/hedera-identify-snap/packages/snap/src/facades/vp/VerifyVPFacade.ts new file mode 100644 index 00000000..75bde236 --- /dev/null +++ b/packages/hedera-identify-snap/packages/snap/src/facades/vp/VerifyVPFacade.ts @@ -0,0 +1,52 @@ +/*- + * + * 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 { VerifiablePresentation } from '@veramo/core'; +import { IdentifySnapParams } from '../../types/state'; +import { getVeramoAgent } from '../../veramo/agent'; + +export class VerifyVPFacade { + /** + * Function to verify VP. + * + * @param identifySnapParams - Identify snap params. + */ + public static async verifyVP( + identifySnapParams: IdentifySnapParams, + vp: VerifiablePresentation, + ): Promise { + const { state } = identifySnapParams; + + // Get Veramo agent + const agent = await getVeramoAgent(state); + + // Verify the verifiable presentation(VP) + const result = await agent.verifyPresentation({ + presentation: vp, + }); + if (result.verified === false) { + console.log( + 'VP Verification Error: ', + JSON.stringify(result.error, null, 4), + ); + } + return result.verified; + } +} diff --git a/packages/hedera-identify-snap/packages/snap/src/hedera/client/create-account.ts b/packages/hedera-identify-snap/packages/snap/src/hedera/client/create-account.ts deleted file mode 100644 index 9255c759..00000000 --- a/packages/hedera-identify-snap/packages/snap/src/hedera/client/create-account.ts +++ /dev/null @@ -1,70 +0,0 @@ -/*- - * - * 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 { - AccountCreateTransaction, - Client, - Hbar, - PublicKey, - TransactionReceipt, -} from '@hashgraph/sdk'; -import { BigNumber } from 'bignumber.js'; -import { HederaMirrorInfo } from '../service'; - -/** - * Create Hedera™ crypto-currency account. - * - * @param client - Hedera Client. - * @param options - Create account options. - * @param options.publicKey - Public key. - * @param options.initialBalance - Initial balance. - */ -export async function createAccountForPublicKey( - client: Client, - options: { - publicKey: PublicKey; - initialBalance: BigNumber; - }, -): Promise { - const tx = new AccountCreateTransaction() - .setInitialBalance(Hbar.fromTinybars(options.initialBalance)) - .setMaxTransactionFee(new Hbar(1)) - .setKey(options.publicKey); - - const receipt: TransactionReceipt = await ( - await tx.execute(client) - ).getReceipt(client); - - const newAccountId = receipt.accountId ? receipt.accountId.toString() : ''; - - console.log('newAccountId: ', newAccountId); - - if (!newAccountId) { - console.log( - "The transaction didn't process successfully so a new accountId was not created", - ); - return null; - } - - return { - account: newAccountId, - publicKey: options.publicKey.toStringRaw(), - } as HederaMirrorInfo; -} diff --git a/packages/hedera-identify-snap/packages/snap/src/hedera/client/index.ts b/packages/hedera-identify-snap/packages/snap/src/hedera/client/index.ts deleted file mode 100644 index b61678d0..00000000 --- a/packages/hedera-identify-snap/packages/snap/src/hedera/client/index.ts +++ /dev/null @@ -1,137 +0,0 @@ -/*- - * - * 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 { - AccountId, - AccountInfo, - AccountInfoQuery, - Client, - Hbar, - PrivateKey, - PublicKey, - TransactionReceipt, - TransactionReceiptQuery, - TransferTransaction, -} from '@hashgraph/sdk'; -import { BigNumber } from 'bignumber.js'; - -import { - HederaAccountInfo, - HederaMirrorInfo, - SimpleHederaClient, -} from '../service'; - -import { createAccountForPublicKey } from './create-account'; - -export class SimpleHederaClientImpl implements SimpleHederaClient { - private _client: Client; - - private _privateKey: PrivateKey | null; - - constructor(client: Client, privateKey: PrivateKey | null) { - this._client = client; - this._privateKey = privateKey; - } - - getPrivateKey(): PrivateKey | null { - return this._privateKey; - } - - getPublicKey(): PublicKey { - /* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */ - return this._client.operatorPublicKey!; - } - - getAccountId(): AccountId { - /* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */ - return this._client.operatorAccountId!; - } - - async getAccountInfo(accountId: string): Promise { - // Create the account info query - const query = new AccountInfoQuery().setAccountId(accountId); - - // Sign with client operator private key and submit the query to a Hedera network - const accountInfo: AccountInfo = await query.execute(this._client); - - return accountInfo as unknown as HederaAccountInfo; - } - - createAccountForPublicKey(options: { - publicKey: PublicKey; - initialBalance: BigNumber; - }): Promise { - return createAccountForPublicKey(this._client, options); - } - - /** - * Create Hedera™ crypto-currency account. - * - * @param options - Create account options. - * @param options.evmAddress - EVM address. - * @param options.initialBalance - Initial balance. - */ - async createAccountForEvmAddress(options: { - evmAddress: string; - initialBalance: BigNumber; - }): Promise { - const privateKey = this.getPrivateKey(); - if (!privateKey) { - console.log("Private key doesn't exist for the operator"); - return null; - } - - const transferTx: TransferTransaction = new TransferTransaction() - .addHbarTransfer( - this.getAccountId(), - new Hbar(options.initialBalance).negated(), - ) - .addHbarTransfer(options.evmAddress, options.initialBalance) - .freezeWith(this._client); - - const transferTxSign = await transferTx.sign(privateKey); - const transferTxSubmit = await transferTxSign.execute(this._client); - - // Get the child receipt or child record to return the Hedera Account ID for the new account that was created - const receipt: TransactionReceipt = await new TransactionReceiptQuery() - .setTransactionId(transferTxSubmit.transactionId) - .setIncludeChildren(true) - .execute(this._client); - - const newAccountId = - receipt.children.length > 0 && receipt.children[0].accountId - ? receipt.children[0].accountId.toString() - : ''; - - console.log('newAccountId: ', newAccountId); - - if (!newAccountId) { - console.log( - "The transaction didn't process successfully so a new accountId was not created", - ); - return null; - } - - return { - account: newAccountId, - evmAddress: options.evmAddress, - } as HederaMirrorInfo; - } -} diff --git a/packages/hedera-identify-snap/packages/snap/src/hedera/config.ts b/packages/hedera-identify-snap/packages/snap/src/hedera/config.ts deleted file mode 100644 index 62508a08..00000000 --- a/packages/hedera-identify-snap/packages/snap/src/hedera/config.ts +++ /dev/null @@ -1,60 +0,0 @@ -/*- - * - * 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 { isIn } from '../types/constants'; - -const hederaChainIDs: Record = { - '0x127': 'mainnet', - '0x128': 'testnet', - '0x129': 'previewnet', -}; - -const otherChainIDs: Record = { - '0x1': 'Ethereum Mainnet', - '0xa': 'Optimisim', - '0xe': 'Flare', - '0x13': 'Songbird', - '0x14': 'Elastos', - '0x1e': 'RSK', - '0x32': 'XDC', - '0x38': 'Binance Smart Chain', - '0x89': 'Polygon', - '0x13a': 'Filecoin', - '0x44d': 'Polygon ZKEVM', - '0x8ae': 'Kava', - '0x2105': 'Base', - '0xa4b1': 'Arbitrum', - '0xa86a': 'Avalanche', - '0xe71c': 'Linea', -}; - -export const getHederaNetwork = (chainId: string): string => { - const network = hederaChainIDs[chainId]; - return network || 'mainnet'; -}; - -export const getOtherNetwork = (chainId: string): string => { - const network = otherChainIDs[chainId]; - return network || `Unknown Network (Chain ID: ${chainId})`; -}; - -export const validHederaChainID = (x: string) => { - return isIn(Object.keys(hederaChainIDs) as string[], x); -}; diff --git a/packages/hedera-identify-snap/packages/snap/src/hedera/index.ts b/packages/hedera-identify-snap/packages/snap/src/hedera/index.ts deleted file mode 100644 index c5c9ff74..00000000 --- a/packages/hedera-identify-snap/packages/snap/src/hedera/index.ts +++ /dev/null @@ -1,297 +0,0 @@ -/*- - * - * 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 { - AccountId, - Client, - Hbar, - Status, - StatusError, - TransferTransaction, -} from '@hashgraph/sdk'; - -import _ from 'lodash'; - -import { SimpleHederaClientImpl } from './client'; -import { HederaMirrorInfo, HederaService, SimpleHederaClient } from './service'; -import { WalletHedera } from './wallet/abstract'; - -export class HederaServiceImpl implements HederaService { - private network: string; - - constructor(network: string) { - this.network = network; - } - - async createClient(options: { - walletHedera: WalletHedera; - keyIndex: number; - accountId: AccountId; - }): Promise { - if (options.walletHedera === null) { - return null; - } - - let client: Client; - - if (this.network === 'testnet') { - client = Client.forTestnet(); - } else if (this.network === 'previewnet') { - client = Client.forPreviewnet(); - } else { - client = Client.forMainnet(); - } - - client.setNetworkUpdatePeriod(2000); - - const transactionSigner = await options.walletHedera.getTransactionSigner( - options.keyIndex, - ); - - const privateKey = await options.walletHedera.getPrivateKey( - options.keyIndex, - ); - const publicKey = await options.walletHedera.getPublicKey(options.keyIndex); - - if (publicKey === null) { - return null; - } - - // TODO: Fix - client.setOperatorWith( - options.accountId, - publicKey ?? '', - transactionSigner, - ); - - if (!(await testClientOperatorMatch(client))) { - console.log('failed testClientOperatorMatch'); - return null; - } - - return new SimpleHederaClientImpl(client, privateKey); - } - - async getAccountFromPublicKey( - publicKey: string, - ): Promise { - // Returns all account information for the given public key - const network = - this.network === 'mainnet' ? 'mainnet-public' : this.network; - const accountInfoUrl = `https://${network}.mirrornode.hedera.com/api/v1/accounts?account.publickey=${publicKey}&limit=1&order=asc`; - const accountInfoResult = await mirrorNodeQuery(accountInfoUrl); - - if ( - !( - accountInfoResult.accounts && - accountInfoResult.accounts.length > 0 && - accountInfoResult.accounts[0].account - ) - ) { - console.log( - `Could not retrieve info about this evm address from hedera mirror node for some reason. Please try again later`, - ); - return null; - } - - const result = accountInfoResult.accounts[0]; - const createdDate = new Date( - (result.created_timestamp.split('.')[0] as number) * 1000, - ).toLocaleString(undefined, { - year: 'numeric', - month: '2-digit', - day: '2-digit', - weekday: 'long', - hour: '2-digit', - hour12: false, - minute: '2-digit', - second: '2-digit', - }); - const expiryDate = new Date( - (result.expiry_timestamp.split('.')[0] as number) * 1000, - ).toLocaleString(undefined, { - year: 'numeric', - month: '2-digit', - day: '2-digit', - weekday: 'long', - hour: '2-digit', - hour12: false, - minute: '2-digit', - second: '2-digit', - }); - return { - account: result.account, - evmAddress: result.evm_address, - publicKey, - alias: result.alias, - balance: result.balance.balance / 100000000.0, - createdDate, - expiryDate, - memo: result.memo, - } as HederaMirrorInfo; - } - - async getAccountFromEvmAddres( - evmAddress: string, - ): Promise { - try { - // Returns all account information for the given evmAddress - const network = - this.network === 'mainnet' ? 'mainnet-public' : this.network; - const accountInfoUrl = `https://${network}.mirrornode.hedera.com/api/v1/accounts/${evmAddress}?limit=1&order=asc`; - const result = await mirrorNodeQuery(accountInfoUrl); - - if (result === null || _.isEmpty(result) || !result.account) { - console.log( - `Could not retrieve info about this evm address from hedera mirror node for some reason. Please try again later`, - ); - return null; - } - - const createdDate = new Date( - (result.created_timestamp.split('.')[0] as number) * 1000, - ).toLocaleString(undefined, { - year: 'numeric', - month: '2-digit', - day: '2-digit', - weekday: 'long', - hour: '2-digit', - hour12: false, - minute: '2-digit', - second: '2-digit', - }); - const expiryDate = new Date( - (result.expiry_timestamp.split('.')[0] as number) * 1000, - ).toLocaleString(undefined, { - year: 'numeric', - month: '2-digit', - day: '2-digit', - weekday: 'long', - hour: '2-digit', - hour12: false, - minute: '2-digit', - second: '2-digit', - }); - return { - account: result.account, - evmAddress: result.evm_address, - alias: result.alias, - balance: result.balance.balance / 100000000.0, - createdDate, - expiryDate, - memo: result.memo, - } as HederaMirrorInfo; - } catch (error) { - console.log( - 'Error while retrieving account info using evm address from the mirror node. Error: ', - error, - ); - return null; - } - } -} - -/** - * Does the operator key belong to the operator account. - * - * @param client - Hedera Client. - */ -export async function testClientOperatorMatch(client: Client) { - const tx = new TransferTransaction() - /* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */ - .addHbarTransfer(client.operatorAccountId!, Hbar.fromTinybars(0)) - .setMaxTransactionFee(Hbar.fromTinybars(1)); - - try { - await tx.execute(client); - } catch (error: any) { - console.log('error: ', JSON.stringify(error, null, 4)); - if (error instanceof StatusError) { - // If the transaction fails with Insufficient Tx Fee, this means - // that the account ID verification succeeded before this point - // Same for Insufficient Payer Balance - return ( - error.status === Status.InsufficientTxFee || - error.status === Status.InsufficientPayerBalance - ); - } - - const errMessage = - 'The account id does not belong to the associated private key'; - console.log(errMessage, String(error)); - throw new Error(errMessage); - } - - // under *no* cirumstances should this transaction succeed - const errMessage = - 'Unexpected success of intentionally-erroneous transaction to confirm account ID'; - console.log(errMessage); - throw new Error(errMessage); -} - -/** - * Retrieve results using hedera mirror node. - * - * @param url - The URL to use to query. - */ -export async function mirrorNodeQuery(url: RequestInfo | URL) { - const response = await fetch(url); - return await response.json(); -} - -/** - * To HederaAccountInfo. - * - * @param _address - EVM address. - * @param _accountId - Account Id. - * @param _network - Network. - */ -export async function isValidHederaAccountInfo( - _address: string, - _accountId: string, - _network: string, -): Promise { - try { - const hederaService = new HederaServiceImpl(_network); - const result = (await hederaService.getAccountFromEvmAddres( - _address, - )) as HederaMirrorInfo; - - if (result === null || _.isEmpty(result)) { - console.error(`Response from mirror node is empty`); - return false; - } - - if (result.account !== _accountId) { - console.error( - `The accountId for the given evm address does not match the accountId that was passed`, - ); - return false; - } - } catch (error) { - console.log( - 'Error while retrieving account info using evm address from the mirror node. Error: ', - error, - ); - return false; - } - - return true; -} diff --git a/packages/hedera-identify-snap/packages/snap/src/hedera/service.ts b/packages/hedera-identify-snap/packages/snap/src/hedera/service.ts deleted file mode 100644 index 89304f77..00000000 --- a/packages/hedera-identify-snap/packages/snap/src/hedera/service.ts +++ /dev/null @@ -1,125 +0,0 @@ -/*- - * - * 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 { - AccountId, - Hbar, - HbarAllowance, - Key, - LedgerId, - LiveHash, - PrivateKey, - PublicKey, - Timestamp, - TokenAllowance, - TokenNftAllowance, -} from '@hashgraph/sdk'; -import TokenRelationshipMap from '@hashgraph/sdk/lib/account/TokenRelationshipMap'; -import Duration from '@hashgraph/sdk/lib/Duration'; -import StakingInfo from '@hashgraph/sdk/lib/StakingInfo'; -import { BigNumber } from 'bignumber.js'; - -import { WalletHedera } from './wallet/abstract'; - -import * as protobuf from 'protobufjs'; -import Long from 'long'; - -// Ensure that protobuf uses Long for 64-bit integers -protobuf.util.Long = Long; -protobuf.configure(); - -export type HederaService = { - // returns null if the account ID does not match the chosen key - createClient(options: { - network: string; - walletHedera: WalletHedera; - // index into the wallet, meaning depends on the wallet type - // 0 always means the canonical key for the wallet - keyIndex: number; - // account ID we wish to associate with the wallet - accountId: AccountId; - }): Promise; - - getAccountFromPublicKey(publicKey: string): Promise; - - getAccountFromEvmAddres(evmAddress: string): Promise; -}; - -export type SimpleHederaClient = { - // get the associated private key, if available - getPrivateKey(): PrivateKey | null; - - // get the associated public key - getPublicKey(): PublicKey; - - // get the associated account ID - getAccountId(): AccountId; - - getAccountInfo(accountId: string): Promise; - - createAccountForPublicKey(options: { - publicKey: PublicKey; - initialBalance: BigNumber; - }): Promise; - - createAccountForEvmAddress(options: { - evmAddress: string; - initialBalance: BigNumber; - }): Promise; -}; - -export type HederaMirrorInfo = { - account: string; - evmAddress: string; - publicKey?: string; - alias: string; - balance: number; - createdDate: string; - expiryDate: string; - memo: string; - newlyCreated?: boolean; -}; - -export type HederaAccountInfo = { - accountId: AccountId; - contractAccountId?: string; - isDeleted: boolean; - proxyAccountId?: object; - proxyReceived: Hbar; - key: Key; - balance: Hbar; - sendRecordThreshold: Hbar; - receiveRecordThreshold: Hbar; - isReceiverSignatureRequired: boolean; - expirationTime: Timestamp; - autoRenewPeriod: Duration; - liveHashes: LiveHash[]; - tokenRelationships: TokenRelationshipMap; - accountMemo: string; - ownedNfts: Long; - maxAutomaticTokenAssociations: Long; - aliasKey: PublicKey; - ledgerId: LedgerId; - hbarAllowances: HbarAllowance[]; - tokenAllowances: TokenAllowance[]; - nftAllowances: TokenNftAllowance[]; - ethereumNonce?: Long; - stakingInfo?: StakingInfo; -}; diff --git a/packages/hedera-identify-snap/packages/snap/src/index.tsx b/packages/hedera-identify-snap/packages/snap/src/index.tsx index 795a7525..8c288253 100644 --- a/packages/hedera-identify-snap/packages/snap/src/index.tsx +++ b/packages/hedera-identify-snap/packages/snap/src/index.tsx @@ -19,53 +19,46 @@ */ import { rpcErrors } from '@metamask/rpc-errors'; -import { OnRpcRequestHandler } from '@metamask/snaps-sdk'; +import type { + OnHomePageHandler, + OnInstallHandler, + OnRpcRequestHandler, + OnUpdateHandler, + OnUserInputHandler, +} from '@metamask/snaps-sdk'; +import _ from 'lodash'; import { HelloUI } from './components/hello'; -import { Account, ExternalAccount, IdentitySnapParams } from './interfaces'; -import { getAccountInfo } from './rpc/account/getAccountInfo'; -import { getAvailableDIDMethods } from './rpc/did/getAvailableDIDMethods'; -import { getCurrentDIDMethod } from './rpc/did/getCurrentDIDMethod'; -import { resolveDID } from './rpc/did/resolveDID'; -import { switchDIDMethod } from './rpc/did/switchDIDMethod'; -import { configureGoogleAccount } from './rpc/gdrive/configureGoogleAccount'; -import { togglePopups } from './rpc/snap/togglePopups'; -import { createVC } from './rpc/vc/createVC'; -import { createVP } from './rpc/vc/createVP'; -import { deleteAllVCs } from './rpc/vc/deleteAllVCs'; -import { getSupportedProofFormats } from './rpc/vc/getSupportedProofFormats'; -import { getVCs } from './rpc/vc/getVCs'; -import { removeVC } from './rpc/vc/removeVC'; -import { saveVC } from './rpc/vc/saveVC'; -import { syncGoogleVCs } from './rpc/vc/syncGoogleVCs'; -import { verifyVC } from './rpc/vc/verifyVC'; -import { verifyVP } from './rpc/vc/verifyVP'; -import { getCurrentAccount } from './snap/account'; -import { getCurrentNetwork } from './snap/network'; -import { getStateUnchecked } from './snap/state'; -import { init } from './utils/init'; -import { - isExternalAccountFlagSet, - isValidConfigueGoogleRequest, - isValidCreateVCRequest, - isValidCreateVPRequest, - isValidDeleteAllVCsRequest, - isValidGetVCsRequest, - isValidMetamaskAccountParams, - isValidRemoveVCRequest, - isValidResolveDIDRequest, - isValidSaveVCRequest, - isValidSwitchMethodRequest, - isValidVerifyVCRequest, - isValidVerifyVPRequest, -} from './utils/params'; +import { ShowAccountPrivateKeyUI } from './components/showAccountPrivateKey'; +import { OnHomePageUI } from './custom-ui/onHome'; +import { onInstallUI } from './custom-ui/onInstall'; +import { onUpdateUI } from './custom-ui/onUpdate'; +import { onUserInputUI } from './custom-ui/onUserInput'; +import { ResolveDIDFacade } from './facades/did/ResolveDIDFacade'; +import { SwitchDIDMethodFacade } from './facades/did/SwitchDIDMethodFacade'; +import { ConfigureGAccountFacde } from './facades/gdrive/ConfigureGAccountFacade'; +import { SyncVCsInGDriveFacade } from './facades/gdrive/SyncVCsInGDriveFacade'; +import { GetAccountInfoFacade } from './facades/snap/GetAccountInfoFacade'; +import { TogglePopupsFacade } from './facades/snap/TogglePopupsFacade'; +import { GetVCsFacade } from './facades/vc/GetVCsFacade'; +import { RemoveVCsFacade } from './facades/vc/RemoveVCsFacade'; +import { SaveVCFacade } from './facades/vc/SaveVCFacade'; +import { VerifyVCFacade } from './facades/vc/VerifyVCFacade'; +import { CreateVPFacade } from './facades/vp/CreateVPFacade'; +import { VerifyVPFacade } from './facades/vp/VerifyVPFacade'; +import { SnapAccounts } from './snap/SnapAccounts'; +import { SnapState } from './snap/SnapState'; +import { availableMethods, availableProofFormats } from './types/constants'; +import type { IdentifySnapParams } from './types/state'; +import { EvmUtils } from './utils/EvmUtils'; +import { ParamUtils } from './utils/ParamUtils'; /** * Handle incoming JSON-RPC requests, sent through `wallet_invokeSnap`. - * * @param args - The request handler args as object. + * @param args.origin - The origin of the request, e.g., the website that + * invoked the snap. * @param args.request - A validated JSON-RPC request object. - * @param args.origin - Origin of the request. - * @returns `null` if the request succeeded. + * @returns The result of `snap_dialog`. * @throws If the request method is not valid for this snap. */ export const onRpcRequest: OnRpcRequestHandler = async ({ @@ -76,21 +69,19 @@ export const onRpcRequest: OnRpcRequestHandler = async ({ console.log('Origin:', origin); console.log('-------------------------------------------------------------'); - const network = await getCurrentNetwork(); - - let state = await getStateUnchecked(); - if (state === null) { - state = await init(origin, network); + let state = await SnapState.getStateUnchecked(); + if (_.isEmpty(state)) { + state = await SnapState.initState(); } - console.log('state:', JSON.stringify(state, null, 4)); - const identitySnapParams: IdentitySnapParams = { + const identifySnapParams: IdentifySnapParams = { origin, - network, state, - account: {} as Account, }; + // Get network + const network = await EvmUtils.getChainId(); + switch (request.method) { case 'hello': { return await snap.request({ @@ -102,114 +93,148 @@ export const onRpcRequest: OnRpcRequestHandler = async ({ }); } case 'togglePopups': { - return await togglePopups(identitySnapParams); + return await TogglePopupsFacade.togglePopups(identifySnapParams); } case 'switchDIDMethod': { - isValidSwitchMethodRequest(request.params); - return await switchDIDMethod( - identitySnapParams, + ParamUtils.isValidSwitchMethodRequest(request.params); + return await SwitchDIDMethodFacade.switchDIDMethod( + identifySnapParams, request.params.didMethod, ); } } - let isExternalAccount: boolean; - let extraData: unknown; - if (isExternalAccountFlagSet(request.params)) { + let isExternalAccount = false; + if (ParamUtils.isExternalAccountFlagSet(request.params)) { isExternalAccount = true; - extraData = (request.params as ExternalAccount).externalAccount.data; - } else { - isExternalAccount = false; - isValidMetamaskAccountParams(request.params); } - const account: Account = await getCurrentAccount( + // Set current account + await SnapAccounts.setCurrentAccount( origin, - network, state, request.params, + network, isExternalAccount, ); - account.extraData = extraData; - - identitySnapParams.account = account; - const accountInfoPublic = await getAccountInfo(identitySnapParams); + const accountInfoPublic = + await GetAccountInfoFacade.getAccountInfo(identifySnapParams); switch (request.method) { - case 'getAccountInfo': { - return accountInfoPublic; - } + case 'getAccountInfo': + return { + currentAccount: accountInfoPublic, + }; + case 'showAccountPrivateKey': + await snap.request({ + method: 'snap_dialog', + params: { + type: 'alert', + content: ( + + ), + }, + }); + return { + currentAccount: accountInfoPublic, + }; + case 'resolveDID': { - isValidResolveDIDRequest(request.params); - return await resolveDID(identitySnapParams, request.params.did); + ParamUtils.isValidResolveDIDRequest(request.params); + return await ResolveDIDFacade.resolveDID( + identifySnapParams, + request.params.did, + ); } case 'getVCs': { - isValidGetVCsRequest(request.params); - return await getVCs(identitySnapParams, request.params); + ParamUtils.isValidGetVCsRequest(request.params); + return await GetVCsFacade.getVCs(identifySnapParams, request.params); } - case 'saveVC': { - isValidSaveVCRequest(request.params); - return await saveVC(identitySnapParams, request.params); + case 'createVC': { + ParamUtils.isValidCreateVCRequest(request.params); + return await SaveVCFacade.createVC(identifySnapParams, request.params); } - case 'createVC': { - isValidCreateVCRequest(request.params); - return await createVC(identitySnapParams, request.params); + case 'saveVC': { + ParamUtils.isValidSaveVCRequest(request.params); + return await SaveVCFacade.saveVC(identifySnapParams, request.params); } case 'verifyVC': { - isValidVerifyVCRequest(request.params); - return await verifyVC( - identitySnapParams, + ParamUtils.isValidVerifyVCRequest(request.params); + return await VerifyVCFacade.verifyVC( + identifySnapParams, request.params.verifiableCredential, ); } case 'removeVC': { - isValidRemoveVCRequest(request.params); - return await removeVC(identitySnapParams, request.params); + ParamUtils.isValidRemoveVCRequest(request.params); + return await RemoveVCsFacade.removeSpecificVC( + identifySnapParams, + request.params, + ); } case 'deleteAllVCs': { - isValidDeleteAllVCsRequest(request.params); - return await deleteAllVCs(identitySnapParams, request.params); + ParamUtils.isValidDeleteAllVCsRequest(request.params); + return await RemoveVCsFacade.removeAllVCs( + identifySnapParams, + request.params, + ); } case 'createVP': { - isValidCreateVPRequest(request.params); - return await createVP(identitySnapParams, request.params); + ParamUtils.isValidCreateVPRequest(request.params); + return await CreateVPFacade.createVP(identifySnapParams, request.params); } case 'verifyVP': { - isValidVerifyVPRequest(request.params); - return await verifyVP( - identitySnapParams, + ParamUtils.isValidVerifyVPRequest(request.params); + return await VerifyVPFacade.verifyVP( + identifySnapParams, request.params.verifiablePresentation, ); } case 'getAvailableMethods': { - return getAvailableDIDMethods(); + return availableMethods.map((key) => key); } case 'getCurrentDIDMethod': { - return getCurrentDIDMethod(identitySnapParams); + return state.snapConfig.dApp.didMethod; } case 'getSupportedProofFormats': { - return getSupportedProofFormats(); + return availableProofFormats.map((key) => key); } case 'configureGoogleAccount': { - isValidConfigueGoogleRequest(request.params); - return await configureGoogleAccount(identitySnapParams, request.params); + ParamUtils.isValidConfigueGoogleRequest(request.params); + return await ConfigureGAccountFacde.configureGoogleAccount( + identifySnapParams, + request.params, + ); } case 'syncGoogleVCs': { - return await syncGoogleVCs(identitySnapParams); + return await SyncVCsInGDriveFacade.syncVCsBetweenSnapAndGDrive( + identifySnapParams, + ); } default: @@ -217,3 +242,11 @@ export const onRpcRequest: OnRpcRequestHandler = async ({ throw rpcErrors.methodNotFound(request.method); } }; + +export const onHomePage: OnHomePageHandler = OnHomePageUI; + +export const onUserInput: OnUserInputHandler = onUserInputUI; + +export const onInstall: OnInstallHandler = onInstallUI; + +export const onUpdate: OnUpdateHandler = onUpdateUI; diff --git a/packages/hedera-identify-snap/packages/snap/src/utils/formatUtils.ts b/packages/hedera-identify-snap/packages/snap/src/interfaces/HederaClientFactory.ts similarity index 54% rename from packages/hedera-identify-snap/packages/snap/src/utils/formatUtils.ts rename to packages/hedera-identify-snap/packages/snap/src/interfaces/HederaClientFactory.ts index bab2cf1c..8154032c 100644 --- a/packages/hedera-identify-snap/packages/snap/src/utils/formatUtils.ts +++ b/packages/hedera-identify-snap/packages/snap/src/interfaces/HederaClientFactory.ts @@ -18,27 +18,18 @@ * */ -import { CodecName, MULTICODECS } from '../constants'; +import type { Wallet } from '../domain/wallet/abstract'; +import type { SimpleHederaClient } from '../types/hedera'; -/** - * Prefix a buffer with a multicodec-packed. - * - * @param {CodecName} multicodec - * @param {Uint8Array} data - * - * @returns {Uint8Array} - */ -export const addMulticodecPrefix = ( - multicodec: CodecName, - data: Uint8Array, -): Uint8Array => { - let prefix; - - if (MULTICODECS[multicodec]) { - prefix = Buffer.from(MULTICODECS[multicodec]); - } else { - throw new Error('multicodec not recognized'); - } - - return Buffer.concat([prefix, data], prefix.length + data.length); +export type HederaClientFactory = { + // returns null if the account ID does not match the chosen key + createClient(options: { + wallet: Wallet; + // index into the wallet, meaning depends on the wallet type + // 0 always means the canonical key for the wallet + keyIndex: number; + // account ID we wish to associate with the wallet + accountId: string; + network: string; + }): Promise; }; diff --git a/packages/hedera-identify-snap/packages/snap/src/plugins/veramo/google-drive-data-store/index.ts b/packages/hedera-identify-snap/packages/snap/src/plugins/veramo/google-drive-data-store/index.ts index e483bfbf..9a0ead3a 100644 --- a/packages/hedera-identify-snap/packages/snap/src/plugins/veramo/google-drive-data-store/index.ts +++ b/packages/hedera-identify-snap/packages/snap/src/plugins/veramo/google-drive-data-store/index.ts @@ -20,4 +20,3 @@ export * from './src/googleDriveVCStore'; export * from './src/googleUtils'; -export * from './src/jwt'; diff --git a/packages/hedera-identify-snap/packages/snap/src/plugins/veramo/google-drive-data-store/src/googleDriveVCStore.ts b/packages/hedera-identify-snap/packages/snap/src/plugins/veramo/google-drive-data-store/src/googleDriveVCStore.ts index 8a253c27..27f9afff 100644 --- a/packages/hedera-identify-snap/packages/snap/src/plugins/veramo/google-drive-data-store/src/googleDriveVCStore.ts +++ b/packages/hedera-identify-snap/packages/snap/src/plugins/veramo/google-drive-data-store/src/googleDriveVCStore.ts @@ -21,8 +21,11 @@ import { VerifiableCredential } from '@veramo/core'; import { sha256 } from 'js-sha256'; import jsonpath from 'jsonpath'; + +import { getDidHederaIdentifier } from '../../../../did/hedera/hederaDidUtils'; import { getDidKeyIdentifier } from '../../../../did/key/keyDidUtils'; -import { IdentitySnapState } from '../../../../interfaces'; +import { IdentifySnapState } from '../../../../types/state'; +import { Utils } from '../../../../utils/Utils'; import { AbstractDataStore, IConfigureArgs, @@ -37,7 +40,6 @@ import { uploadToGoogleDrive, verifyToken, } from './googleUtils'; -import { decodeJWT } from './jwt'; /** * An implementation of {@link AbstractDataStore} that holds everything in snap state. @@ -45,11 +47,11 @@ import { decodeJWT } from './jwt'; * This is usable by {@link @vc-manager/VCManager} to hold the vc data */ export class GoogleDriveVCStore extends AbstractDataStore { - state: IdentitySnapState; + state: IdentifySnapState; accessToken: string; email: string; - constructor(state: IdentitySnapState) { + constructor(state: IdentifySnapState) { super(); this.state = state; this.accessToken = ''; @@ -58,7 +60,7 @@ export class GoogleDriveVCStore extends AbstractDataStore { async queryVC(args: IFilterArgs): Promise { const { filter } = args; - const account = this.state.currentAccount.metamaskAddress; + const account = this.state.currentAccount.snapEvmAddress; if (!account) { throw Error( @@ -82,7 +84,7 @@ export class GoogleDriveVCStore extends AbstractDataStore { const decodeVC = (k: string) => { let vc = googleVCs[k] as unknown; if (typeof vc === 'string') { - vc = decodeJWT(vc); + vc = Utils.decodeJWT(vc); } return { metadata: { id: k }, data: vc }; }; @@ -124,7 +126,7 @@ export class GoogleDriveVCStore extends AbstractDataStore { async saveVC(args: { data: ISaveVC[] }): Promise { const { data: vcs } = args; - const account = this.state.currentAccount.metamaskAddress; + const account = this.state.currentAccount.snapEvmAddress; if (!account) { throw Error( @@ -147,10 +149,20 @@ export class GoogleDriveVCStore extends AbstractDataStore { const currentMethod = this.state.currentAccount.method; for (const vc of vcs) { - let identifier = this.state.currentAccount.snapAddress; + let identifier = this.state.currentAccount.snapEvmAddress; if (currentMethod === 'did:key') { - identifier = await getDidKeyIdentifier( + identifier = getDidKeyIdentifier( this.state.currentAccount.publicKey, + this.state.accountState[this.state.currentAccount.snapEvmAddress][ + this.state.currentAccount.network + ].keyStore.curve, + ); + } else if (currentMethod === 'did:hedera') { + identifier = getDidHederaIdentifier( + this.state.accountState[this.state.currentAccount.snapEvmAddress][ + this.state.currentAccount.network + ], + currentMethod, ); } @@ -174,7 +186,7 @@ export class GoogleDriveVCStore extends AbstractDataStore { } async deleteVC({ id }: { id: string }): Promise { - const account = this.state.currentAccount.metamaskAddress; + const account = this.state.currentAccount.snapEvmAddress; if (!account) { throw Error( @@ -193,10 +205,20 @@ export class GoogleDriveVCStore extends AbstractDataStore { } const currentMethod = this.state.currentAccount.method; - let identifier = this.state.currentAccount.snapAddress; + let identifier = this.state.currentAccount.snapEvmAddress; if (currentMethod === 'did:key') { - identifier = await getDidKeyIdentifier( + identifier = getDidKeyIdentifier( this.state.currentAccount.publicKey, + this.state.accountState[this.state.currentAccount.snapEvmAddress][ + this.state.currentAccount.network + ].keyStore.curve, + ); + } else if (currentMethod === 'did:hedera') { + identifier = getDidHederaIdentifier( + this.state.accountState[this.state.currentAccount.snapEvmAddress][ + this.state.currentAccount.network + ], + currentMethod, ); } @@ -222,7 +244,7 @@ export class GoogleDriveVCStore extends AbstractDataStore { } public async clearVCs(_args: IFilterArgs): Promise { - const account = this.state.currentAccount.metamaskAddress; + const account = this.state.currentAccount.snapEvmAddress; if (!account) { throw Error( diff --git a/packages/hedera-identify-snap/packages/snap/src/plugins/veramo/google-drive-data-store/src/jwt.ts b/packages/hedera-identify-snap/packages/snap/src/plugins/veramo/google-drive-data-store/src/jwt.ts deleted file mode 100644 index 45ca014b..00000000 --- a/packages/hedera-identify-snap/packages/snap/src/plugins/veramo/google-drive-data-store/src/jwt.ts +++ /dev/null @@ -1,40 +0,0 @@ -/*- - * - * 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 { VerifiableCredential } from '@veramo/core'; -import { normalizeCredential } from 'did-jwt-vc'; -import cloneDeep from 'lodash.clonedeep'; - -/** - * Function to decode JWT. - * - * @param jwt - JWT string. - * @returns Verifiable Credential. - */ -export function decodeJWT(jwt: string): VerifiableCredential { - try { - const normalizedVC = normalizeCredential(jwt); - const vc = cloneDeep(normalizedVC); - - return vc; - } catch (e) { - throw new Error('Invalid JWT'); - } -} diff --git a/packages/hedera-identify-snap/packages/snap/src/plugins/veramo/snap-data-store/src/snapDataStore.ts b/packages/hedera-identify-snap/packages/snap/src/plugins/veramo/snap-data-store/src/snapDataStore.ts index 50d46aa4..3f8f0035 100644 --- a/packages/hedera-identify-snap/packages/snap/src/plugins/veramo/snap-data-store/src/snapDataStore.ts +++ b/packages/hedera-identify-snap/packages/snap/src/plugins/veramo/snap-data-store/src/snapDataStore.ts @@ -33,15 +33,11 @@ import { } from '@veramo/key-manager'; import { sha256 } from 'js-sha256'; import jsonpath from 'jsonpath'; -import { v4 as uuidv4 } from 'uuid'; +import { getDidHederaIdentifier } from '../../../../did/hedera/hederaDidUtils'; import { getDidKeyIdentifier } from '../../../../did/key/keyDidUtils'; -import { IdentitySnapState } from '../../../../interfaces'; -import { - getAccountStateByCoinType, - getCurrentCoinType, - updateState, -} from '../../../../snap/state'; -import { decodeJWT } from '../../../../utils/jwt'; +import { SnapState } from '../../../../snap/SnapState'; +import { IdentifySnapState } from '../../../../types/state'; +import { Utils } from '../../../../utils/Utils'; import { AbstractDataStore, IFilterArgs, @@ -55,20 +51,18 @@ import { * This is usable by {@link @veramo/kms-local} to hold the key data. */ export class SnapKeyStore extends AbstractKeyStore { - state: IdentitySnapState; + state: IdentifySnapState; - constructor(state: IdentitySnapState) { + constructor(state: IdentifySnapState) { super(); this.state = state; } async getKey({ kid }: { kid: string }): Promise { - const account = this.state.currentAccount.metamaskAddress; - if (!account) { - throw Error(`SnapKeyStore - Cannot get current account: ${account}`); - } - - const accountState = await getAccountStateByCoinType(this.state, account); + const accountState = + this.state.accountState[this.state.currentAccount.snapEvmAddress][ + this.state.currentAccount.network + ]; const key = accountState.snapKeyStore[kid]; if (!key) { throw Error(`SnapKeyStore - kid '${kid}' not found`); @@ -77,43 +71,36 @@ export class SnapKeyStore extends AbstractKeyStore { } async deleteKey({ kid }: { kid: string }) { - const account = this.state.currentAccount.metamaskAddress; - if (!account) { - throw Error(`SnapKeyStore - Cannot get current account: ${account}`); - } - - const accountState = await getAccountStateByCoinType(this.state, account); + const accountState = + this.state.accountState[this.state.currentAccount.snapEvmAddress][ + this.state.currentAccount.network + ]; if (!accountState.snapKeyStore[kid]) { throw Error(`SnapKeyStore - kid '${kid}' not found`); } - const coinType = await getCurrentCoinType(); - delete this.state.accountState[coinType][account].snapKeyStore[kid]; - await updateState(this.state); + delete this.state.accountState[this.state.currentAccount.snapEvmAddress][ + this.state.currentAccount.network + ].snapKeyStore[kid]; + await SnapState.updateState(this.state); return true; } async importKey(args: IKey) { - const account = this.state.currentAccount.metamaskAddress; - if (!account) { - throw Error(`SnapKeyStore - Cannot get current account: ${account}`); - } - - const coinType = await getCurrentCoinType(); - this.state.accountState[coinType][account].snapKeyStore[args.kid] = { + this.state.accountState[this.state.currentAccount.snapEvmAddress][ + this.state.currentAccount.network + ].snapKeyStore[args.kid] = { ...args, }; - await updateState(this.state); + await SnapState.updateState(this.state); return true; } async listKeys(): Promise[]> { - const account = this.state.currentAccount.metamaskAddress; - if (!account) { - throw Error(`SnapKeyStore - Cannot get current account: ${account}`); - } - - const accountState = await getAccountStateByCoinType(this.state, account); + const accountState = + this.state.accountState[this.state.currentAccount.snapEvmAddress][ + this.state.currentAccount.network + ]; const safeKeys = Object.values(accountState.snapKeyStore).map((key) => { const { privateKeyHex, ...safeKey } = key; return safeKey; @@ -128,22 +115,18 @@ export class SnapKeyStore extends AbstractKeyStore { * This is usable by {@link @veramo/kms-local} to hold the key data. */ export class SnapPrivateKeyStore extends AbstractPrivateKeyStore { - state: IdentitySnapState; + state: IdentifySnapState; - constructor(state: IdentitySnapState) { + constructor(state: IdentifySnapState) { super(); this.state = state; } async getKey({ alias }: { alias: string }): Promise { - const account = this.state.currentAccount.metamaskAddress; - if (!account) { - throw Error( - `SnapPrivateKeyStore - Cannot get current account: ${account}`, - ); - } - - const accountState = await getAccountStateByCoinType(this.state, account); + const accountState = + this.state.accountState[this.state.currentAccount.snapEvmAddress][ + this.state.currentAccount.network + ]; const key = accountState.snapPrivateKeyStore[alias]; if (!key) { throw Error( @@ -154,36 +137,28 @@ export class SnapPrivateKeyStore extends AbstractPrivateKeyStore { } async deleteKey({ alias }: { alias: string }) { - const account = this.state.currentAccount.metamaskAddress; - if (!account) { - throw Error( - `SnapPrivateKeyStore - Cannot get current account: ${account}`, - ); - } - - const accountState = await getAccountStateByCoinType(this.state, account); + const accountState = + this.state.accountState[this.state.currentAccount.snapEvmAddress][ + this.state.currentAccount.network + ]; if (!accountState.snapPrivateKeyStore[alias]) { throw Error('SnapPrivateKeyStore - Key not found'); } - const coinType = await getCurrentCoinType(); - delete this.state.accountState[coinType][account].snapPrivateKeyStore[ - alias - ]; - await updateState(this.state); + delete this.state.accountState[this.state.currentAccount.snapEvmAddress][ + this.state.currentAccount.network + ].snapPrivateKeyStore[alias]; + await SnapState.updateState(this.state); return true; } async importKey(args: ImportablePrivateKey) { - const account = this.state.currentAccount.metamaskAddress; - if (!account) { - throw Error( - `SnapPrivateKeyStore - Cannot get current account: ${account}`, - ); - } + const accountState = + this.state.accountState[this.state.currentAccount.snapEvmAddress][ + this.state.currentAccount.network + ]; - const alias = args.alias || uuidv4(); - const accountState = await getAccountStateByCoinType(this.state, account); + const alias = args.alias || sha256(JSON.stringify(args)); const existingEntry = accountState.snapPrivateKeyStore[alias]; if (existingEntry && existingEntry.privateKeyHex !== args.privateKeyHex) { console.error( @@ -194,26 +169,23 @@ export class SnapPrivateKeyStore extends AbstractPrivateKeyStore { ); } - const coinType = await getCurrentCoinType(); - this.state.accountState[coinType][account].snapPrivateKeyStore[alias] = { + this.state.accountState[this.state.currentAccount.snapEvmAddress][ + this.state.currentAccount.network + ].snapPrivateKeyStore[alias] = { ...args, alias, }; - await updateState(this.state); - return this.state.accountState[coinType][account].snapPrivateKeyStore[ - alias - ]; + await SnapState.updateState(this.state); + return this.state.accountState[this.state.currentAccount.snapEvmAddress][ + this.state.currentAccount.network + ].snapPrivateKeyStore[alias]; } async listKeys(): Promise { - const account = this.state.currentAccount.metamaskAddress; - if (!account) { - throw Error( - `SnapPrivateKeyStore - Cannot get current account: ${account}`, - ); - } - - const accountState = await getAccountStateByCoinType(this.state, account); + const accountState = + this.state.accountState[this.state.currentAccount.snapEvmAddress][ + this.state.currentAccount.network + ]; return [...Object.values(accountState.snapPrivateKeyStore)]; } } @@ -224,9 +196,9 @@ export class SnapPrivateKeyStore extends AbstractPrivateKeyStore { * This is usable by {@link @veramo/did-manager} to hold the did key data. */ export class SnapDIDStore extends AbstractDIDStore { - state: IdentitySnapState; + state: IdentifySnapState; - constructor(state: IdentitySnapState) { + constructor(state: IdentifySnapState) { super(); this.state = state; } @@ -240,14 +212,11 @@ export class SnapDIDStore extends AbstractDIDStore { alias: string; provider: string; }): Promise { - const account = this.state.currentAccount.metamaskAddress; - if (!account) { - throw Error(`SnapDIDStore - Cannot get current account: ${account}`); - } - const { identifiers } = await getAccountStateByCoinType( - this.state, - account, - ); + const accountState = + this.state.accountState[this.state.currentAccount.snapEvmAddress][ + this.state.currentAccount.network + ]; + const { identifiers } = accountState; if (did && !alias) { if (!identifiers[did]) { @@ -276,30 +245,24 @@ export class SnapDIDStore extends AbstractDIDStore { } async deleteDID({ did }: { did: string }) { - const account = this.state.currentAccount.metamaskAddress; - if (!account) { - throw Error(`SnapDIDStore - Cannot get current account: ${account}`); - } - - const accountState = await getAccountStateByCoinType(this.state, account); + const accountState = + this.state.accountState[this.state.currentAccount.snapEvmAddress][ + this.state.currentAccount.network + ]; if (!accountState.identifiers[did]) { throw Error( `SnapDIDStore - not_found: IIdentifier not found with did=${did}`, ); } - const coinType = await getCurrentCoinType(); - delete this.state.accountState[coinType][account].identifiers[did]; - await updateState(this.state); + delete this.state.accountState[this.state.currentAccount.snapEvmAddress][ + this.state.currentAccount.network + ].identifiers[did]; + await SnapState.updateState(this.state); return true; } async importDID(args: IIdentifier) { - const account = this.state.currentAccount.metamaskAddress; - if (!account) { - throw Error(`SnapDIDStore - Cannot get current account: ${account}`); - } - const identifier = { ...args }; for (const key of identifier.keys) { if ('privateKeyHex' in key) { @@ -307,11 +270,10 @@ export class SnapDIDStore extends AbstractDIDStore { } } - const coinType = await getCurrentCoinType(); - console.log('account: ', account, ' did: ', args.did); - this.state.accountState[coinType][account].identifiers[args.did] = - identifier; - await updateState(this.state); + this.state.accountState[this.state.currentAccount.snapEvmAddress][ + this.state.currentAccount.network + ].identifiers[args.did] = identifier; + await SnapState.updateState(this.state); return true; } @@ -319,12 +281,10 @@ export class SnapDIDStore extends AbstractDIDStore { alias?: string; provider?: string; }): Promise { - const account = this.state.currentAccount.metamaskAddress; - if (!account) { - throw Error(`SnapDIDStore - Cannot get current account: ${account}`); - } - - const accountState = await getAccountStateByCoinType(this.state, account); + const accountState = + this.state.accountState[this.state.currentAccount.snapEvmAddress][ + this.state.currentAccount.network + ]; let result: IIdentifier[] = []; for (const key of Object.keys(accountState.identifiers)) { result.push(accountState.identifiers[key]); @@ -350,30 +310,28 @@ export class SnapDIDStore extends AbstractDIDStore { * This is usable by {@link @vc-manager/VCManager} to hold the vc data */ export class SnapVCStore extends AbstractDataStore { - state: IdentitySnapState; + state: IdentifySnapState; configure: undefined; - constructor(state: IdentitySnapState) { + constructor(state: IdentifySnapState) { super(); this.state = state; } async queryVC(args: IFilterArgs): Promise { const { filter } = args; - const account = this.state.currentAccount.metamaskAddress; - if (!account) { - throw Error(`SnapVCStore - Cannot get current account: ${account}`); - } - - const accountState = await getAccountStateByCoinType(this.state, account); + const accountState = + this.state.accountState[this.state.currentAccount.snapEvmAddress][ + this.state.currentAccount.network + ]; const currentMethod = this.state.currentAccount.method; // Helper function to decode VC if it's in JWT format const decodeVC = (k: string) => { let vc = accountState.vcs[k] as unknown; if (typeof vc === 'string') { - vc = decodeJWT(vc); + vc = Utils.decodeJWT(vc); } return { metadata: { id: k }, data: vc }; }; @@ -417,22 +375,25 @@ export class SnapVCStore extends AbstractDataStore { async saveVC(args: { data: ISaveVC[] }): Promise { const { data: vcs } = args; - const account = this.state.currentAccount.metamaskAddress; - - if (!account) { - throw Error(`SnapVCStore - Cannot get current account: ${account}`); - } - - const coinType = await getCurrentCoinType(); - const currentMethod = this.state.currentAccount.method; // 'did:key' or 'did:pkh' + const currentMethod = this.state.currentAccount.method; const ids: string[] = []; for (const vc of vcs) { - let identifier = this.state.currentAccount.snapAddress; + let identifier = this.state.currentAccount.snapEvmAddress; if (currentMethod === 'did:key') { - identifier = await getDidKeyIdentifier( + identifier = getDidKeyIdentifier( this.state.currentAccount.publicKey, + this.state.accountState[this.state.currentAccount.snapEvmAddress][ + this.state.currentAccount.network + ].keyStore.curve, + ); + } else if (currentMethod === 'did:hedera') { + identifier = getDidHederaIdentifier( + this.state.accountState[this.state.currentAccount.snapEvmAddress][ + this.state.currentAccount.network + ], + currentMethod, ); } @@ -444,31 +405,38 @@ export class SnapVCStore extends AbstractDataStore { ) { const newId = vc.id || sha256(JSON.stringify(vc)); ids.push(newId); - this.state.accountState[coinType][account].vcs[newId] = - vc.vc as W3CVerifiableCredential; + this.state.accountState[this.state.currentAccount.snapEvmAddress][ + this.state.currentAccount.network + ].vcs[newId] = vc.vc as W3CVerifiableCredential; } } - await updateState(this.state); + await SnapState.updateState(this.state); return ids; } async deleteVC({ id }: { id: string }): Promise { - const account = this.state.currentAccount.metamaskAddress; - - if (!account) { - throw Error(`SnapVCStore - Cannot get current account: ${account}`); - } - - const accountState = await getAccountStateByCoinType(this.state, account); - const coinType = await getCurrentCoinType(); + const accountState = + this.state.accountState[this.state.currentAccount.snapEvmAddress][ + this.state.currentAccount.network + ]; // Ensure VC exists and matches the current DID method const currentMethod = this.state.currentAccount.method; - let identifier = this.state.currentAccount.snapAddress; + let identifier = this.state.currentAccount.snapEvmAddress; if (currentMethod === 'did:key') { - identifier = await getDidKeyIdentifier( + identifier = getDidKeyIdentifier( this.state.currentAccount.publicKey, + this.state.accountState[this.state.currentAccount.snapEvmAddress][ + this.state.currentAccount.network + ].keyStore.curve, + ); + } else if (currentMethod === 'did:hedera') { + identifier = getDidHederaIdentifier( + this.state.accountState[this.state.currentAccount.snapEvmAddress][ + this.state.currentAccount.network + ], + currentMethod, ); } @@ -484,26 +452,29 @@ export class SnapVCStore extends AbstractDataStore { return false; } - delete this.state.accountState[coinType][account].vcs[id]; - await updateState(this.state); + delete this.state.accountState[this.state.currentAccount.snapEvmAddress][ + this.state.currentAccount.network + ].vcs[id]; + await SnapState.updateState(this.state); return true; } public async clearVCs(_args: IFilterArgs): Promise { - const account = this.state.currentAccount.metamaskAddress; + const accountState = + this.state.accountState[this.state.currentAccount.snapEvmAddress][ + this.state.currentAccount.network + ]; - if (!account) { - throw Error(`SnapVCStore - Cannot get current account: ${account}`); - } - - const coinType = await getCurrentCoinType(); const currentMethod = this.state.currentAccount.method; // Ensure vc is correctly typed with a conditional check - const accountVCs = this.state.accountState[coinType][account].vcs; - this.state.accountState[coinType][account].vcs = Object.fromEntries( - Object.entries(accountVCs).filter(([_, vc]) => { + const { vcs } = accountState; + + this.state.accountState[this.state.currentAccount.snapEvmAddress][ + this.state.currentAccount.network + ].vcs = Object.fromEntries( + Object.entries(vcs).filter(([_, vc]) => { return ( vc && // Check vc exists typeof vc === 'object' && // Ensure vc is an object @@ -513,7 +484,7 @@ export class SnapVCStore extends AbstractDataStore { }), ); - await updateState(this.state); + await SnapState.updateState(this.state); return true; } } diff --git a/packages/hedera-identify-snap/packages/snap/src/plugins/veramo/verifiable-creds-manager/agent/dataManager.ts b/packages/hedera-identify-snap/packages/snap/src/plugins/veramo/verifiable-creds-manager/agent/dataManager.ts index c27761d6..d61698c6 100644 --- a/packages/hedera-identify-snap/packages/snap/src/plugins/veramo/verifiable-creds-manager/agent/dataManager.ts +++ b/packages/hedera-identify-snap/packages/snap/src/plugins/veramo/verifiable-creds-manager/agent/dataManager.ts @@ -189,8 +189,11 @@ export class DataManager implements IAgentPlugin { const storePlugin = this.stores[storeName]; try { - const result = await storePlugin.deleteVC({ id }); - res.push({ id, removed: result, store: storeName }); + let ids = Array.isArray(id) ? id : [id]; + for (const id of ids) { + const result = await storePlugin.deleteVC({ id }); + res.push({ id, removed: result, store: storeName }); + } } catch (e) { console.log(e); } diff --git a/packages/hedera-identify-snap/packages/snap/src/plugins/veramo/verifiable-creds-manager/data-store/memoryDataStore.ts b/packages/hedera-identify-snap/packages/snap/src/plugins/veramo/verifiable-creds-manager/data-store/memoryDataStore.ts index b1ac96ae..ded8d8f1 100644 --- a/packages/hedera-identify-snap/packages/snap/src/plugins/veramo/verifiable-creds-manager/data-store/memoryDataStore.ts +++ b/packages/hedera-identify-snap/packages/snap/src/plugins/veramo/verifiable-creds-manager/data-store/memoryDataStore.ts @@ -18,8 +18,8 @@ * */ +import { sha256 } from 'js-sha256'; import jsonpath from 'jsonpath'; -import { v4 } from 'uuid'; import { AbstractDataStore, IDeleteArgs, @@ -38,7 +38,7 @@ export class MemoryDataStore extends AbstractDataStore { // eslint-disable-next-line @typescript-eslint/require-await public async saveVC(args: ISaveArgs): Promise { - const id = v4(); + const id = sha256(JSON.stringify(args)); this.data[id] = args.data; return [id]; } diff --git a/packages/hedera-identify-snap/packages/snap/src/plugins/veramo/verifiable-creds-manager/types/IDataManager.ts b/packages/hedera-identify-snap/packages/snap/src/plugins/veramo/verifiable-creds-manager/types/IDataManager.ts index 6cb475b9..36b984a6 100644 --- a/packages/hedera-identify-snap/packages/snap/src/plugins/veramo/verifiable-creds-manager/types/IDataManager.ts +++ b/packages/hedera-identify-snap/packages/snap/src/plugins/veramo/verifiable-creds-manager/types/IDataManager.ts @@ -82,7 +82,7 @@ export type IDataManagerClearArgs = { }; export type IDataManagerDeleteArgs = { - id: string; + id: string | string[]; options?: DeleteOptions; accessToken?: string; }; diff --git a/packages/hedera-identify-snap/packages/snap/src/rpc/account/getAccountInfo.ts b/packages/hedera-identify-snap/packages/snap/src/rpc/account/getAccountInfo.ts deleted file mode 100644 index f7a35d35..00000000 --- a/packages/hedera-identify-snap/packages/snap/src/rpc/account/getAccountInfo.ts +++ /dev/null @@ -1,46 +0,0 @@ -/*- - * - * 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 { IdentitySnapParams, PublicAccountInfo } from '../../interfaces'; -import { getCurrentCoinType } from '../../snap/state'; - -/** - * Get account info such as address, did, public key, etc. - * - * @param identitySnapParams - Identity snap params. - * @returns Public Account Info. - */ -export async function getAccountInfo( - identitySnapParams: IdentitySnapParams, -): Promise { - const { state, account } = identitySnapParams; - - const coinType = (await getCurrentCoinType()).toString(); - - const publicAccountInfo: PublicAccountInfo = { - metamaskAddress: account.metamaskAddress, - snapAddress: account.snapAddress, - did: account.identifier.did, - method: account.method, - accountID: state.accountState[coinType][account.metamaskAddress] - .extraData as string, - }; - return publicAccountInfo; -} diff --git a/packages/hedera-identify-snap/packages/snap/src/rpc/did/getAvailableDIDMethods.ts b/packages/hedera-identify-snap/packages/snap/src/rpc/did/getAvailableDIDMethods.ts deleted file mode 100644 index 018acd3d..00000000 --- a/packages/hedera-identify-snap/packages/snap/src/rpc/did/getAvailableDIDMethods.ts +++ /dev/null @@ -1,30 +0,0 @@ -/*- - * - * 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 { availableMethods } from '../../types/constants'; - -/** - * Function to get available methods. - * - * @returns Available methods. - */ -export function getAvailableDIDMethods(): string[] { - return availableMethods.map((key) => key); -} diff --git a/packages/hedera-identify-snap/packages/snap/src/rpc/did/getCurrentDIDMethod.ts b/packages/hedera-identify-snap/packages/snap/src/rpc/did/getCurrentDIDMethod.ts deleted file mode 100644 index 2d6866ae..00000000 --- a/packages/hedera-identify-snap/packages/snap/src/rpc/did/getCurrentDIDMethod.ts +++ /dev/null @@ -1,33 +0,0 @@ -/*- - * - * 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 { IdentitySnapParams } from '../../interfaces'; - -/** - * Function to get available methods. - * - * @param identitySnapParams - Identity snap params. - */ -export async function getCurrentDIDMethod( - identitySnapParams: IdentitySnapParams, -): Promise { - const { state } = identitySnapParams; - return state.snapConfig.dApp.didMethod; -} diff --git a/packages/hedera-identify-snap/packages/snap/src/rpc/did/resolveDID.ts b/packages/hedera-identify-snap/packages/snap/src/rpc/did/resolveDID.ts deleted file mode 100644 index a4c5f3f7..00000000 --- a/packages/hedera-identify-snap/packages/snap/src/rpc/did/resolveDID.ts +++ /dev/null @@ -1,45 +0,0 @@ -/*- - * - * 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 { IdentitySnapParams } from '../../interfaces'; - -/** - * Resolve DID. - * - * @param identitySnapParams - Identity snap params. - * @param didUrl - DID url. - */ -export async function resolveDID( - identitySnapParams: IdentitySnapParams, - didUrl?: string, -): Promise { - const { account } = identitySnapParams; - - let did = didUrl; - // GET DID if not exists - if (!did) { - did = account.identifier.did; - } - - const response = await fetch( - `https://dev.uniresolver.io/1.0/identifiers/${did}`, - ); - return await response.json(); -} diff --git a/packages/hedera-identify-snap/packages/snap/src/rpc/did/switchDIDMethod.ts b/packages/hedera-identify-snap/packages/snap/src/rpc/did/switchDIDMethod.ts deleted file mode 100644 index bb415ba3..00000000 --- a/packages/hedera-identify-snap/packages/snap/src/rpc/did/switchDIDMethod.ts +++ /dev/null @@ -1,69 +0,0 @@ -/*- - * - * 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 { DialogParams, divider, heading, text } from '@metamask/snaps-sdk'; -import { IdentitySnapParams } from '../../interfaces'; -import { updateDIDMethod } from '../../snap/dapp'; -import { generateCommonPanel, snapDialog } from '../../snap/dialog'; -import { availableMethods, isValidMethod } from '../../types/constants'; - -/** - * Function to switch method. - * - * @param identitySnapParams - Identity snap params. - * @param didMethod - DID method. - */ -export async function switchDIDMethod( - identitySnapParams: IdentitySnapParams, - didMethod: string, -): Promise { - const { origin, network, state } = identitySnapParams; - - const method = state.snapConfig.dApp.didMethod; - if (!isValidMethod(didMethod)) { - console.error( - `did method '${didMethod}' not supported. Supported methods are: ${availableMethods}`, - ); - throw new Error( - `did method ${didMethod}'not supported. Supported methods are: ${availableMethods}`, - ); - } - - if (method !== didMethod) { - const dialogParams: DialogParams = { - type: 'confirmation', - content: await generateCommonPanel(origin, network, [ - heading('Switch to a different DID method to use'), - text('Would you like to change did method to the following?'), - divider(), - text(`Current DID method: ${method}`), - text(`New DID method: ${didMethod}`), - ]), - }; - - if (await snapDialog(dialogParams)) { - await updateDIDMethod(state, didMethod); - return true; - } - - return false; - } - return true; -} diff --git a/packages/hedera-identify-snap/packages/snap/src/rpc/gdrive/configureGoogleAccount.ts b/packages/hedera-identify-snap/packages/snap/src/rpc/gdrive/configureGoogleAccount.ts deleted file mode 100644 index b724a256..00000000 --- a/packages/hedera-identify-snap/packages/snap/src/rpc/gdrive/configureGoogleAccount.ts +++ /dev/null @@ -1,77 +0,0 @@ -/*- - * - * 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 { DialogParams, divider, heading, text } from '@metamask/snaps-sdk'; -import { GoogleToken, IdentitySnapParams } from '../../interfaces'; -import { verifyToken } from '../../plugins/veramo/google-drive-data-store'; -import { generateCommonPanel, snapDialog } from '../../snap/dialog'; -import { getCurrentCoinType, updateState } from '../../snap/state'; - -export const configureGoogleAccount = async ( - identitySnapParams: IdentitySnapParams, - { accessToken }: GoogleToken, -) => { - const { origin, network, state, account } = identitySnapParams; - try { - const newGUserEmail = await verifyToken(accessToken); - const coinType = await getCurrentCoinType(); - - const currentGUserInfo = - state.accountState[coinType][account.metamaskAddress].accountConfig - .identity.googleUserInfo; - - const dialogParams: DialogParams = { - type: 'confirmation', - content: await generateCommonPanel(origin, network, [ - heading('Configure Google Drive'), - text('Would you like to change your Google account to the following?'), - divider(), - text( - `Current Gdrive account: ${ - currentGUserInfo.email ? currentGUserInfo.email : 'Not yet set' - }`, - ), - text(`New Gdrive account: ${newGUserEmail}`), - ]), - }; - - const result = await snapDialog(dialogParams); - if (result) { - state.accountState[coinType][ - account.metamaskAddress - ].accountConfig.identity.googleUserInfo.accessToken = accessToken; - - state.accountState[coinType][ - account.metamaskAddress - ].accountConfig.identity.googleUserInfo.email = newGUserEmail; - - console.log('new state: ', JSON.stringify(state, null, 4)); - await updateState(state); - return true; - } - return false; - } catch (error) { - console.error( - 'Could not configure Google Drive', - JSON.stringify(error, null, 4), - ); - throw error; - } -}; diff --git a/packages/hedera-identify-snap/packages/snap/src/rpc/snap/togglePopups.ts b/packages/hedera-identify-snap/packages/snap/src/rpc/snap/togglePopups.ts deleted file mode 100644 index 5d7b726a..00000000 --- a/packages/hedera-identify-snap/packages/snap/src/rpc/snap/togglePopups.ts +++ /dev/null @@ -1,51 +0,0 @@ -/*- - * - * 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 { DialogParams, heading, text } from '@metamask/snaps-sdk'; -import { IdentitySnapParams } from '../../interfaces'; -import { updatePopups } from '../../snap/dapp'; -import { generateCommonPanel, snapDialog } from '../../snap/dialog'; - -/** - * Function to toggle popups. - * - * @param identitySnapParams - Identity snap params. - */ -export async function togglePopups( - identitySnapParams: IdentitySnapParams, -): Promise { - const { origin, network, state } = identitySnapParams; - const { disablePopups } = state.snapConfig.dApp; - - const toggleTextToShow = disablePopups ? 'enable' : 'disable'; - const dialogParams: DialogParams = { - type: 'confirmation', - content: await generateCommonPanel(origin, network, [ - heading('Toggle Popups'), - text(`Would you like to ${toggleTextToShow} the popups?`), - ]), - }; - const result = await snapDialog(dialogParams); - if (result) { - await updatePopups(state); - return true; - } - return false; -} diff --git a/packages/hedera-identify-snap/packages/snap/src/rpc/vc/createVC.ts b/packages/hedera-identify-snap/packages/snap/src/rpc/vc/createVC.ts deleted file mode 100644 index 15052abb..00000000 --- a/packages/hedera-identify-snap/packages/snap/src/rpc/vc/createVC.ts +++ /dev/null @@ -1,158 +0,0 @@ -/*- - * - * 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 { DialogParams, divider, heading, text } from '@metamask/snaps-sdk'; -import { ProofFormat, W3CVerifiableCredential } from '@veramo/core'; -import cloneDeep from 'lodash.clonedeep'; -import { v4 as uuidv4 } from 'uuid'; -import { validHederaChainID } from '../../hedera/config'; -import { IdentitySnapParams } from '../../interfaces'; -import { - IDataManagerSaveResult, - ISaveVC, - QueryMetadata, - SaveOptions, -} from '../../plugins/veramo/verifiable-creds-manager'; -import { generateCommonPanel, snapDialog } from '../../snap/dialog'; -import { getCurrentNetwork } from '../../snap/network'; -import { getAccountStateByCoinType } from '../../snap/state'; -import { - CreateVCRequestParams, - CreateVCResponseResult, -} from '../../types/params'; -import { getVeramoAgent } from '../../veramo/agent'; -import { sha256 } from 'js-sha256'; - -/** - * Function to create VC. - * - * @param identitySnapParams - Identity snap params. - * @param vcRequestParams - VC request params. - */ -export async function createVC( - identitySnapParams: IdentitySnapParams, - vcRequestParams: CreateVCRequestParams, -): Promise { - const { origin, network, state, account } = identitySnapParams; - - // Get Veramo agent - const agent = await getVeramoAgent(state); - - // GET DID - const { did } = account.identifier; - - const { - vcKey = 'vcData', - vcValue, - credTypes = [], - options, - } = vcRequestParams || {}; - const { store = 'snap' } = options || {}; - const optionsFiltered = { store } as SaveOptions; - - const dialogParams: DialogParams = { - type: 'confirmation', - content: await generateCommonPanel(origin, network, [ - heading('Create Verifiable Credential'), - text('Would you like to create and save the following VC in the snap?'), - divider(), - text( - JSON.stringify({ - [vcKey]: vcValue, - }), - ), - ]), - }; - - if (await snapDialog(dialogParams)) { - const issuanceDate = new Date(); - // Set the expiration date to be 1 year from the date it's issued - const expirationDate = cloneDeep(issuanceDate); - expirationDate.setFullYear( - issuanceDate.getFullYear() + 1, - issuanceDate.getMonth(), - issuanceDate.getDate(), - ); - - const credential = new Map(); - credential.set('issuanceDate', issuanceDate.toISOString()); // the entity that issued the credential+ - credential.set('expirationDate', expirationDate.toISOString()); // when the credential was issued - credential.set('type', credTypes); - - const issuer: { id: string; hederaAccountId?: string } = { id: did }; - const credentialSubject: { id: string; hederaAccountId?: string } = { - id: did, // identifier for the only subject of the credential - [vcKey]: vcValue, // assertion about the only subject of the credential - }; - const chainId = await getCurrentNetwork(); - const accountState = await getAccountStateByCoinType( - state, - account.metamaskAddress, - ); - if (validHederaChainID(chainId)) { - const hederaAccountId = accountState.extraData as string; - issuer.hederaAccountId = hederaAccountId; - credentialSubject.hederaAccountId = hederaAccountId; - } - credential.set('issuer', issuer); // the entity that issued the credential - credential.set('credentialSubject', credentialSubject); - - // Generate a Verifiable Credential - const verifiableCredential: W3CVerifiableCredential = - await agent.createVerifiableCredential({ - credential: JSON.parse(JSON.stringify(Object.fromEntries(credential))), - // digital proof that makes the credential tamper-evident - proofFormat: 'jwt' as ProofFormat, - }); - console.log( - 'verifiableCredential: ', - JSON.stringify(verifiableCredential, null, 4), - ); - - // Save the Verifiable Credential to all the stores the user requested for - const saved: IDataManagerSaveResult[] = await agent.saveVC({ - data: [ - { - vc: verifiableCredential, - id: sha256(JSON.stringify(verifiableCredential)), - }, - ] as ISaveVC[], - options: optionsFiltered, - accessToken: - accountState.accountConfig.identity.googleUserInfo.accessToken, - }); - - // Retrieve the created Verifiable Credential - const result: CreateVCResponseResult = { - data: verifiableCredential as W3CVerifiableCredential, - metadata: { - id: saved[0].id, - store: saved.map((res) => res.store), - } as QueryMetadata, - }; - - console.log( - 'Created and saved verifiableCredential: ', - JSON.stringify(result, null, 4), - ); - return result; - } - throw new Error('User rejected'); -} diff --git a/packages/hedera-identify-snap/packages/snap/src/rpc/vc/createVP.ts b/packages/hedera-identify-snap/packages/snap/src/rpc/vc/createVP.ts deleted file mode 100644 index 19573689..00000000 --- a/packages/hedera-identify-snap/packages/snap/src/rpc/vc/createVP.ts +++ /dev/null @@ -1,141 +0,0 @@ -/*- - * - * 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 { DialogParams } from '@metamask/snaps-sdk'; -import { - ProofFormat, - VerifiableCredential, - VerifiablePresentation, -} from '@veramo/core'; -import { IdentitySnapParams } from '../../interfaces'; -import { - IDataManagerQueryResult, - QueryOptions, -} from '../../plugins/veramo/verifiable-creds-manager'; -import { generateVCPanel, snapDialog } from '../../snap/dialog'; -import { - CreateVPOptions, - CreateVPRequestParams, - ProofInfo, -} from '../../types/params'; -import { getVeramoAgent } from '../../veramo/agent'; - -/** - * Function to create verifiable presentation. - * - * @param identitySnapParams - Identity snap params. - * @param vpRequestParams - VP request params. - */ -export async function createVP( - identitySnapParams: IdentitySnapParams, - vpRequestParams: CreateVPRequestParams, -): Promise { - const { origin, network, state, account } = identitySnapParams; - - const { - vcIds = [], - vcs = [], - proofInfo = {} as ProofInfo, - options, - } = vpRequestParams || {}; - const { store = 'snap' } = options || {}; - const optionsFiltered = { store } as CreateVPOptions; - - const proofFormat = proofInfo?.proofFormat - ? proofInfo.proofFormat - : ('jwt' as ProofFormat); - const type = proofInfo?.type ? proofInfo.type : 'Custom'; - const domain = proofInfo?.domain; - const challenge = proofInfo?.challenge; - - // Get Veramo agent - const agent = await getVeramoAgent(state); - - // GET DID - const { did } = account.identifier; - - const vcsRes: VerifiableCredential[] = []; - const vcsWithMetadata: IDataManagerQueryResult[] = []; - - // Iterate through vcIds - for (const vcId of vcIds) { - const vcObj = (await agent.queryVC({ - filter: { - type: 'id', - filter: vcId, - }, - options: optionsFiltered as QueryOptions, - })) as IDataManagerQueryResult[]; - - if (vcObj.length > 0) { - const { data, metadata } = vcObj[0]; - vcsRes.push(data as VerifiableCredential); - vcsWithMetadata.push({ - data, - metadata, - }); - } - } - - // Iterate through vcs - vcs.forEach(function (vc, index) { - vcsRes.push(vc as VerifiableCredential); - vcsWithMetadata.push({ - data: vc, - metadata: { id: `External VC #${(index + 1).toString()}`, store: 'snap' }, - }); - }); - - if (vcsRes.length === 0) { - return null; - } - const config = state.snapConfig; - - const header = 'Create Verifiable Presentation'; - const prompt = 'Do you wish to create a VP from the following VCs?'; - const description = - 'A Verifiable Presentation is a secure way for someone to present information about themselves or their identity to someone else while ensuring that the information is accureate and trustworthy'; - const dialogParams: DialogParams = { - type: 'confirmation', - content: await generateVCPanel( - origin, - network, - header, - prompt, - description, - vcsWithMetadata, - ), - }; - if (config.dApp.disablePopups || (await snapDialog(dialogParams))) { - // Generate a Verifiable Presentation from VCs - const vp = await agent.createVerifiablePresentation({ - presentation: { - holder: did, // - type: ['VerifiablePresentation', type], - verifiableCredential: vcsRes, - }, - proofFormat, // The desired format for the VerifiablePresentation to be created - domain, // Optional string domain parameter to add to the verifiable presentation - challenge, // Optional (only JWT) string challenge parameter to add to the verifiable presentation - }); - return vp; - } - throw new Error('User rejected'); -} diff --git a/packages/hedera-identify-snap/packages/snap/src/rpc/vc/deleteAllVCs.ts b/packages/hedera-identify-snap/packages/snap/src/rpc/vc/deleteAllVCs.ts deleted file mode 100644 index 13347e75..00000000 --- a/packages/hedera-identify-snap/packages/snap/src/rpc/vc/deleteAllVCs.ts +++ /dev/null @@ -1,85 +0,0 @@ -/*- - * - * 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 { DialogParams } from '@metamask/snaps-sdk'; -import { IdentitySnapParams } from '../../interfaces'; -import { - ClearOptions, - IDataManagerClearArgs, - IDataManagerClearResult, - IDataManagerQueryResult, -} from '../../plugins/veramo/verifiable-creds-manager'; -import { generateVCPanel, snapDialog } from '../../snap/dialog'; -import { getAccountStateByCoinType } from '../../snap/state'; -import { getVeramoAgent } from '../../veramo/agent'; - -/** - * Function to delete all VCs. - * - * @param identitySnapParams - Identity snap params. - * @param vcRequestParams - VC request params. - */ -export async function deleteAllVCs( - identitySnapParams: IdentitySnapParams, - vcRequestParams: IDataManagerClearArgs, -): Promise { - const { origin, network, state, account } = identitySnapParams; - const { options } = vcRequestParams || {}; - const { store = 'snap' } = options || {}; - const optionsFiltered = { store } as ClearOptions; - - // Get Veramo agent - const agent = await getVeramoAgent(state); - - const accountState = await getAccountStateByCoinType( - state, - account.metamaskAddress, - ); - const vcsToBeRemoved = (await agent.queryVC({ - filter: undefined, - options: optionsFiltered, - accessToken: accountState.accountConfig.identity.googleUserInfo.accessToken, - })) as IDataManagerQueryResult[]; - - const header = 'Delete all Verifiable Credentials'; - const prompt = `Are you sure you want to remove all your VCs from the store '${store}'?`; - const description = `Note that this action cannot be reversed and you will need to recreate your VCs if you go through with it. Number of VCs to be removed is ${vcsToBeRemoved.length.toString()}`; - const dialogParams: DialogParams = { - type: 'confirmation', - content: await generateVCPanel( - origin, - network, - header, - prompt, - description, - vcsToBeRemoved, - ), - }; - - if (await snapDialog(dialogParams)) { - // Remove all the Verifiable Credentials from the store - return await agent.clearVCs({ - options: optionsFiltered, - accessToken: - accountState.accountConfig.identity.googleUserInfo.accessToken, - }); - } - throw new Error('User rejected'); -} diff --git a/packages/hedera-identify-snap/packages/snap/src/rpc/vc/getSupportedProofFormats.ts b/packages/hedera-identify-snap/packages/snap/src/rpc/vc/getSupportedProofFormats.ts deleted file mode 100644 index e1c4791a..00000000 --- a/packages/hedera-identify-snap/packages/snap/src/rpc/vc/getSupportedProofFormats.ts +++ /dev/null @@ -1,30 +0,0 @@ -/*- - * - * 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 { availableProofFormats } from '../../types/constants'; - -/** - * Function to get supported proof formats. - * - * @returns Proof formats. - */ -export function getSupportedProofFormats(): string[] { - return availableProofFormats.map((key) => key); -} diff --git a/packages/hedera-identify-snap/packages/snap/src/rpc/vc/getVCs.ts b/packages/hedera-identify-snap/packages/snap/src/rpc/vc/getVCs.ts deleted file mode 100644 index 4cacf4d9..00000000 --- a/packages/hedera-identify-snap/packages/snap/src/rpc/vc/getVCs.ts +++ /dev/null @@ -1,82 +0,0 @@ -/*- - * - * 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 { DialogParams } from '@metamask/snaps-sdk'; -import { IdentitySnapParams } from '../../interfaces'; -import { - IDataManagerQueryArgs, - IDataManagerQueryResult, - QueryOptions, -} from '../../plugins/veramo/verifiable-creds-manager'; -import { generateVCPanel, snapDialog } from '../../snap/dialog'; -import { getAccountStateByCoinType } from '../../snap/state'; -import { getVeramoAgent } from '../../veramo/agent'; - -/** - * Function to get VCs. - * - * @param identitySnapParams - Identity snap params. - * @param vcRequestParams - VC request params. - */ -export async function getVCs( - identitySnapParams: IdentitySnapParams, - vcRequestParams: IDataManagerQueryArgs, -): Promise { - const { origin, network, state, account } = identitySnapParams; - - const { filter, options } = vcRequestParams || {}; - const { store = 'snap', returnStore = true } = options || {}; - - // Get Veramo agent - const agent = await getVeramoAgent(state); - - // Get VCs - const accountState = await getAccountStateByCoinType( - state, - account.metamaskAddress, - ); - const optionsFiltered = { store, returnStore } as QueryOptions; - const vcs = (await agent.queryVC({ - filter, - options: optionsFiltered, - accessToken: accountState.accountConfig.identity.googleUserInfo.accessToken, - })) as IDataManagerQueryResult[]; - - const header = 'Retrieve Verifiable Credentials'; - const prompt = 'Are you sure you want to send VCs to the dApp?'; - const description = `Some dApps are less secure than others and could save data from VCs against your will. Be careful where you send your private VCs! Number of VCs submitted is ${vcs.length.toString()}`; - const dialogParams: DialogParams = { - type: 'confirmation', - content: await generateVCPanel( - origin, - network, - header, - prompt, - description, - vcs, - ), - }; - - if (state.snapConfig.dApp.disablePopups || (await snapDialog(dialogParams))) { - return vcs; - } - - return []; -} diff --git a/packages/hedera-identify-snap/packages/snap/src/rpc/vc/removeVC.ts b/packages/hedera-identify-snap/packages/snap/src/rpc/vc/removeVC.ts deleted file mode 100644 index 95f0ac34..00000000 --- a/packages/hedera-identify-snap/packages/snap/src/rpc/vc/removeVC.ts +++ /dev/null @@ -1,108 +0,0 @@ -/*- - * - * 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 { DialogParams } from '@metamask/snaps-sdk'; -import { IdentitySnapParams } from '../../interfaces'; -import { - DeleteOptions, - IDataManagerDeleteArgs, - IDataManagerDeleteResult, - IDataManagerQueryResult, -} from '../../plugins/veramo/verifiable-creds-manager'; -import { generateVCPanel, snapDialog } from '../../snap/dialog'; -import { getAccountStateByCoinType } from '../../snap/state'; -import { getVeramoAgent } from '../../veramo/agent'; - -/** - * Function to remove VC. - * - * @param identitySnapParams - Identity snap params. - * @param vcRequestParams - VC request params. - */ -export async function removeVC( - identitySnapParams: IdentitySnapParams, - vcRequestParams: IDataManagerDeleteArgs, -): Promise { - const { origin, network, state, account } = identitySnapParams; - - const { id = '', options } = vcRequestParams || {}; - const { store = 'snap' } = options || {}; - const optionsFiltered = { store } as DeleteOptions; - - // Get Veramo agent - const agent = await getVeramoAgent(state); - - const ids = typeof id === 'string' ? [id] : id; - if (ids.length === 0) { - return null; - } - - const accountState = await getAccountStateByCoinType( - state, - account.metamaskAddress, - ); - const vcsToBeRemoved: IDataManagerQueryResult[] = []; - for (const vcId of ids) { - const vcs = (await agent.queryVC({ - filter: { - type: 'id', - filter: vcId, - }, - options: optionsFiltered, - accessToken: - accountState.accountConfig.identity.googleUserInfo.accessToken, - })) as IDataManagerQueryResult[]; - if (vcs.length > 0) { - vcsToBeRemoved.push(vcs[0]); - } - } - - const header = 'Remove specific Verifiable Credentials'; - const prompt = 'Are you sure you want to remove the following VCs?'; - const description = `Note that this action cannot be reversed and you will need to recreate your VCs if you go through with it. Number of VCs to be removed is ${vcsToBeRemoved.length.toString()}`; - const dialogParams: DialogParams = { - type: 'confirmation', - content: await generateVCPanel( - origin, - network, - header, - prompt, - description, - vcsToBeRemoved, - ), - }; - - if (await snapDialog(dialogParams)) { - // Remove the specified Verifiable Credentials from the store based on their IDs - return Promise.all( - ids.map(async (_id: string) => { - return await agent.deleteVC({ - id: _id, - options: optionsFiltered, - accessToken: - accountState.accountConfig.identity.googleUserInfo.accessToken, - }); - }), - ).then((data: IDataManagerDeleteResult[][]) => { - return data.flat(); - }); - } - throw new Error('User rejected'); -} diff --git a/packages/hedera-identify-snap/packages/snap/src/rpc/vc/saveVC.ts b/packages/hedera-identify-snap/packages/snap/src/rpc/vc/saveVC.ts deleted file mode 100644 index 988ad129..00000000 --- a/packages/hedera-identify-snap/packages/snap/src/rpc/vc/saveVC.ts +++ /dev/null @@ -1,117 +0,0 @@ -/*- - * - * 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 { DialogParams } from '@metamask/snaps-sdk'; -import { W3CVerifiableCredential } from '@veramo/core'; -import { IdentitySnapParams } from '../../interfaces'; -import { - IDataManagerQueryResult, - IDataManagerSaveArgs, - IDataManagerSaveResult, - ISaveVC, - SaveOptions, -} from '../../plugins/veramo/verifiable-creds-manager'; -import { generateVCPanel, snapDialog } from '../../snap/dialog'; -import { getAccountStateByCoinType } from '../../snap/state'; -import { getVeramoAgent } from '../../veramo/agent'; - -/** - * Function to save VC. - * - * @param identitySnapParams - Identity snap params. - * @param vcSaveRequestParams - VC save request params. - */ -export async function saveVC( - identitySnapParams: IdentitySnapParams, - vcSaveRequestParams: IDataManagerSaveArgs, -): Promise { - const { origin, network, state, account } = identitySnapParams; - - const { data: verifiableCredentials, options } = vcSaveRequestParams || {}; - const { store = 'snap' } = options || {}; - - const optionsFiltered = { store } as SaveOptions; - - // Get Veramo agent - const agent = await getVeramoAgent(state); - - const header = 'Save Verifiable Credentials'; - const prompt = `Are you sure you want to save the following VCs in ${ - typeof store === 'string' ? store : store.join(', ') - }?`; - const description = `Number of VCs submitted is ${( - verifiableCredentials as W3CVerifiableCredential[] - ).length.toString()}`; - - const vcsWithMetadata: IDataManagerQueryResult[] = []; - // Iterate through vcs - (verifiableCredentials as W3CVerifiableCredential[]).forEach( - function (vc, index) { - vcsWithMetadata.push({ - data: vc, - metadata: { - id: `External VC #${(index + 1).toString()}`, - store: 'snap', - }, - }); - }, - ); - const dialogParams: DialogParams = { - type: 'confirmation', - content: await generateVCPanel( - origin, - network, - header, - prompt, - description, - vcsWithMetadata, - ), - }; - - if (await snapDialog(dialogParams)) { - // Save the Verifiable Credential - const accountState = await getAccountStateByCoinType( - state, - account.metamaskAddress, - ); - - const filteredCredentials: W3CVerifiableCredential[] = ( - verifiableCredentials as W3CVerifiableCredential[] - ).filter((x: W3CVerifiableCredential) => { - const vcObj = JSON.parse(JSON.stringify(x)); - - const subjectDid: string = vcObj.credentialSubject.id; - const subjectAccount = subjectDid.split(':')[4]; - return account.metamaskAddress === subjectAccount; - }); - return await agent.saveVC({ - data: (filteredCredentials as W3CVerifiableCredential[]).map( - (x: W3CVerifiableCredential) => { - return { vc: x } as ISaveVC; - }, - ) as ISaveVC[], - options: optionsFiltered, - accessToken: - accountState.accountConfig.identity.googleUserInfo.accessToken, - }); - } - - throw new Error('User rejected'); -} diff --git a/packages/hedera-identify-snap/packages/snap/src/rpc/vc/verifyVP.ts b/packages/hedera-identify-snap/packages/snap/src/rpc/vc/verifyVP.ts deleted file mode 100644 index 13952687..00000000 --- a/packages/hedera-identify-snap/packages/snap/src/rpc/vc/verifyVP.ts +++ /dev/null @@ -1,51 +0,0 @@ -/*- - * - * 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 { VerifiablePresentation } from '@veramo/core'; -import { IdentitySnapParams } from '../../interfaces'; -import { getVeramoAgent } from '../../veramo/agent'; - -/** - * Function to verify VP. - * - * @param identitySnapParams - Identity snap params. - * @param vp - Verifiable Presentation. - */ -export async function verifyVP( - identitySnapParams: IdentitySnapParams, - vp: VerifiablePresentation, -): Promise { - const { state } = identitySnapParams; - // Get Veramo agent - const agent = await getVeramoAgent(state); - - // Verify the verifiable presentation(VP) - const result = await agent.verifyPresentation({ - presentation: vp, - }); - if (result.verified === false) { - console.log('result: ', JSON.stringify(result, null, 4)); - console.log( - 'VP Verification Error: ', - JSON.stringify(result.error, null, 4), - ); - } - return result.verified; -} diff --git a/packages/hedera-identify-snap/packages/snap/src/snap/SnapAccounts.ts b/packages/hedera-identify-snap/packages/snap/src/snap/SnapAccounts.ts new file mode 100644 index 00000000..b675bc8a --- /dev/null +++ b/packages/hedera-identify-snap/packages/snap/src/snap/SnapAccounts.ts @@ -0,0 +1,638 @@ +/*- + * + * 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 { PrivateKey } from '@hashgraph/sdk'; +import { rpcErrors } from '@metamask/rpc-errors'; + +import type { DialogParams } from '@metamask/snaps-sdk'; +import { copyable, divider, heading, text } from '@metamask/snaps-sdk'; +import { IIdentifier, MinimalImportableKey, TKeyType } from '@veramo/core'; +import { ethers, Wallet } from 'ethers'; +import _ from 'lodash'; +import { HederaClientImplFactory } from '../client/HederaClientImplFactory'; +import { + ECDSA_SECP256K1_KEY_TYPE, + ED25519_KEY_TYPE, +} from '../constants/crypto'; +import { + getDidHederaIdentifier, + getHcsDidClient, +} from '../did/hedera/hederaDidUtils'; +import { getDidKeyIdentifier } from '../did/key/keyDidUtils'; +import type { + Account, + ExternalAccount, + HederaAccountInfo, +} from '../types/account'; +import type { IdentifySnapState, KeyStore } from '../types/state'; +import { CryptoUtils } from '../utils/CryptoUtils'; +import { EvmUtils } from '../utils/EvmUtils'; +import { HederaUtils } from '../utils/HederaUtils'; +import { SnapUtils } from '../utils/SnapUtils'; +import { StateUtils } from '../utils/StateUtils'; +import { Utils } from '../utils/Utils'; +import { getVeramoAgent } from '../veramo/agent'; +import { SnapState } from './SnapState'; + +export class SnapAccounts { + /** + * Function that creates an empty IdentitySnapState object in the Identity Snap state for the provided address. + * @param state - WalletSnapState. + * @param network - Hedera network. + * @param evmAddress - The account address. + */ + public static async initAccountState( + state: IdentifySnapState, + network: string, + evmAddress: string, + ): Promise { + state.currentAccount = { snapEvmAddress: evmAddress } as Account; + if (_.isEmpty(state.accountState[evmAddress])) { + state.accountState[evmAddress] = {}; + } + + state.accountState[evmAddress][network] = StateUtils.getEmptyAccountState(); + + await SnapState.updateState(state); + } + + /** + * Check if Hedera account was imported. + * @param state - WalletSnapState. + * @param network - Hedera network. + * @param evmAddress - Ethereum address. + * @returns Result. + */ + public static async getHederaAccountIdIfExists( + state: IdentifySnapState, + network: string, + evmAddress: string, + ): Promise { + let result = ''; + for (const address of Object.keys(state.accountState)) { + if (state.accountState[address][network]) { + const { keyStore } = state.accountState[address][network]; + if (keyStore.address === evmAddress) { + result = keyStore.hederaAccountId; + } + } + } + return result; + } + + public static async getCurrentMetamaskAccount(): Promise { + const accounts = (await ethereum.request({ + method: 'eth_requestAccounts', + })) as string[]; + return accounts[0]; + } + + /** + * Function that returns account info of the currently selected MetaMask account. + * @param origin - Source. + * @param state - WalletSnapState. + * @param params - Parameters that were passed by the user. + * @param network - Hedera network. + * @param isExternalAccount - Whether this is a metamask or a non-metamask account. + * @param returnEarly - Whether to return early. + * @returns Nothing. + */ + public static async setCurrentAccount( + origin: string, + state: IdentifySnapState, + params: unknown, + network: string, + isExternalAccount: boolean, + returnEarly = false, + ): Promise { + let metamaskEvmAddress = ''; + let externalEvmAddress = ''; + let connectedAddress = ''; + let keyStore = {} as KeyStore; + // Handle external account(non-metamask account) + if (isExternalAccount) { + const nonMetamaskAccount = params as ExternalAccount; + const { accountIdOrEvmAddress, curve = ECDSA_SECP256K1_KEY_TYPE } = + nonMetamaskAccount.externalAccount; + if ( + state.snapConfig.dApp.didMethod !== 'did:key' && + curve !== ECDSA_SECP256K1_KEY_TYPE + ) { + const errMessage = `You must connect using the curve '${ECDSA_SECP256K1_KEY_TYPE}' for did method '${state.snapConfig.dApp.didMethod}'. Please make sure to pass in the correct value for "curve".`; + console.error(errMessage); + throw rpcErrors.invalidRequest({ + message: errMessage, + data: { network, curve, accountIdOrEvmAddress }, + }); + } + if (ethers.isAddress(accountIdOrEvmAddress)) { + const { connectedAddress: _connectedAddress, keyStore: _keyStore } = + await SnapAccounts.connectEVMAccount( + origin, + state, + network, + ECDSA_SECP256K1_KEY_TYPE, + Utils.ensure0xPrefix(accountIdOrEvmAddress), + ); + connectedAddress = _connectedAddress; + keyStore = _keyStore; + } else { + try { + const { connectedAddress: _connectedAddress, keyStore: _keyStore } = + await SnapAccounts.connectHederaAccount( + origin, + state, + network, + ECDSA_SECP256K1_KEY_TYPE, + (accountIdOrEvmAddress as string).toLowerCase(), + ); + connectedAddress = _connectedAddress; + keyStore = _keyStore; + } catch (error: any) { + const address = accountIdOrEvmAddress as string; + const errMessage = `Could not connect to the Hedera account ${address} on ${network}`; + console.error('Error occurred: %s', errMessage, String(error)); + throw rpcErrors.resourceNotFound({ + message: errMessage, + data: address, + }); + } + } + externalEvmAddress = connectedAddress.toLowerCase(); + } else { + // Handle metamask connected account + connectedAddress = await SnapAccounts.getCurrentMetamaskAccount(); + metamaskEvmAddress = connectedAddress.toLowerCase(); + + // Generate a new wallet according to the Hedera Wallet's entrophy combined with the currently connected EVM address + const res = await CryptoUtils.generateWallet(connectedAddress); + if (!res) { + const errMessage = `Failed to generate snap wallet for ${connectedAddress}`; + console.log(errMessage); + throw rpcErrors.internal(errMessage); + } + keyStore.curve = ECDSA_SECP256K1_KEY_TYPE; + keyStore.privateKey = res.privateKey.split('0x')[1]; + keyStore.publicKey = res.publicKey.split('0x')[1]; + keyStore.address = res.address.toLowerCase(); + connectedAddress = res.address.toLowerCase(); + keyStore.hederaAccountId = await SnapAccounts.getHederaAccountIdIfExists( + state, + network, + connectedAddress, + ); + } + + connectedAddress = connectedAddress.toLowerCase(); + + // Initialize if not in snap state + if ( + !Object.keys(state.accountState).includes(connectedAddress) || + (Object.keys(state.accountState).includes(connectedAddress) && + !Object.keys(state.accountState[connectedAddress]).includes(network)) + ) { + console.log( + `The address ${connectedAddress} has NOT yet been configured for the '${network}' network in the Hedera Wallet. Configuring now...`, + ); + await SnapAccounts.initAccountState(state, network, connectedAddress); + } + + return await SnapAccounts.importMetaMaskAccount( + state, + network, + metamaskEvmAddress, + externalEvmAddress, + keyStore, + returnEarly, + ); + } + + /** + * Connect EVM Account. + * @param origin - Source. + * @param state - Wallet state. + * @param network - Hedera network. + * @param curve - Public Key curve('ECDSA_SECP256K1' | 'ED25519'). + * @param evmAddress - EVM Account address. + * @returns Result. + */ + public static async connectEVMAccount( + origin: string, + state: IdentifySnapState, + network: string, + curve: string, + evmAddress: string, + ): Promise { + let result = {} as KeyStore; + let connectedAddress = ''; + for (const addr of Object.keys(state.accountState)) { + if (state.accountState[addr][network]) { + const { keyStore } = state.accountState[addr][network]; + + if (evmAddress === keyStore.address) { + if (keyStore.curve !== curve) { + const errMessage = `You passed '${curve}' as the digital signature algorithm to use but the account was derived using ${keyStore.curve} on '${network}'. Please make sure to pass in the correct value for "curve".`; + console.log(errMessage); + throw rpcErrors.invalidRequest({ + message: errMessage, + data: { network, curve, evmAddress }, + }); + } + connectedAddress = addr; + result = keyStore; + break; + } + } + } + + if (_.isEmpty(connectedAddress)) { + const dialogParamsForPrivateKey: DialogParams = { + type: 'prompt', + content: await SnapUtils.generateCommonPanel(origin, network, [ + heading('Connect to EVM Account'), + divider(), + text(`EVM Address:`), + copyable(evmAddress), + ]), + placeholder: '2386d1d21644dc65d...', + }; + const privateKey = (await SnapUtils.snapDialog( + dialogParamsForPrivateKey, + )) as string; + + try { + const wallet: Wallet = new ethers.Wallet(privateKey); + result.curve = ECDSA_SECP256K1_KEY_TYPE; + result.privateKey = privateKey.startsWith('0x') + ? privateKey.split('0x')[1] + : privateKey; + result.publicKey = wallet.signingKey.publicKey.startsWith('0x') + ? wallet.signingKey.publicKey.split('0x')[1] + : wallet.signingKey.publicKey; + result.address = wallet.address.toLowerCase(); + connectedAddress = wallet.address.toLowerCase(); + } catch (error: any) { + const errMessage = `Could not connect to EVM account. Please try again`; + console.error('Error occurred: %s', errMessage, String(error)); + await SnapUtils.snapNotification( + `Error occurred: ${errMessage} - ${String(error)}`, + ); + throw rpcErrors.transactionRejected(errMessage); + } + } + + return { + connectedAddress, + keyStore: result, + }; + } + + /** + * Connect Hedera Account. + * @param origin - Source. + * @param state - Wallet state. + * @param network - Hedera network. + * @param curve - Public Key curve('Secp256k1' | 'Ed25519'). + * @param accountId - Hedera Account id. + * @returns Result. + */ + public static async connectHederaAccount( + origin: string, + state: IdentifySnapState, + network: string, + curve: string, + accountId: string, + ): Promise { + let result = {} as KeyStore; + let connectedAddress = ''; + for (const addr of Object.keys(state.accountState)) { + if (state.accountState[addr][network]) { + const { keyStore } = state.accountState[addr][network]; + if (keyStore.hederaAccountId === accountId) { + if (keyStore.curve !== curve) { + const errMessage = `You passed '${curve}' as the digital signature algorithm to use but the account was derived using ${keyStore.curve} on '${network}'. Please make sure to pass in the correct value for "curve".`; + console.error(errMessage); + throw rpcErrors.invalidRequest({ + message: errMessage, + data: { network, curve, accountId }, + }); + } + connectedAddress = addr; + result = keyStore; + break; + } + } + } + + if (_.isEmpty(connectedAddress)) { + const dialogParamsForPrivateKey: DialogParams = { + type: 'prompt', + content: await SnapUtils.generateCommonPanel(origin, network, [ + heading('Connect to Hedera Account'), + text('Enter private key for the following account'), + divider(), + text(`Account Id:`), + copyable(accountId), + ]), + placeholder: '2386d1d21644dc65d...', + }; + const privateKey = (await SnapUtils.snapDialog( + dialogParamsForPrivateKey, + )) as string; + + try { + const { mirrorNodeUrl } = HederaUtils.getHederaNetworkInfo(network); + const accountInfo: HederaAccountInfo = + await HederaUtils.getMirrorAccountInfo(accountId, mirrorNodeUrl); + + let publicKey = + PrivateKey.fromStringECDSA(privateKey).publicKey.toStringRaw(); + if (curve === ED25519_KEY_TYPE) { + publicKey = + PrivateKey.fromStringED25519(privateKey).publicKey.toStringRaw(); + } + publicKey = publicKey.startsWith('0x') + ? publicKey.split('0x')[1] + : publicKey; + if (_.isEmpty(accountInfo)) { + const errMessage = `This Hedera account is not yet active. Please activate it by sending some HBAR to this account on '${network}'. Account Id: ${accountId} Public Key: ${publicKey}`; + console.error(errMessage); + throw rpcErrors.resourceNotFound({ + message: errMessage, + data: { network, curve, accountId, publicKey }, + }); + } + + if ( + accountInfo.key.type === 'ProtobufEncoded' && + curve !== ECDSA_SECP256K1_KEY_TYPE + ) { + const errMessage = `You passed '${curve}' as the digital signature algorithm to use but the account was derived using '${ECDSA_SECP256K1_KEY_TYPE}' on '${network}'. Please make sure to pass in the correct value for "curve".`; + console.error(errMessage); + throw rpcErrors.invalidRequest({ + message: errMessage, + data: { network, curve, accountId, publicKey }, + }); + } + + if ( + accountInfo.key.type !== 'ProtobufEncoded' && + accountInfo.key.type !== curve + ) { + const errMessage = `You passed '${curve}' as the digital signature algorithm to use but the account was derived using '${accountInfo.key.type}' on '${network}'. Please make sure to pass in the correct value for "curve".`; + console.error(errMessage); + throw rpcErrors.invalidRequest({ + message: errMessage, + data: { network, curve, accountId, publicKey }, + }); + } + + const hederaClientFactory = new HederaClientImplFactory( + accountId, + network, + curve, + privateKey, + ); + const hederaClient = await hederaClientFactory.createClient(); + + if (hederaClient) { + result.privateKey = privateKey.startsWith('0x') + ? privateKey.split('0x')[1] + : privateKey; + result.curve = curve; + result.publicKey = publicKey.startsWith('0x') + ? publicKey.split('0x')[1] + : publicKey; + result.hederaAccountId = accountId; + result.address = Utils.ensure0xPrefix(accountInfo.evmAddress); + connectedAddress = Utils.ensure0xPrefix(accountInfo.evmAddress); + } else { + const dialogParamsForHederaAccountId: DialogParams = { + type: 'alert', + content: await SnapUtils.generateCommonPanel(origin, network, [ + heading('Hedera Account Status'), + text( + `The private key you passed is not associated with the Hedera account '${accountId}' on '${network}' that uses the elliptic curve '${curve}'`, + ), + ]), + }; + await SnapUtils.snapDialog(dialogParamsForHederaAccountId); + + const errMessage = `The private key you passed is not associated with the Hedera account '${accountId}' on '${network}' that uses the elliptic curve '${curve}'`; + console.error(errMessage); + throw rpcErrors.invalidRequest({ + message: errMessage, + data: { network, curve, accountId, publicKey }, + }); + } + } catch (error: any) { + const errMessage = `Could not setup a Hedera client. Please try again`; + console.error('Error occurred: %s', errMessage, String(error)); + await SnapUtils.snapNotification( + `Error occurred: ${errMessage} - ${String(error)}`, + ); + throw rpcErrors.transactionRejected(errMessage); + } + } + + return { + connectedAddress, + keyStore: result, + }; + } + + /** + * Veramo Import metamask account. + * @param state - HederaWalletSnapState. + * @param network - Hedera network. + * @param metamaskEvmAddress - Metamask EVM address. + * @param externalEvmAddress - External EVM address. + * @param keyStore - Keystore for private, public keys and EVM address. + * @param returnEarly - Whether to return early. + * @returns Result. + */ + public static async importMetaMaskAccount( + state: IdentifySnapState, + network: string, + metamaskEvmAddress: string, + externalEvmAddress: string, + keyStore: KeyStore, + returnEarly = false, + ): Promise { + const { curve, privateKey, publicKey, address } = keyStore; + + const { hederaNetwork, mirrorNodeUrl } = + HederaUtils.getHederaNetworkInfo(network); + const accountInfo: HederaAccountInfo = + await HederaUtils.getMirrorAccountInfo(address, mirrorNodeUrl); + + const method = state.snapConfig.dApp.didMethod; + if (_.isEmpty(accountInfo)) { + if (method === 'did:hedera') { + const errMessage = `Could not get account info from Hedera Mirror Node on '${hederaNetwork}'. Address: ${address}. Please ensure that this account has been activated on ledger.`; + if (returnEarly) { + // eslint-disable-next-line require-atomic-updates + state.accountState[address][network].keyStore = { + curve, + privateKey, + publicKey, + address, + hederaAccountId: '', + }; + + await SnapState.updateState(state); + return address; + } + throw rpcErrors.resourceNotFound({ + message: errMessage, + data: address, + }); + } + accountInfo.accountId = ''; + accountInfo.evmAddress = address; + accountInfo.key = { + type: curve, + key: publicKey, + }; + } + + // eslint-disable-next-line require-atomic-updates + state.accountState[address][network].accountInfo = accountInfo; + + // eslint-disable-next-line require-atomic-updates + state.accountState[address][network].keyStore = { + curve, + privateKey, + publicKey, + address, + hederaAccountId: accountInfo.accountId, + }; + + // eslint-disable-next-line require-atomic-updates + state.currentAccount = { + metamaskEvmAddress, + externalEvmAddress, + method, + hederaAccountId: accountInfo.accountId, + snapEvmAddress: accountInfo.evmAddress, + privateKey, + publicKey, + network, + } as Account; + + let did = ''; + if (method === 'did:pkh') { + did = `did:pkh:eip155:${EvmUtils.convertChainIdFromHex(network)}:${address}`; + } else if (method === 'did:key') { + did = `did:key:${getDidKeyIdentifier(publicKey, curve)}`; + } else if (method === 'did:hedera') { + did = `did:hedera:${hederaNetwork}:${getDidHederaIdentifier( + state.accountState[address][network], + method, + )}`; + if (_.isEmpty(did.split(':').pop())) { + // Register the DID on Hedera Consensus Network + const hederaDidClient = await getHcsDidClient(state); + if (!hederaDidClient) { + console.error('Failed to create HcsDid client'); + throw new Error('Failed to create HcsDid client'); + } + try { + const registeredDid = await hederaDidClient.register(); + did = registeredDid.getIdentifier() || ''; + } catch (e: any) { + const errMessage = `Failed to register DID on Hedera ${hederaNetwork} network.`; + console.error(`${errMessage}: ${JSON.stringify(e)}`); + + // Use normalized errorStatus for the check + if ( + String(e.status || '') + .trim() + .toUpperCase() === 'INSUFFICIENT_PAYER_BALANCE' + ) { + throw rpcErrors.transactionRejected({ + message: `Insufficient funds to create DID on Hedera ${hederaNetwork}. Please ensure that the account has enough HBAR to create the DID.`, + data: { + hederaNetwork, + address, + publicKey, + accountId: accountInfo.accountId, + }, + }); + } else { + throw rpcErrors.transactionRejected({ + message: errMessage, + data: { + hederaNetwork, + address, + publicKey, + }, + }); + } + } + } + } + + if (_.isEmpty(did)) { + console.log('Failed to generate DID'); + throw new Error('Failed to generate DID'); + } + + // Get Veramo agent + const agent = await getVeramoAgent(state); + const controllerKeyId = `metamask-${address}`; + console.log( + `Importing using did=${did}, provider=${method}, controllerKeyId=${controllerKeyId}...`, + ); + + let identifier: IIdentifier; + console.log('did: ', did); + // Get identifier if it exists + try { + identifier = await agent.didManagerImport({ + did, + provider: method, + controllerKeyId, + keys: [ + { + kid: controllerKeyId, + type: curve as TKeyType, + kms: 'snap', + privateKeyHex: privateKey, + publicKeyHex: publicKey, + } as MinimalImportableKey, + ], + }); + } catch (error) { + console.log( + `Error while creating identifier: ${(error as Error).message}`, + ); + throw new Error( + `Error while creating identifier: ${(error as Error).message}`, + ); + } + + state.currentAccount.identifier = identifier; + + await SnapState.updateState(state); + + return address; + } +} diff --git a/packages/hedera-identify-snap/packages/snap/src/snap/SnapState.ts b/packages/hedera-identify-snap/packages/snap/src/snap/SnapState.ts new file mode 100644 index 00000000..e0fa8aab --- /dev/null +++ b/packages/hedera-identify-snap/packages/snap/src/snap/SnapState.ts @@ -0,0 +1,149 @@ +/*- + * + * 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 { MetaMaskInpageProvider } from '@metamask/providers'; + +import _ from 'lodash'; +import type { IdentifySnapState } from '../types/state'; +import { StateUtils } from '../utils/StateUtils'; + +export class SnapState { + /** + * Function for updating WalletSnapState object in the MetaMask state. + * @public + * @param snapState - Object to replace the current object in the MetaMask state. + */ + public static async updateState(snapState: IdentifySnapState) { + await snap.request({ + method: 'snap_manageState', + params: { + operation: 'update', + newState: JSON.parse(JSON.stringify(snapState)), + }, + }); + } + + /** + * Function to retrieve WalletSnapState object from the MetaMask state. + * @public + * @returns Object from the state. + */ + public static async getState(): Promise { + const state = (await snap.request({ + method: 'snap_manageState', + params: { operation: 'get' }, + })) as IdentifySnapState | null; + + if (_.isEmpty(state)) { + throw Error('WalletSnapState is not initialized!'); + } + + return state; + } + + /** + * Function to retrieve WalletSnapState object from the MetaMask state. + * @public + * @returns Object from the state. + */ + public static async getStateUnchecked(): Promise { + const state = (await snap.request({ + method: 'snap_manageState', + params: { operation: 'get' }, + })) as IdentifySnapState | null; + + return state; + } + + /** + * Function to initialize WalletSnapState object. + * @public + * @returns Object. + */ + public static async initState(): Promise { + const state = StateUtils.getInitialSnapState(); + await SnapState.updateState(state); + return state; + } + + /** + * Get current network. + * @param metamask - Metamask provider. + * @returns Current network. + */ + public static async getCurrentNetwork( + metamask: MetaMaskInpageProvider, + ): Promise { + return (await metamask.request({ + method: 'eth_chainId', + })) as string; + } + + /** + * Function that toggles the disablePopups flag in the config. + * @param state - WalletSnapState. + */ + public static async updatePopups(state: IdentifySnapState) { + state.snapConfig.dApp.disablePopups = !state.snapConfig.dApp.disablePopups; + await SnapState.updateState(state); + } + + /** + * Function that lets you add a friendly dApp. + * @param state - WalletSnapState. + * @param dapp - Dapp. + */ + public static async addFriendlyDapp(state: IdentifySnapState, dapp: string) { + state.snapConfig.dApp.friendlyDapps.push(dapp); + await SnapState.updateState(state); + } + + /** + * Function that removes a friendly dApp. + * @param state - WalletSnapState. + * @param dapp - Dapp. + */ + public static async removeFriendlyDapp( + state: IdentifySnapState, + dapp: string, + ) { + // FIXME: TEST IF YOU CAN REFERENCE FRIENDLY DAPS + // let friendlyDapps = state.snapConfig.dApp.friendlyDapps; + // friendlyDapps = friendlyDapps.filter((app) => app !== dapp); + state.snapConfig.dApp.friendlyDapps = + state.snapConfig.dApp.friendlyDapps.filter((app) => app !== dapp); + await SnapState.updateState(state); + } + + /** + * Function that switches the did method to use. + * + * @param snap - Snap. + * @param state - IdentitySnapState. + */ + public static async updateDIDMethod( + state: IdentifySnapState, + didMethod: string, + ) { + state.currentAccount.method = didMethod; + state.snapConfig.dApp.didMethod = didMethod; + await SnapState.updateState(state); + } +} diff --git a/packages/hedera-identify-snap/packages/snap/src/snap/account.ts b/packages/hedera-identify-snap/packages/snap/src/snap/account.ts deleted file mode 100644 index 7a639088..00000000 --- a/packages/hedera-identify-snap/packages/snap/src/snap/account.ts +++ /dev/null @@ -1,262 +0,0 @@ -/*- - * - * 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 { AccountId, PrivateKey } from '@hashgraph/sdk'; -import { MetaMaskInpageProvider } from '@metamask/providers'; -import { DialogParams, divider, heading, text } from '@metamask/snaps-sdk'; -import { Wallet, ethers } from 'ethers'; -import _ from 'lodash'; -import { validHederaChainID } from '../hedera/config'; -import { - Account, - AccountViaPrivateKey, - EvmAccountParams, - ExternalAccount, - HederaAccountParams, - IdentitySnapState, - MetamaskAccountParams, -} from '../interfaces'; -import { DEFAULTCOINTYPE, HEDERACOINTYPE } from '../types/constants'; -import { getHederaAccountIfExists } from '../utils/params'; -import { veramoImportMetaMaskAccount } from '../veramo/accountImport'; -import { generateCommonPanel, snapDialog } from './dialog'; -import { getCurrentNetwork } from './network'; -import { getCurrentCoinType, initAccountState } from './state'; - -/** - * Function that returns account info of the currently selected MetaMask account. - * - * @param state - IdentitySnapState. - * @param params - Parameters passed. - * @param isExternalAccount - Whether this is a metamask or a non-metamask account. - * @returns MetaMask address and did. - */ -export async function getCurrentAccount( - origin: string, - network: string, - state: IdentitySnapState, - params: unknown, - isExternalAccount: boolean, -): Promise { - try { - // Handle external account(non-metamask account) - if (isExternalAccount) { - const nonMetamaskAccount = params as ExternalAccount; - if (nonMetamaskAccount.externalAccount) { - if (nonMetamaskAccount.externalAccount.blockchainType === 'hedera') { - const hederaAccountId = ( - nonMetamaskAccount.externalAccount.data as HederaAccountParams - ).accountId; - if (!AccountId.fromString(hederaAccountId)) { - console.error( - `Invalid Hedera Account Id '${hederaAccountId}' is not a valid account Id`, - ); - throw new Error( - `Invalid Hedera Account Id '${hederaAccountId}' is not a valid account Id`, - ); - } - return await connectHederaAccount( - origin, - network, - state, - hederaAccountId, - ); - } else if ( - nonMetamaskAccount.externalAccount.blockchainType === 'evm' - ) { - const ethAddress = ( - nonMetamaskAccount.externalAccount.data as EvmAccountParams - ).address.toLowerCase(); - if (!ethers.isAddress(ethAddress)) { - console.error( - `Invalid EVM Account Address '${ethAddress}' is not a valid address`, - ); - throw new Error( - `Invalid EVM Account Address '${ethAddress}' is not a valid address`, - ); - } - return await connectEVMAccount(origin, network, state, ethAddress); - } - - console.error( - `Invalid blockchainType '${nonMetamaskAccount.externalAccount.blockchainType}'. The valid blockchain types are ['hedera', 'evm']`, - ); - throw new Error( - `Invalid blockchainType '${nonMetamaskAccount.externalAccount.blockchainType}'. The valid blockchain types are ['hedera', 'evm']`, - ); - } - } - - // Handle metamask account - const { metamaskAddress } = params as MetamaskAccountParams; - if (!ethers.isAddress(metamaskAddress)) { - console.error( - `Invalid Account Address '${metamaskAddress}' is not a valid address`, - ); - throw new Error( - `Invalid Account Address '${metamaskAddress}' is not a valid address`, - ); - } - - // Initialize if not there - const coinType = (await getCurrentCoinType()).toString(); - if (metamaskAddress && !(metamaskAddress in state.accountState[coinType])) { - console.log( - `The address ${metamaskAddress} has NOT yet been configured in the Identify Snap. Configuring now...`, - ); - await initAccountState(state, coinType, metamaskAddress); - } - return await veramoImportMetaMaskAccount( - network, - state, - (window as any).ethereum as MetaMaskInpageProvider, - metamaskAddress, - ); - } catch (e: any) { - console.error(`Error while trying to get the account: ${e}`); - throw new Error(`Error while trying to get the account: ${e}`); - } -} - -/** - * Connect EVM Account. - * - * @param state - Identity state. - * @param evmAddress - EVM Account address. - */ -async function connectEVMAccount( - origin: string, - network: string, - state: IdentitySnapState, - evmAddress: string, -): Promise { - let accountExists = false; - for (const address of Object.keys(state.accountState[DEFAULTCOINTYPE])) { - if (evmAddress === address) { - accountExists = true; - break; - } - } - - let privateKey: string; - if (accountExists) { - const controllerKeyId = `metamask-${evmAddress}`; - privateKey = - state.accountState[DEFAULTCOINTYPE][evmAddress].snapPrivateKeyStore[ - controllerKeyId - ].privateKeyHex; - } else { - const dialogParamsForPrivateKey: DialogParams = { - type: 'prompt', - content: await generateCommonPanel(origin, network, [ - heading('Connect to EVM Account'), - text('Enter your ECDSA private key for the following Account'), - divider(), - text(`EVM Address: ${evmAddress}`), - ]), - placeholder: '2386d1d21644dc65d...', // You can use '2386d1d21644dc65d4e4b9e2242c5f155cab174916cbc46ad85622cdaeac835c' for testing purposes - }; - privateKey = PrivateKey.fromString( - (await snapDialog(dialogParamsForPrivateKey)) as string, - ).toStringRaw(); - } - - const wallet: Wallet = new ethers.Wallet(privateKey); - const accountViaPrivateKey: AccountViaPrivateKey = { - privateKey, - publicKey: wallet.signingKey.publicKey, - address: wallet.address, - }; - - return await veramoImportMetaMaskAccount( - network, - state, - (window as any).ethereum as MetaMaskInpageProvider, - '', - accountViaPrivateKey, - ); -} - -/** - * Connect Hedera Account. - * - * @param state - Identity state. - * @param accountId - Account id. - */ -async function connectHederaAccount( - origin: string, - network: string, - state: IdentitySnapState, - accountId: string, -): Promise { - const chainId = await getCurrentNetwork(); - if (!validHederaChainID(chainId)) { - console.error( - 'Invalid Chain ID. Valid chainIDs for Hedera: [0x127: mainnet, 0x128: testnet, 0x129: previewnet]', - ); - throw new Error( - 'Non-Hedera network was selected on Metamask while trying to configure the Hedera network. Please switch the network to Hedera Network first', - ); - } - - let privateKey: string; - const evmAddress = await getHederaAccountIfExists( - state, - accountId, - undefined, - ); - if (evmAddress === null || _.isEmpty(evmAddress)) { - const dialogParamsForPrivateKey: DialogParams = { - type: 'prompt', - content: await generateCommonPanel(origin, network, [ - heading('Connect to Hedera Account'), - text('Enter your ECDSA private key for the following Account'), - divider(), - text(`Account Id: ${accountId}`), - ]), - placeholder: '2386d1d21644dc65d...', // You can use '2386d1d21644dc65d4e4b9e2242c5f155cab174916cbc46ad85622cdaeac835c' and '0.0.15215' for testing purposes - }; - privateKey = PrivateKey.fromStringECDSA( - (await snapDialog(dialogParamsForPrivateKey)) as string, - ).toStringRaw(); - } else { - const controllerKeyId = `metamask-${evmAddress}`; - privateKey = - state.accountState[HEDERACOINTYPE][evmAddress].snapPrivateKeyStore[ - controllerKeyId - ].privateKeyHex; - } - - const wallet: Wallet = new ethers.Wallet(privateKey); - const accountViaPrivateKey: AccountViaPrivateKey = { - privateKey, - publicKey: wallet.signingKey.publicKey, - address: wallet.address, - extraData: accountId, - }; - - return await veramoImportMetaMaskAccount( - network, - state, - (window as any).ethereum as MetaMaskInpageProvider, - '', - accountViaPrivateKey, - ); -} diff --git a/packages/hedera-identify-snap/packages/snap/src/snap/dapp.ts b/packages/hedera-identify-snap/packages/snap/src/snap/dapp.ts deleted file mode 100644 index 246c8e3e..00000000 --- a/packages/hedera-identify-snap/packages/snap/src/snap/dapp.ts +++ /dev/null @@ -1,78 +0,0 @@ -/*- - * - * 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 { IdentitySnapState } from '../interfaces'; -import { updateState } from './state'; - -/** - * Function that toggles the disablePopups flag in the config. - * - * @param snap - Snap. - * @param state - IdentitySnapState. - */ -export async function updatePopups(state: IdentitySnapState) { - state.snapConfig.dApp.disablePopups = !state.snapConfig.dApp.disablePopups; - await updateState(state); -} - -/** - * Function that lets you add a friendly dApp. - * - * @param snap - Snap. - * @param state - IdentitySnapState. - * @param dapp - Dapp. - */ -export async function addFriendlyDapp(state: IdentitySnapState, dapp: string) { - state.snapConfig.dApp.friendlyDapps.push(dapp); - await updateState(state); -} - -/** - * Function that removes a friendly dApp. - * - * @param snap - Snap. - * @param state - IdentitySnapState. - * @param dapp - Dapp. - */ -export async function removeFriendlyDapp( - state: IdentitySnapState, - dapp: string, -) { - // FIXME: TEST IF YOU CAN REFERENCE FRIENDLY DAPS - // let friendlyDapps = state.snapConfig.dApp.friendlyDapps; - // friendlyDapps = friendlyDapps.filter((d) => d !== dapp); - state.snapConfig.dApp.friendlyDapps = - state.snapConfig.dApp.friendlyDapps.filter((d) => d !== dapp); - await updateState(state); -} - -/** - * Function that switches the did method to use. - * - * @param snap - Snap. - * @param state - IdentitySnapState. - */ -export async function updateDIDMethod( - state: IdentitySnapState, - didMethod: string, -) { - state.snapConfig.dApp.didMethod = didMethod; - await updateState(state); -} diff --git a/packages/hedera-identify-snap/packages/snap/src/snap/dialog.ts b/packages/hedera-identify-snap/packages/snap/src/snap/dialog.ts deleted file mode 100644 index e5d21e28..00000000 --- a/packages/hedera-identify-snap/packages/snap/src/snap/dialog.ts +++ /dev/null @@ -1,225 +0,0 @@ -/*- - * - * 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 { - DialogParams, - divider, - heading, - NodeType, - panel, - Panel, - text, -} from '@metamask/snaps-sdk'; -import { VerifiableCredential } from '@veramo/core'; -import _ from 'lodash'; -import cloneDeep from 'lodash.clonedeep'; -import { - getHederaNetwork, - getOtherNetwork, - validHederaChainID, -} from '../hedera/config'; -import { IDataManagerQueryResult } from '../plugins/veramo/verifiable-creds-manager'; - -export function initializePanelToShow(): any { - const panelToShow: ( - | { - value: string; - type: NodeType.Heading; - } - | { - value: string; - type: NodeType.Text; - markdown?: boolean | undefined; - } - | { - type: NodeType.Divider; - } - | { - value: string; - type: NodeType.Copyable; - sensitive?: boolean | undefined; - } - )[] = []; - return panelToShow; -} - -/** - * Function that opens snap dialog. - * - * @param snap - Snap. - * @param params - Snap dialog params. - */ -export async function snapDialog( - params: DialogParams, -): Promise { - return (await snap.request({ - method: 'snap_dialog', - params, - })) as boolean; -} - -/** - * Function to generate snap dialog panel. - * @param origin - The origin of where the call is being made from. - * @param network - The network the call is being made on. - * @param mirrorNodeUrl - The mirror node url. - * @param prompt - Prompt text of the metamask dialog box(eg. 'Are you sure you want to send VCs to the dApp?'). - * @returns Panel to be displayed in the snap dialog. - */ -export async function generateCommonPanel( - origin: string, - network: string, - prompt: any[], -): Promise { - let networkToShow = network; - if (validHederaChainID(network)) { - networkToShow = `Hedera - ${getHederaNetwork(network)}`; - } else { - networkToShow = getOtherNetwork(network); - } - const panelToShow = [ - text(`Origin: **${origin}**`), - text(`Network: **${networkToShow}**`), - divider(), - ...prompt, - ]; - return panel(panelToShow); -} - -/** - * Function to generate snap dialog panel for VC related functions. - * - * @param origin - The origin of where the call is being made from. - * @param header - Header text of the metamask dialog box(eg. 'Retrieve Verifiable Credentials'). - * @param prompt - Prompt text of the metamask dialog box(eg. 'Are you sure you want to send VCs to the dApp?'). - * @param description - Description text of the metamask dialog box(eg. 'Some dApps are less secure than others and could save data from VCs against your will. Be careful where you send your private VCs! Number of VCs submitted is 2'). - * @param vcs - The Verifiable Credentials to show on the metamask dialog box. - */ -export async function generateVCPanel( - origin: string, - network: string, - header: string, - prompt: string, - description: string, - vcs: IDataManagerQueryResult[], -): Promise { - let networkToShow = network; - if (validHederaChainID(network)) { - networkToShow = `Hedera - ${getHederaNetwork(network)}`; - } else { - networkToShow = getOtherNetwork(network); - } - const vcsToUse = cloneDeep(vcs); - const panelToShow = [ - text(`Origin: ${origin}`), - text(`Network: **${networkToShow}**`), - divider(), - heading(header), - text(prompt), - divider(), - text(description), - ]; - vcsToUse.forEach((vc, index) => { - const vcData = vc.data as VerifiableCredential; - delete vcData.credentialSubject.id; - delete vcData.credentialSubject.hederaAccountId; - panelToShow.push(divider()); - - const credentialNumber = (index + 1).toString(); - panelToShow.push(text(`Credential #${credentialNumber}`)); - panelToShow.push(divider()); - - panelToShow.push(text('ID: ')); - panelToShow.push( - text(_.isEmpty(vc.metadata.id) ? credentialNumber : vc.metadata.id), - ); - - panelToShow.push(text('STORAGE: ')); - panelToShow.push(text(vc.metadata.store as string)); - - panelToShow.push(text('TYPE:')); - panelToShow.push(text(JSON.stringify(vcData.type))); - - panelToShow.push(text('SUBJECT:')); - panelToShow.push(text(JSON.stringify(vcData.credentialSubject))); - - panelToShow.push(text('ISSUANCE DATE:')); - const issuanceDate = new Date(vcData.issuanceDate as string).toLocaleString( - undefined, - { - year: 'numeric', - month: '2-digit', - day: '2-digit', - weekday: 'long', - hour: '2-digit', - hour12: false, - minute: '2-digit', - second: '2-digit', - }, - ); - panelToShow.push(text(issuanceDate)); - - panelToShow.push(text('EXPIRATION DATE:')); - let expirationDate = 'Does not expire'; - if (vcData.expirationDate) { - expirationDate = new Date(vcData.expirationDate as string).toLocaleString( - undefined, - { - year: 'numeric', - month: '2-digit', - day: '2-digit', - weekday: 'long', - hour: '2-digit', - hour12: false, - minute: '2-digit', - second: '2-digit', - }, - ); - } - panelToShow.push(text(expirationDate)); - }); - return panel(panelToShow); -} - -/** - * Request Hedera Account Id. - * - * @param snap - SnapGlobalObject. - * @param prevHederaAccountId - HederaIdentifier. - */ -export async function requestHederaAccountId( - origin: string, - network: string, - prevHederaAccountId?: string, -): Promise { - const dialogParamsForHederaAccountId: DialogParams = { - type: 'prompt', - content: await generateCommonPanel(origin, network, [ - heading('Connect to Hedera Account'), - prevHederaAccountId - ? text( - `You had previously set your account Id to be ${prevHederaAccountId} however, this is not the correct account Id associated with this account. Please re-enter your account Id`, - ) - : text(`Enter your hedera account Id associated with this account`), - ]), - placeholder: '0.0.3658062', - }; - return (await snapDialog(dialogParamsForHederaAccountId)) as string; -} diff --git a/packages/hedera-identify-snap/packages/snap/src/snap/network.ts b/packages/hedera-identify-snap/packages/snap/src/snap/network.ts deleted file mode 100644 index ec924bb2..00000000 --- a/packages/hedera-identify-snap/packages/snap/src/snap/network.ts +++ /dev/null @@ -1,33 +0,0 @@ -/*- - * - * 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 { MetaMaskInpageProvider } from '@metamask/providers'; - -/** - * Get current network. - * @param metamask - Metamask provider. - * @returns Current network. - */ -export async function getCurrentNetwork(): Promise { - const metamask = (window as any).ethereum as MetaMaskInpageProvider; - return (await metamask.request({ - method: 'eth_chainId', - })) as string; -} diff --git a/packages/hedera-identify-snap/packages/snap/src/snap/state.ts b/packages/hedera-identify-snap/packages/snap/src/snap/state.ts deleted file mode 100644 index 6d96d1e0..00000000 --- a/packages/hedera-identify-snap/packages/snap/src/snap/state.ts +++ /dev/null @@ -1,142 +0,0 @@ -/*- - * - * 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 { validHederaChainID } from '../hedera/config'; -import { - Account, - IdentitySnapState as IdentifySnapState, - IdentityAccountState, -} from '../interfaces'; -import { DEFAULTCOINTYPE, HEDERACOINTYPE } from '../types/constants'; -import { getEmptyAccountState, getInitialSnapState } from '../utils/config'; -import { getCurrentNetwork } from './network'; - -/** - * Function for updating IdentitySnapState object in the MetaMask state. - * - * @public - * @param snap - Snap. - * @param snapState - Object to replace the current object in the MetaMask state. - */ -export async function updateState(snapState: IdentifySnapState) { - await snap.request({ - method: 'snap_manageState', - params: { - operation: 'update', - newState: JSON.parse(JSON.stringify(snapState)), - }, - }); -} - -/** - * Function to retrieve IdentitySnapState object from the MetaMask state. - * - * @param snap - Snap. - * @public - * @returns Object from the state. - */ -export async function getState(): Promise { - const state = (await snap.request({ - method: 'snap_manageState', - params: { operation: 'get' }, - })) as IdentifySnapState | null; - - if (!state) { - throw Error('IdentifySnapState is not initialized!'); - } - - return state; -} - -/** - * Function to retrieve IdentifySnapState object from the MetaMask state. - * - * @param snap - Snap. - * @public - * @returns Object from the state. - */ -export async function getStateUnchecked(): Promise { - const state = (await snap.request({ - method: 'snap_manageState', - params: { operation: 'get' }, - })) as IdentifySnapState | null; - - return state; -} - -/** - * Function to initialize IdentitySnapState object. - * - * @param snap - Snap. - * @public - * @returns Object. - */ -export async function initState(): Promise { - const state = getInitialSnapState(); - await updateState(state); - return state; -} - -/** - * Function that creates an empty IdentitySnapState object in the Identity Snap state for the provided address. - * - * @param snap - Snap. - * @param state - IdentitySnapState. - * @param coinType - The type of cointype. - * @param evmAddress - The account address. - */ -export async function initAccountState( - state: IdentifySnapState, - coinType: string, - evmAddress: string, -): Promise { - state.currentAccount = { metamaskAddress: evmAddress } as Account; - state.accountState[coinType][evmAddress] = getEmptyAccountState(); - await updateState(state); -} - -/** - * Function that returns the current coin type based on what network is selected. - * - * @returns Result. - */ -export async function getCurrentCoinType(): Promise { - const chainId = await getCurrentNetwork(); - let coinType = DEFAULTCOINTYPE; - if (validHederaChainID(chainId)) { - coinType = HEDERACOINTYPE; - } - return coinType; -} - -/** - * Function that get account state according to coin type. - * - * @param state - IdentitySnapState. - * @param evmAddress - The account address. - * @returns Result. - */ -export async function getAccountStateByCoinType( - state: IdentifySnapState, - evmAddress: string, -): Promise { - const coinType = await getCurrentCoinType(); - return state.accountState[coinType][evmAddress]; -} diff --git a/packages/hedera-identify-snap/packages/snap/src/types/account.ts b/packages/hedera-identify-snap/packages/snap/src/types/account.ts new file mode 100644 index 00000000..74f70798 --- /dev/null +++ b/packages/hedera-identify-snap/packages/snap/src/types/account.ts @@ -0,0 +1,62 @@ +/*- + * + * 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 { IIdentifier } from '@veramo/core'; + +export type ExternalAccount = { + externalAccount: { + accountIdOrEvmAddress: string; + curve?: string; + }; +}; + +export type Account = { + metamaskEvmAddress: string; + externalEvmAddress: string; + method: string; + identifier: IIdentifier; + hederaAccountId: string; + snapEvmAddress: string; + privateKey: string; + publicKey: string; + network: string; +}; + +export type PublicAccountInfo = { + metamaskAddress: string; + snapAddress: string; + snapPublicKey: string; + did: string; + method: string; + hederaAccountId: string; +}; + +export type HederaAccountInfo = { + accountId: string; + evmAddress: string; + key: { + type: string; + key: string; + }; +}; + +export type NetworkParams = { + network: string; +}; diff --git a/packages/hedera-identify-snap/packages/snap/src/types/constants.ts b/packages/hedera-identify-snap/packages/snap/src/types/constants.ts index 88bfeddd..6e47113d 100644 --- a/packages/hedera-identify-snap/packages/snap/src/types/constants.ts +++ b/packages/hedera-identify-snap/packages/snap/src/types/constants.ts @@ -30,13 +30,15 @@ export const isValidVCStore = (x: string) => isIn(availableVCStores, x); export const availableMethods = ['did:pkh', 'did:key', 'did:hedera'] as const; export const isValidMethod = (x: string) => isIn(availableMethods, x); -// 60 for ethereum and 3030 for hedera +export const availableProofFormats = ['jwt' as ProofFormat] as const; +export const isValidProofFormat = (x: string) => isIn(availableProofFormats, x); + export const DEFAULTCOINTYPE = 60; -export const HEDERACOINTYPE = 3030; -export const availableProofFormats = [ - 'jwt' as ProofFormat, - 'lds' as ProofFormat, - 'EthereumEip712Signature2021' as ProofFormat, -] as const; -export const isValidProofFormat = (x: string) => isIn(availableProofFormats, x); +export const EMPTY_STRING = ''; +export const FEE_DISPLAY_REGEX = /(\.\d*?[1-9])0+$|\.0*$/u; +export const FEE_DIGIT_LENGTH = 8; +export const HBAR_ASSET_STRING = 'HBAR'; +export const NFT_ASSET_STRING = 'NFT'; + +export const MAX_RETRIES = 3; diff --git a/packages/hedera-identify-snap/packages/snap/src/types/hedera.ts b/packages/hedera-identify-snap/packages/snap/src/types/hedera.ts new file mode 100644 index 00000000..a5333ed4 --- /dev/null +++ b/packages/hedera-identify-snap/packages/snap/src/types/hedera.ts @@ -0,0 +1,84 @@ +/*- + * + * 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 { AccountId, Client, PrivateKey, PublicKey } from '@hashgraph/sdk'; +import type { Long } from '@hashgraph/sdk/lib/long'; + +import type { HederaAccountInfo } from './account'; + +export type HederaNetworkInfo = { + hederaNetwork: string; + mirrorNodeUrl: string; +}; + +export type HederaService = { + getMirrorAccountInfo( + idOrAliasOrEvmAddress: string, + ): Promise; +}; + +export type SimpleHederaClient = { + // close the client + close(): void; + + // get the associated client + getClient(): Client; + + // get the associated private key, if available + getPrivateKey(): PrivateKey | null; + + // get the associated public key + getPublicKey(): PublicKey; + + // get the associated account ID + getAccountId(): AccountId; +}; + +export type MirrorAccountInfo = { + account: string; + alias: string; + auto_renew_period: Long; + balance: { + balance: number; + timestamp: string; + tokens: []; + }; + created_timestamp: string; + decline_reward: boolean; + deleted: boolean; + ethereum_nonce: Long; + evm_address: string; + expiry_timestamp: string; + key: { + _type: string; + key: string; + }; + max_automatic_token_associations: Long; + memo: string; + pending_reward: Long; + receiver_sig_required: boolean; + staked_account_id?: string; + staked_node_id?: number; + stake_period_start?: string; + transactions: []; + links: { + next: string; + }; +}; diff --git a/packages/hedera-identify-snap/packages/snap/src/types/params.ts b/packages/hedera-identify-snap/packages/snap/src/types/params.ts index 0602820f..fd1abbea 100644 --- a/packages/hedera-identify-snap/packages/snap/src/types/params.ts +++ b/packages/hedera-identify-snap/packages/snap/src/types/params.ts @@ -18,12 +18,36 @@ * */ -import { ProofFormat, W3CVerifiableCredential } from '@veramo/core'; +import { + ProofFormat, + VerifiablePresentation, + W3CVerifiableCredential, +} from '@veramo/core'; import { QueryMetadata, SaveOptions, } from '../plugins/veramo/verifiable-creds-manager'; +export type MirrorNodeParams = { mirrorNodeUrl?: string }; + +export type NetworkParams = { + network: string; +}; + +export type ResolveDIDRequestParams = { did?: string }; + +export type SwitchMethodRequestParams = { + didMethod: string; +}; + +export type VerifyVCRequestParams = { + verifiableCredential: W3CVerifiableCredential; +}; + +export type VerifyVPRequestParams = { + verifiablePresentation: VerifiablePresentation; +}; + export type CreateVCRequestParams = { vcValue: object; vcKey?: string; @@ -54,3 +78,12 @@ export type CreateVPRequestParams = { options?: CreateVPOptions; proofInfo?: ProofInfo; }; + +export type GoogleToken = { + accessToken: string; +}; + +export type UploadData = { + fileName: string; + content: string; +}; diff --git a/packages/hedera-identify-snap/packages/snap/src/interfaces.ts b/packages/hedera-identify-snap/packages/snap/src/types/state.ts similarity index 51% rename from packages/hedera-identify-snap/packages/snap/src/interfaces.ts rename to packages/hedera-identify-snap/packages/snap/src/types/state.ts index 49016f81..107a0d2c 100644 --- a/packages/hedera-identify-snap/packages/snap/src/interfaces.ts +++ b/packages/hedera-identify-snap/packages/snap/src/types/state.ts @@ -20,41 +20,24 @@ import { IIdentifier, IKey, W3CVerifiableCredential } from '@veramo/core'; import { ManagedPrivateKey } from '@veramo/key-manager'; +import type { Account, HederaAccountInfo } from './account'; -export type Account = { - metamaskAddress: string; - snapAddress: string; - method: string; - identifier: IIdentifier; - privateKey: string; - publicKey: string; - extraData?: unknown; -}; - -export type PublicAccountInfo = { - metamaskAddress: string; - snapAddress: string; - did: string; - method: string; - accountID: string; -}; - -export type IdentitySnapState = { +export type IdentifySnapState = { currentAccount: Account; /** * Account specific storage - * mapping(coinType -> mapping(address -> state)) + * mapping(evm address -> mapping(network -> state)) */ - accountState: Record>; + accountState: Record>; /** - * Configuration for IdentitySnap + * Configuration for IdentifySnap */ - snapConfig: IdentitySnapConfig; + snapConfig: IdentifySnapConfig; }; -export type IdentitySnapConfig = { +export type IdentifySnapConfig = { snap: { acceptedTerms: boolean; }; @@ -65,70 +48,42 @@ export type IdentitySnapConfig = { }; }; +export type KeyStore = { + curve: string; + privateKey: string; + publicKey: string; + address: string; + hederaAccountId: string; +}; + /** - * Identity Snap State for a MetaMask address + * Identify Snap State for a MetaMask address */ -export type IdentityAccountState = { +export type IdentifyAccountState = { + keyStore: KeyStore; + accountInfo: HederaAccountInfo; + snapKeyStore: Record; snapPrivateKeyStore: Record; identifiers: Record; vcs: Record; - accountConfig: IdentityAccountConfig; - index?: number; - extraData?: unknown; + accountConfig: IdentifyAccountConfig; }; -export type GoogleUserInfo = { - accessToken: string; - email: string; -}; - -export type IdentityAccountConfig = { +export type IdentifyAccountConfig = { identity: { vcStore: string; googleUserInfo: GoogleUserInfo; }; }; -export type IdentitySnapParams = { - origin: string; - network: string; - state: IdentitySnapState; - account: Account; -}; - -export type UploadData = { - fileName: string; - content: string; -}; - -export type GoogleToken = { +export type GoogleUserInfo = { accessToken: string; + email: string; }; -export type AccountViaPrivateKey = { - privateKey: string; - publicKey: string; - address: string; - extraData?: unknown; -}; - -export type ExternalAccount = { - externalAccount: { - blockchainType: string; - data: unknown; - }; -}; - -export type HederaAccountParams = { - accountId: string; -}; - -export type EvmAccountParams = { - address: string; -}; - -export type MetamaskAccountParams = { - metamaskAddress: string; +export type IdentifySnapParams = { + origin: string; + state: IdentifySnapState; }; diff --git a/packages/hedera-identify-snap/packages/snap/src/utils/CryptoUtils.ts b/packages/hedera-identify-snap/packages/snap/src/utils/CryptoUtils.ts new file mode 100644 index 00000000..62eef3b9 --- /dev/null +++ b/packages/hedera-identify-snap/packages/snap/src/utils/CryptoUtils.ts @@ -0,0 +1,249 @@ +/*- + * + * 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 { Key } from '@hashgraph/sdk'; +import { PublicKey } from '@hashgraph/sdk'; +import type { HashgraphProto } from '@hashgraph/sdk/lib/Key'; +import { HDNodeWallet, Mnemonic, assertArgument, ethers } from 'ethers'; +import { publicKeyConvert } from 'secp256k1'; +import { DEFAULTCOINTYPE } from '../types/constants'; + +export class CryptoUtils { + /** + * Derives a wallet from the provided node using the provided path. + * NOTE: This method is a copy of the 'derivePath' method from the 'ethers' library as that method + * changed in the recent version and the new method does not work as expected. + * @param node - The node to derive the wallet from. + * @param path - The path to use for derivation. + * @returns The derived HDNodeWallet. + */ + // eslint-disable-next-line no-restricted-syntax + private static derivePathForWallet( + node: HDNodeWallet, + path: string, + ): HDNodeWallet { + const components = path.split('/'); + + assertArgument( + components.length > 0 && (components[0] === 'm' || node.depth > 0), + 'invalid path', + 'path', + path, + ); + + if (components[0] === 'm') { + components.shift(); + } + + let result: HDNodeWallet = node; + + const HardenedBit = 0x80000000; + for (let i = 0; i < components.length; i++) { + const component = components[i]; + + if (component.match(/^[0-9]+'$/u)) { + const index = parseInt( + component.substring(0, component.length - 1), + 10, + ); + + assertArgument( + index < HardenedBit, + 'invalid path index', + `path[${i}]`, + component, + ); + result = result.deriveChild(HardenedBit + index); + } else if (component.match(/^[0-9]+$/u)) { + const index = parseInt(component, 10); + assertArgument( + index < HardenedBit, + 'invalid path index', + `path[${i}]`, + component, + ); + result = result.deriveChild(index); + } else { + assertArgument( + false, + 'invalid path component', + `path[${i}]`, + component, + ); + } + } + + return result; + } + + /** + * Generates a wallet using the provided EVM address to generate entropy. + * @param evmAddress - The EVM address used as salt for entropy. + * @returns A promise that resolves to an HDNodeWallet. + */ + public static async generateWallet( + evmAddress: string, + ): Promise { + const entropy = await snap.request({ + method: 'snap_getEntropy', + params: { + version: 1, + salt: evmAddress, + }, + }); + + let nodeWallet = HDNodeWallet.fromMnemonic(Mnemonic.fromEntropy(entropy)); + nodeWallet = CryptoUtils.derivePathForWallet( + nodeWallet, + `m/44'/${DEFAULTCOINTYPE}'/0'/0/0`, + ); + + return nodeWallet; + } + + public static getCompressedPublicKey(publicKey: string): string { + const hexToUIntArray = new Uint8Array( + Buffer.from(publicKey.split('0x')[1], 'hex'), + ); + const pKeyConvert = publicKeyConvert(hexToUIntArray, true); + return Buffer.from(pKeyConvert).toString('hex'); + } + + /** + * Checks whether the provided key is a valid Ethereum public key. + * @param key - The public key to check. + * @returns True if the key is valid, false otherwise. + */ + public static isValidEthereumPublicKey(key: string): boolean { + let publicKey: string = key; + // Check if the key has the '0x' prefix + if (!publicKey.startsWith('0x')) { + publicKey = `0x${publicKey}`; + } + + // Check if the key is a compressed (66 characters) or uncompressed (130 characters) public key + return ( + (publicKey.length === 68 || publicKey.length === 130) && + ethers.isHexString(publicKey) + ); + } + + /** + * Checks whether the provided key is a valid Hedera public key. + * @param key - The public key to check. + * @returns True if the key is valid, false otherwise. + */ + public static isValidHederaPublicKey(key: string): boolean { + try { + PublicKey.fromString(key); + } catch (error: any) { + return false; + } + return true; + } + + /** + * Converts a string to a Uint8Array. + * @param data - The string to convert. + * @returns The converted Uint8Array. + */ + public static stringToUint8Array(data: string): Uint8Array { + const encoder = new TextEncoder(); // The TextEncoder encodes into UTF-8 by default + const uint8Array = encoder.encode(data); + return uint8Array; + } + + /** + * Converts a Uint8Array to a hexadecimal string. + * @param data - The Uint8Array to convert. + * @returns The hexadecimal string. + */ + public static uint8ArrayToHex(data: Uint8Array | null | undefined): string { + if (!data) { + return ''; + } + return data.reduce( + (str, byte) => str + byte.toString(16).padStart(2, '0'), + '', + ); + } + + /** + * Converts a hexadecimal string to a Uint8Array. + * @param data - The hexadecimal string to convert. + * @returns The Uint8Array. + */ + public static hexToUInt8Array(data: string): Uint8Array { + let hexString = data; + // Remove the '0x' prefix if it exists + if (hexString.startsWith('0x')) { + hexString = hexString.slice(2); + } + + // Ensure the hex string has an even length + if (hexString.length % 2 !== 0) { + throw new Error('Invalid hex string'); + } + + // Convert the hex string to a byte array + const byteArray = new Uint8Array(hexString.length / 2); + for (let i = 0; i < byteArray.length; i++) { + byteArray[i] = parseInt(hexString.substr(i * 2, 2), 16); + } + + return byteArray; + } + + /** + * Convert a key to a string representation. + * @param key - The key to convert. + * @returns The string representation of the key. + */ + public static keyToString(key: Key | null): string { + if (!key) { + return ''; + } + + // Convert the key to a protobuf key + const protobufKey: HashgraphProto.proto.IKey = key._toProtobufKey(); + + // Extract the key value from the protobuf key + if (protobufKey.ed25519) { + return CryptoUtils.uint8ArrayToHex(protobufKey.ed25519); + } else if (protobufKey.ECDSASecp256k1) { + return CryptoUtils.uint8ArrayToHex(protobufKey.ECDSASecp256k1); + } else if (protobufKey.RSA_3072) { + return CryptoUtils.uint8ArrayToHex(protobufKey.RSA_3072); + } else if (protobufKey.ECDSA_384) { + return CryptoUtils.uint8ArrayToHex(protobufKey.ECDSA_384); + } else if (protobufKey.contractID) { + return protobufKey.contractID.contractNum + ? protobufKey.contractID.contractNum.toString() + : ''; + } + // Handle other types of keys if necessary + return JSON.stringify(protobufKey, (_key, value) => { + if (value instanceof Uint8Array) { + return CryptoUtils.uint8ArrayToHex(value); // Convert Uint8Array to hex string + } + return value; + }); + } +} diff --git a/packages/hedera-identify-snap/packages/snap/src/utils/EvmUtils.ts b/packages/hedera-identify-snap/packages/snap/src/utils/EvmUtils.ts new file mode 100644 index 00000000..4935c89c --- /dev/null +++ b/packages/hedera-identify-snap/packages/snap/src/utils/EvmUtils.ts @@ -0,0 +1,48 @@ +/*- + * + * 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. + * + */ + +export class EvmUtils { + /** + * Adds the prefix to the EVM address. + * @param address - EVM Account address. + * @returns EVM address. + */ + public static ensure0xPrefix(address: string): string { + let result = address; + if (!address.startsWith('0x')) { + result = `0x${address}`; + } + return result.toLowerCase(); + } + + /** + * Get current chainId. + * @returns Current chainId. + */ + public static async getChainId(): Promise { + return (await ethereum.request({ + method: 'eth_chainId', + })) as string; + } + + public static convertChainIdFromHex = (chainId: string): string => { + return parseInt(chainId, 16).toString(); + }; +} diff --git a/packages/hedera-identify-snap/packages/snap/src/utils/FetchUtils.ts b/packages/hedera-identify-snap/packages/snap/src/utils/FetchUtils.ts new file mode 100644 index 00000000..fedc6eb4 --- /dev/null +++ b/packages/hedera-identify-snap/packages/snap/src/utils/FetchUtils.ts @@ -0,0 +1,56 @@ +/*- + * + * 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. + * + */ + +export type FetchResponse = { + success: boolean; + data: any; + error: string | undefined; +}; + +/** + * Provides utilities for interacting with Hedera Identify Snap functionalities. + */ +export class FetchUtils { + /** + * Retrieve results using hedera mirror node. + * @param url - The URL to use to query. + * @returns A promise that resolves to the fetch response. + */ + public static async fetchDataFromUrl( + url: RequestInfo | URL, + ): Promise { + let data; + let error; + + const response = await fetch(url); + + if (response.ok) { + data = await response.json(); + } else { + error = `Network response was not ok. Status: ${response.status} ${response.statusText}`; + } + + return { + success: response.ok, + data, + error, + }; + } +} diff --git a/packages/hedera-identify-snap/packages/snap/src/utils/HederaUtils.ts b/packages/hedera-identify-snap/packages/snap/src/utils/HederaUtils.ts new file mode 100644 index 00000000..51cec197 --- /dev/null +++ b/packages/hedera-identify-snap/packages/snap/src/utils/HederaUtils.ts @@ -0,0 +1,80 @@ +/*- + * + * 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 { + DEFAULT_HEDERA_MIRRORNODES, + HEDERA_CHAIN_IDS, + HEDERA_NETWORKS, +} from '../constants/network'; +import type { HederaAccountInfo } from '../types/account'; +import { isIn } from '../types/constants'; +import type { HederaNetworkInfo, MirrorAccountInfo } from '../types/hedera'; +import { FetchUtils, type FetchResponse } from './FetchUtils'; + +export class HederaUtils { + /** + * Check Validation of network flag and mirrorNodeUrl flag and return their values. + * @param params - Request params. + * @returns Network and MirrorNodeUrl. + */ + public static getHederaNetworkInfo(chainId: string): HederaNetworkInfo { + const networkInfo = { + hederaNetwork: 'mainnet', + mirrorNodeUrl: DEFAULT_HEDERA_MIRRORNODES.mainnet, + } as HederaNetworkInfo; + + networkInfo.hederaNetwork = HEDERA_CHAIN_IDS[chainId] || 'mainnet'; + networkInfo.mirrorNodeUrl = + DEFAULT_HEDERA_MIRRORNODES[networkInfo.hederaNetwork]; + + return networkInfo; + } + + public static validHederaNetwork(network: string) { + return isIn(HEDERA_NETWORKS, network); + } + + public static async getMirrorAccountInfo( + idOrAliasOrEvmAddress: string, + mirrorNodeUrl: string, + ): Promise { + const result = {} as HederaAccountInfo; + const url = `${mirrorNodeUrl}/api/v1/accounts/${encodeURIComponent(idOrAliasOrEvmAddress)}`; + const response: FetchResponse = await FetchUtils.fetchDataFromUrl(url); + if (!response.success) { + return result; + } + + const mirrorNodeData = response.data as MirrorAccountInfo; + + try { + result.accountId = mirrorNodeData.account; + result.evmAddress = mirrorNodeData.evm_address; + result.key = { + type: mirrorNodeData?.key?._type ?? '', + key: mirrorNodeData?.key?.key ?? '', + }; + } catch (error: any) { + console.error('Error in getMirrorAccountInfo:', String(error)); + } + + return result; + } +} diff --git a/packages/hedera-identify-snap/packages/snap/src/utils/ParamUtils.ts b/packages/hedera-identify-snap/packages/snap/src/utils/ParamUtils.ts new file mode 100644 index 00000000..403f1ec4 --- /dev/null +++ b/packages/hedera-identify-snap/packages/snap/src/utils/ParamUtils.ts @@ -0,0 +1,1015 @@ +/*- + * + * 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 { AccountId } from '@hashgraph/sdk'; +import { rpcErrors } from '@metamask/rpc-errors'; +import { ethers } from 'ethers'; +import _ from 'lodash'; +import { ECDSA_SECP256K1_KEY_TYPE, ED25519_KEY_TYPE } from '../constants'; +import { + IDataManagerClearArgs, + IDataManagerDeleteArgs, + IDataManagerQueryArgs, + IDataManagerSaveArgs, +} from '../plugins/veramo/verifiable-creds-manager'; +import { ExternalAccount } from '../types/account'; +import { + availableProofFormats, + availableVCStores, + isValidProofFormat, + isValidVCStore, +} from '../types/constants'; +import { + CreateVCRequestParams, + CreateVPRequestParams, + GoogleToken, + ResolveDIDRequestParams, + SwitchMethodRequestParams, + VerifyVCRequestParams, + VerifyVPRequestParams, +} from '../types/params'; +import { CryptoUtils } from './CryptoUtils'; + +export class ParamUtils { + /** + * Checks if the specified property in the given object is passed. + * @param parameter - The object containing the property to check. + * @param methodName - The method name. + * @param propertyName - The name of the property to validate. + * @param isRequired - Whether to check if this property is required to be present. + */ + // eslint-disable-next-line no-restricted-syntax + static checkRequiredProperty( + parameter: any, + methodName: string, + propertyName: string, + isRequired: boolean, + ) { + // Check if the property exists if isRequired is true + if (isRequired && !(propertyName in parameter)) { + console.error( + `Invalid ${methodName} Params passed. "${propertyName}" must be passed`, + ); + throw rpcErrors.invalidParams( + `Invalid ${methodName} Params passed. "${propertyName}" must be passed`, + ); + } + } + + /** + * Checks if the specified property in the given object is a valid string. + * @param parameter - The object containing the property to check. + * @param methodName - The method name. + * @param propertyName - The name of the property to validate. + * @param isRequired - Whether to check if this property is required to be present. + */ + // eslint-disable-next-line no-restricted-syntax + static checkValidString( + parameter: any, + methodName: string, + propertyName: string, + isRequired: boolean, + ) { + // Check if the property exists if isRequired is true + this.checkRequiredProperty(parameter, methodName, propertyName, isRequired); + // Check if the property exists and is a valid string + if ( + propertyName in parameter && + (typeof parameter[propertyName] !== 'string' || + _.isEmpty(parameter[propertyName])) + ) { + console.error( + `Invalid ${methodName} Params passed. "${propertyName}" must be a string`, + ); + throw rpcErrors.invalidParams( + `Invalid ${methodName} Params passed. "${propertyName}" must be a string`, + ); + } + } + + /** + * Checks if the specified property in the given object is a valid Hedera AccountId. + * @param parameter - The object containing the property to check. + * @param methodName - The method name. + * @param propertyName - The name of the property to validate. + * @param isRequired - Whether to check if this property is required to be present. + */ + // eslint-disable-next-line no-restricted-syntax + static checkValidAccountId( + parameter: any, + methodName: string, + propertyName: string, + isRequired: boolean, + ) { + // Check if the property exists if isRequired is true + this.checkRequiredProperty(parameter, methodName, propertyName, isRequired); + // Check if the property exists and is a valid Hedera AccountId + if ( + propertyName in parameter && + (typeof parameter[propertyName] !== 'string' || + _.isEmpty(parameter[propertyName]) || + !AccountId.fromString(parameter[propertyName])) + ) { + console.error( + `Invalid ${methodName} Params passed. "${propertyName}" must be a string and a valid Hedera Account Id`, + ); + throw rpcErrors.invalidParams( + `Invalid ${methodName} Params passed. "${propertyName}" must be a string and a valid Hedera Account Id`, + ); + } + } + + /** + * Checks if the specified property in the given object is a valid boolean. + * @param parameter - The object containing the property to check. + * @param methodName - The method name. + * @param propertyName - The name of the property to validate. + * @param isRequired - Whether to check if this property is required to be present. + */ + // eslint-disable-next-line no-restricted-syntax + static checkValidBoolean( + parameter: any, + methodName: string, + propertyName: string, + isRequired: boolean, + ) { + // Check if the property exists if isRequired is true + this.checkRequiredProperty(parameter, methodName, propertyName, isRequired); + // Check if the property exists and is a valid boolean + if ( + propertyName in parameter && + typeof parameter[propertyName] !== 'boolean' + ) { + console.error( + `Invalid ${methodName} Params passed. "${propertyName}" must be a boolean`, + ); + throw rpcErrors.invalidParams( + `Invalid ${methodName} Params passed. "${propertyName}" must be a boolean`, + ); + } + } + + /** + * Checks if the specified property in the given object is a valid number. + * @param parameter - The object containing the property to check. + * @param methodName - The method name. + * @param propertyName - The name of the property to validate. + * @param isRequired - Whether to check if this property is required to be present. + */ + // eslint-disable-next-line no-restricted-syntax + static checkValidNumber( + parameter: any, + methodName: string, + propertyName: string, + isRequired: boolean, + ) { + // Check if the property exists if isRequired is true + this.checkRequiredProperty(parameter, methodName, propertyName, isRequired); + // Check if the property exists and is a valid number + if ( + propertyName in parameter && + (typeof parameter[propertyName] !== 'number' || + !Number.isFinite(parameter[propertyName]) || + parameter[propertyName] < 0) + ) { + console.error( + `Invalid ${methodName} Params passed. "${propertyName}" must be a number`, + ); + throw rpcErrors.invalidParams( + `Invalid ${methodName} Params passed. "${propertyName}" must be a number`, + ); + } + } + + /** + * Checks if the specified property in the given object is a valid timestamp. + * @param parameter - The object containing the property to check. + * @param methodName - The method name. + * @param propertyName - The name of the property to validate. + * @param isRequired - Whether to check if this property is required to be present. + */ + // eslint-disable-next-line no-restricted-syntax + static checkValidTimestamp( + parameter: any, + methodName: string, + propertyName: string, + isRequired: boolean, + ) { + // Check if the property exists if isRequired is true + this.checkRequiredProperty(parameter, methodName, propertyName, isRequired); + // Check if the property exists and is a valid timestamp + + // Regular expression for validating date in YYYY-MM-DD format + // and date-time in YYYY-MM-DDTHH:mm:ss format + const dateTimeRegex = + /^\d{4}-[01]\d-[0-3]\d(?:T([01]?[0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9])?$/u; + + if ( + propertyName in parameter && + (!dateTimeRegex.test(parameter[propertyName]) || + typeof parameter[propertyName] !== 'string' || + _.isEmpty(parameter[propertyName])) + ) { + console.error( + `Invalid ${methodName} Params passed. "${propertyName}" must be a valid date string in the format YYYY-MM-DD or date-time string in the format YYYY-MM-DDTHH:mm:ss`, + ); + throw rpcErrors.invalidParams( + `Invalid ${methodName} Params passed. "${propertyName}" must be a valid date string in the format YYYY-MM-DD or date-time string in the format YYYY-MM-DDTHH:mm:ss`, + ); + } + } + + /** + * Checks if the specified property in the given object is a valid public key. + * @param parameter - The object containing the property to check. + * @param methodName - The method name. + * @param propertyName - The name of the property to validate. + * @param isRequired - Whether to check if this property is required to be present. + */ + // eslint-disable-next-line no-restricted-syntax + static checkValidPublicKey( + parameter: any, + methodName: string, + propertyName: string, + isRequired: boolean, + ) { + // Check if the property exists if isRequired is true + this.checkRequiredProperty(parameter, methodName, propertyName, isRequired); + // Check if the property exists and is a valid string first + this.checkValidString(parameter, methodName, propertyName, isRequired); + // Check if the property exists and is a valid public key + if ( + propertyName in parameter && + !( + CryptoUtils.isValidEthereumPublicKey(parameter[propertyName]) || + CryptoUtils.isValidHederaPublicKey(parameter[propertyName]) + ) + ) { + console.error( + `Invalid ${methodName} Params passed. "${propertyName}" must be a valid public key`, + ); + throw rpcErrors.invalidParams( + `Invalid ${methodName} Params passed. "${propertyName}" must be a public key`, + ); + } + } + + /** + * Check whether the account was imported using private key(external account). + * @param params - Request params. + * @returns Whether to treat it as an external account that was imported using private key. + */ + + static isExternalAccountFlagSet(params: unknown): boolean { + if ( + params !== null && + typeof params === 'object' && + 'externalAccount' in params && + params.externalAccount !== null && + typeof params.externalAccount === 'object' + ) { + const parameter = params as ExternalAccount; + + if ('accountIdOrEvmAddress' in parameter.externalAccount) { + if ( + parameter.externalAccount.accountIdOrEvmAddress !== null && + typeof parameter.externalAccount.accountIdOrEvmAddress === 'string' + ) { + if (_.isEmpty(parameter.externalAccount.accountIdOrEvmAddress)) { + console.error( + 'Invalid externalAccount Params passed. "accountIdOrEvmAddress" must not be empty', + ); + throw rpcErrors.invalidParams( + 'Invalid externalAccount Params passed. "accountIdOrEvmAddress" must not be empty', + ); + } + + const evmAddress = parameter.externalAccount.accountIdOrEvmAddress; + + if (ethers.isAddress(evmAddress)) { + // Ensure the curve is set to ECDSA_SECP256K1 for Ethereum addresses + if ( + !( + 'curve' in parameter.externalAccount && + parameter.externalAccount.curve === ECDSA_SECP256K1_KEY_TYPE + ) + ) { + console.error( + `Invalid externalAccount Params passed. If "accountIdOrEvmAddress" is an Ethereum address, the "curve" must be set to "${ECDSA_SECP256K1_KEY_TYPE}".`, + ); + throw rpcErrors.invalidParams( + `Invalid externalAccount Params passed. If "accountIdOrEvmAddress" is an Ethereum address, the "curve" must be set to "${ECDSA_SECP256K1_KEY_TYPE}".`, + ); + } + } + + if ( + 'curve' in parameter.externalAccount && + parameter.externalAccount.curve !== null + ) { + if ( + typeof parameter.externalAccount.curve !== 'string' || + (parameter.externalAccount.curve !== ECDSA_SECP256K1_KEY_TYPE && + parameter.externalAccount.curve !== ED25519_KEY_TYPE) + ) { + console.error( + `Invalid externalAccount Params passed. "curve" must be a string and must be either "${ECDSA_SECP256K1_KEY_TYPE}" or "${ED25519_KEY_TYPE}"`, + ); + throw rpcErrors.invalidParams( + `Invalid externalAccount Params passed. "curve" must be a string and must be either "${ECDSA_SECP256K1_KEY_TYPE}" or "${ED25519_KEY_TYPE}"`, + ); + } + } + return true; + } + } + } + return false; + } + + /** + * Check Validation of Switch Method request. + * + * @param params - Request params. + */ + static isValidSwitchMethodRequest( + params: unknown, + ): asserts params is SwitchMethodRequestParams { + if (params === null || _.isEmpty(params) || !('didMethod' in params)) { + console.error( + 'Invalid switchMethod Params passed. "didMethod" must be passed as a parameter', + ); + throw rpcErrors.invalidParams( + 'Invalid switchMethod Params passed. "didMethod" must be passed as a parameter', + ); + } + const parameter = params as SwitchMethodRequestParams; + + ParamUtils.checkValidString(parameter, 'switchMethod', 'didMethod', true); + } + + /** + * Check Validation of Resolve DID request. + * + * @param params - Request params. + */ + static isValidResolveDIDRequest( + params: unknown, + ): asserts params is ResolveDIDRequestParams { + const parameter = params as ResolveDIDRequestParams; + + ParamUtils.checkValidString(parameter, 'resolveDID', 'did', false); + } + + /** + * Check Validation of Get VCs request. + * + * @param params - Request params. + */ + static isValidGetVCsRequest( + params: unknown, + ): asserts params is IDataManagerQueryArgs { + const parameter = params as IDataManagerQueryArgs; + + // Check if filter is valid + if ('filter' in parameter) { + ParamUtils.checkRequiredProperty( + parameter.filter, + 'getVCs', + 'filter', + true, + ); + } + + // Check if options is valid + if ( + 'options' in parameter && + parameter.options !== null && + typeof parameter.options === 'object' + ) { + if ('store' in parameter.options && parameter.options?.store !== null) { + if (typeof parameter.options?.store === 'string') { + if (!isValidVCStore(parameter.options?.store)) { + console.error( + `Invalid getVCs Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, + ); + throw new Error( + `Invalid getVCs Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, + ); + } + } else if ( + Array.isArray(parameter.options?.store) && + parameter.options?.store.length > 0 + ) { + (parameter.options?.store as [string]).forEach((store) => { + if (!isValidVCStore(store)) { + console.error( + `Invalid getVCs Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, + ); + throw new Error( + `Invalid getVCs Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, + ); + } + }); + } else { + console.error( + 'Invalid getVCs Params passed. "options.store" is not in a valid format. It must either be a string or an array of strings', + ); + throw new Error( + 'Invalid getVCs Params passed. "options.store" is not in a valid format. It must either be a string or an array of strings', + ); + } + } + } + + ParamUtils.checkValidBoolean( + parameter.options, + 'getVCs', + 'returnStore', + false, + ); + + // Check if accessToken is valid + ParamUtils.checkValidString(parameter, 'getVCs', 'accessToken', false); + } + + /** + * Check Validation of Save VC request. + * + * @param params - Request params. + */ + static isValidSaveVCRequest( + params: unknown, + ): asserts params is IDataManagerSaveArgs { + if (params === null || _.isEmpty(params) || !('data' in params)) { + console.error( + 'Invalid saveVC Params passed. "data" must be passed as a parameter', + ); + throw new Error( + 'Invalid saveVC Params passed. "data" must be passed as a parameter', + ); + } + + const parameter = params as IDataManagerSaveArgs; + + ParamUtils.checkRequiredProperty(parameter, 'saveVC', 'data', true); + + // Check if options is valid + if ( + 'options' in parameter && + parameter.options !== null && + typeof parameter.options === 'object' + ) { + if ('store' in parameter.options && parameter.options?.store !== null) { + if (typeof parameter.options?.store === 'string') { + if (!isValidVCStore(parameter.options?.store)) { + console.error( + `Invalid saveVC Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, + ); + throw new Error( + `Invalid saveVC Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, + ); + } + } else if ( + Array.isArray(parameter.options?.store) && + parameter.options?.store.length > 0 + ) { + (parameter.options?.store as [string]).forEach((store) => { + if (!isValidVCStore(store)) { + console.error( + `Invalid saveVC Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, + ); + throw new Error( + `Invalid saveVC Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, + ); + } + }); + } else { + console.error( + 'Invalid saveVC Params passed. "options.store" is not in a valid format. It must either be a string or an array of strings', + ); + throw new Error( + 'Invalid saveVC Params passed. "options.store" is not in a valid format. It must either be a string or an array of strings', + ); + } + } + } + + // Check if accessToken is valid + ParamUtils.checkValidString(parameter, 'saveVC', 'accessToken', false); + } + + /** + * Check Validation of Create VC request. + * + * @param params - Request params. + */ + static isValidCreateVCRequest( + params: unknown, + ): asserts params is CreateVCRequestParams { + if (params === null || _.isEmpty(params) || !('vcValue' in params)) { + console.error( + 'Invalid createVC Params passed. "vcValue" must be passed as a parameter', + ); + throw new Error( + 'Invalid createVC Params passed. "vcValue" must be passed as a parameter', + ); + } + + const parameter = params as CreateVCRequestParams; + + ParamUtils.checkRequiredProperty(parameter, 'createVC', 'vcValue', true); + ParamUtils.checkValidString(parameter, 'createVC', 'vcKey', false); + + // Check if credTypes is valid + if ('credTypes' in parameter) { + if ( + !( + parameter.credTypes !== null && + Array.isArray(parameter.credTypes) && + parameter.credTypes.length > 0 + ) + ) { + console.error( + 'Invalid createVC Params passed. "credTypes" is not in a valid format. It must be an array of strings', + ); + throw new Error( + 'Invalid createVC Params passed. "credTypes" is not in a valid format. It must be an array of strings', + ); + } + } + + // Check if options is valid + if ( + 'options' in parameter && + parameter.options !== null && + typeof parameter.options === 'object' + ) { + if ('store' in parameter.options && parameter.options?.store !== null) { + if (typeof parameter.options?.store === 'string') { + if (!isValidVCStore(parameter.options?.store)) { + console.error( + `Invalid createVC Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, + ); + throw new Error( + `Invalid createVC Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, + ); + } + } else if ( + Array.isArray(parameter.options?.store) && + parameter.options?.store.length > 0 + ) { + (parameter.options?.store as [string]).forEach((store) => { + if (!isValidVCStore(store)) { + console.error( + `Invalid createVC Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, + ); + throw new Error( + `Invalid createVC Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, + ); + } + }); + } else { + console.error( + 'Invalid createVC Params passed. "options.store" is not in a valid format. It must either be a string or an array of strings', + ); + throw new Error( + 'Invalid createVC Params passed. "options.store" is not in a valid format. It must either be a string or an array of strings', + ); + } + } + } + + // Check if accessToken is valid + ParamUtils.checkValidString(parameter, 'createVC', 'accessToken', false); + } + + /** + * Check Validation of Verify VC request. + * + * @param params - Request params. + */ + static isValidVerifyVCRequest( + params: unknown, + ): asserts params is VerifyVCRequestParams { + if ( + params === null || + _.isEmpty(params) || + !('verifiableCredential' in params) + ) { + console.error( + 'Invalid verifyVC Params passed. "verifiableCredential" must be passed as a parameter', + ); + throw new Error( + 'Invalid verifyVC Params passed. "verifiableCredential" must be passed as a parameter', + ); + } + + const parameter = params as VerifyVCRequestParams; + + ParamUtils.checkRequiredProperty( + parameter, + 'verifyVC', + 'verifiableCredential', + true, + ); + } + + /** + * Check Validation of Remove VC request. + * + * @param params - Request params. + */ + static isValidRemoveVCRequest( + params: unknown, + ): asserts params is IDataManagerDeleteArgs { + if (params === null || _.isEmpty(params) || !('id' in params)) { + console.error( + 'Invalid removeVC Params passed. "id" must be passed as a parameter', + ); + throw new Error( + 'Invalid removeVC Params passed. "id" must be passed as a parameter', + ); + } + + const parameter = params as IDataManagerDeleteArgs; + + ParamUtils.checkValidString(parameter, 'removeVC', 'id', true); + // Check if id is valid + if (!Array.isArray(parameter.id) && !(typeof parameter.id === 'string')) { + console.error( + 'Invalid removeVC Params passed. "id" must be a string or an array of strings', + ); + throw new Error( + 'Invalid removeVC Params passed. "id" must be a string or an array of strings', + ); + } + + // Check if options is valid + if ( + 'options' in parameter && + parameter.options !== null && + typeof parameter.options === 'object' + ) { + if ('store' in parameter.options && parameter.options?.store !== null) { + if (typeof parameter.options?.store === 'string') { + if (!isValidVCStore(parameter.options?.store)) { + console.error( + `Invalid removeVC Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, + ); + throw new Error( + `Invalid removeVC Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, + ); + } + } else if ( + Array.isArray(parameter.options?.store) && + parameter.options?.store.length > 0 + ) { + (parameter.options?.store as [string]).forEach((store) => { + if (!isValidVCStore(store)) { + console.error( + `Invalid removeVC Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, + ); + throw new Error( + `Invalid removeVC Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, + ); + } + }); + } else { + console.error( + 'Invalid removeVC Params passed. "options.store" is not in a valid format. It must either be a string or an array of strings', + ); + throw new Error( + 'Invalid removeVC Params passed. "options.store" is not in a valid format. It must either be a string or an array of strings', + ); + } + } + } + + // Check if accessToken is valid + ParamUtils.checkValidString(parameter, 'removeVC', 'accessToken', false); + } + + /** + * Check Validation of Delete all VCs request. + * + * @param params - Request params. + */ + static isValidDeleteAllVCsRequest( + params: unknown, + ): asserts params is IDataManagerClearArgs { + const parameter = params as IDataManagerClearArgs; + + // Check if filter is valid + if ('filter' in parameter) { + ParamUtils.checkValidString( + parameter.filter, + 'deleteAllVCs', + 'type', + true, + ); + ParamUtils.checkRequiredProperty( + parameter.filter, + 'getVCs', + 'filter', + true, + ); + } + + // Check if options is valid + if ( + 'options' in parameter && + parameter.options !== null && + typeof parameter.options === 'object' + ) { + if ('store' in parameter.options && parameter.options?.store !== null) { + if (typeof parameter.options?.store === 'string') { + if (!isValidVCStore(parameter.options?.store)) { + console.error( + `Invalid deleteAllVCs Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, + ); + throw new Error( + `Invalid deleteAllVCs Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, + ); + } + } else if ( + Array.isArray(parameter.options?.store) && + parameter.options?.store.length > 0 + ) { + (parameter.options?.store as [string]).forEach((store) => { + if (!isValidVCStore(store)) { + console.error( + `Invalid deleteAllVCs Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, + ); + throw new Error( + `Invalid deleteAllVCs Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, + ); + } + }); + } else { + console.error( + 'Invalid deleteAllVCs Params passed. "options.store" is not in a valid format. It must either be a string or an array of strings', + ); + throw new Error( + 'Invalid deleteAllVCs Params passed. "options.store" is not in a valid format. It must either be a string or an array of strings', + ); + } + } + } + + // Check if accessToken is valid + ParamUtils.checkValidString( + parameter, + 'deleteAllVCs', + 'accessToken', + false, + ); + } + + /** + * Check Validation of Create VP request. + * + * @param params - Request params. + */ + static isValidCreateVPRequest( + params: unknown, + ): asserts params is CreateVPRequestParams { + if (params === null || _.isEmpty(params)) { + console.error( + 'Invalid createVP Params passed. "vcIds" or "vcs" must be passed as a parameter', + ); + throw new Error( + 'Invalid createVP Params passed. "vcIds" or "vcs" must be passed as a parameter', + ); + } + + const parameter = params as CreateVPRequestParams; + + // Ensure that either vcIds or vcs is passed + if (!('vcIds' in parameter || 'vcs' in parameter)) { + console.error( + 'Invalid createVP Params passed. Either "vcIds" or "vcs" must be passed as parameters', + ); + throw new Error( + 'Invalid createVP Params passed. Either "vcIds" or "vcs" must be passed as parameters', + ); + } + + // Check if vcIds is valid + if ( + 'vcIds' in parameter && + parameter.vcs !== null && + Array.isArray(parameter.vcs) + ) { + (parameter.vcIds as [string]).forEach((vcId) => { + // Check if vcId is valid + if (!(vcId !== null && typeof vcId === 'string')) { + console.error( + `Invalid createVP Params passed. vcId: '${vcId}' is not in a valid format. It must be a string`, + ); + throw new Error( + `Invalid createVP Params passed. vcId: '${vcId}' is not in a valid format. It must be a string`, + ); + } + }); + + // Check if options is valid + if ( + 'options' in parameter && + parameter.options !== null && + typeof parameter.options === 'object' + ) { + if ('store' in parameter.options && parameter.options?.store !== null) { + if (typeof parameter.options?.store === 'string') { + if (!isValidVCStore(parameter.options?.store)) { + console.error( + `Invalid createVP Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, + ); + throw new Error( + `Invalid createVP Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, + ); + } + } else if ( + Array.isArray(parameter.options?.store) && + parameter.options?.store.length > 0 + ) { + (parameter.options?.store as [string]).forEach((store) => { + if (!isValidVCStore(store)) { + console.error( + `Invalid createVP Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, + ); + throw new Error( + `Invalid createVP Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, + ); + } + }); + } else { + console.error( + 'Invalid createVP Params passed. "options.store" is not in a valid format. It must either be a string or an array of strings', + ); + throw new Error( + 'Invalid createVP Params passed. "options.store" is not in a valid format. It must either be a string or an array of strings', + ); + } + } + } + } + + // Check if vcs is valid + if ( + 'vcs' in parameter && + parameter.vcs !== null && + Array.isArray(parameter.vcs) + ) { + (parameter.vcs as [string]).forEach((vc) => { + // Check if vc is valid + if (!(vc !== null && typeof vc === 'object')) { + console.error( + 'Invalid createVP Params passed. One of the vcs that was passed is not in a valid format. It must be an object', + ); + throw new Error( + 'Invalid createVP Params passed. One of the vcs that was passed is not in a valid format. It must be an object', + ); + } + }); + } + + // Check if proofInfo is valid + if ( + 'proofInfo' in parameter && + parameter.proofInfo !== null && + typeof parameter.proofInfo === 'object' + ) { + // Check if proofFormat is valid + if ( + 'proofFormat' in parameter.proofInfo && + parameter.proofInfo.proofFormat !== null && + !isValidProofFormat(parameter.proofInfo.proofFormat as string) + ) { + console.error( + `Invalid createVP Params passed. Proofformat '${parameter.proofInfo.proofFormat}' not supported. The supported proof formats are: ${availableProofFormats}`, + ); + throw new Error( + `Invalid createVP Params passed. Proofformat '${parameter.proofInfo.proofFormat}' not supported. The supported proof formats are: ${availableProofFormats}`, + ); + } + + // Check if type is a string + if ( + 'type' in parameter.proofInfo && + parameter.proofInfo.type !== null && + typeof parameter.proofInfo.type !== 'string' + ) { + console.error( + 'Invalid createVP Params passed. "proofInfo.type" is not in a valid format. It must be a string', + ); + throw new Error( + 'Invalid createVP Params passed. "proofInfo.type" is not in a valid format. It must be a string', + ); + } + + // Check if domain is a string + if ( + 'domain' in parameter.proofInfo && + parameter.proofInfo.domain !== null && + typeof parameter.proofInfo.domain !== 'string' + ) { + console.error( + 'Invalid createVP Params passed. "proofInfo.domain" is not in a valid format. It must be a string', + ); + throw new Error( + 'Invalid createVP Params passed. "proofInfo.domain" is not in a valid format. It must be a string', + ); + } + + // Check if challenge is a string + if ( + 'challenge' in parameter.proofInfo && + parameter.proofInfo.challenge !== null && + typeof parameter.proofInfo.challenge !== 'string' + ) { + console.error( + 'Invalid createVP Params passed. "proofInfo.challenge" is not in a valid format. It must be a string', + ); + throw new Error( + 'Invalid createVP Params passed. "proofInfo.challenge" is not in a valid format. It must be a string', + ); + } + } + } + + /** + * Check Validation of Verify VP request. + * + * @param params - Request params. + */ + static isValidVerifyVPRequest( + params: unknown, + ): asserts params is VerifyVPRequestParams { + if ( + params === null || + _.isEmpty(params) || + !('verifiablePresentation' in params) + ) { + console.error( + 'Invalid verifyVP Params passed. "verifiablePresentation" must be passed as a parameter', + ); + throw new Error( + 'Invalid verifyVP Params passed. "verifiablePresentation" must be passed as a parameter', + ); + } + + const parameter = params as VerifyVPRequestParams; + + ParamUtils.checkRequiredProperty( + parameter, + 'verifyVP', + 'verifiablePresentation', + true, + ); + } + + /** + * Check Validation of Configure google request. + * + * @param params - Request params. + */ + static isValidConfigueGoogleRequest( + params: unknown, + ): asserts params is GoogleToken { + if (params === null || _.isEmpty(params) || !('accessToken' in params)) { + console.error( + 'Invalid configureGoogleAccount Params passed. "accessToken" must be passed as a parameter', + ); + throw new Error( + 'Invalid configureGoogleAccount Params passed. "accessToken" must be passed as a parameter', + ); + } + + const parameter = params as GoogleToken; + + ParamUtils.checkValidString( + parameter, + 'configureGoogleAccount', + 'accessToken', + true, + ); + } +} diff --git a/packages/hedera-identify-snap/packages/snap/src/utils/SnapUtils.tsx b/packages/hedera-identify-snap/packages/snap/src/utils/SnapUtils.tsx new file mode 100644 index 00000000..33769e2d --- /dev/null +++ b/packages/hedera-identify-snap/packages/snap/src/utils/SnapUtils.tsx @@ -0,0 +1,274 @@ +/*- + * + * 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, NodeType, Panel } from '@metamask/snaps-sdk'; +import { + NotificationType, + divider, + heading, + panel, + text, +} from '@metamask/snaps-sdk'; +import { VerifiableCredential } from '@veramo/core'; +import _ from 'lodash'; +import cloneDeep from 'lodash.clonedeep'; +import { getNetworkNameFromChainId } from '../constants'; +import { IDataManagerQueryResult } from '../plugins/veramo/verifiable-creds-manager'; + +export class SnapUtils { + /** + * Function to generate panel. + * @returns Panel to be displayed in the snap dialog. + */ + public static initializePanelToShow(): any { + const panelToShow: ( + | { + value: string; + type: NodeType.Heading; + } + | { + value: string; + type: NodeType.Text; + markdown?: boolean | undefined; + } + | { + type: NodeType.Divider; + } + | { + value: string; + type: NodeType.Copyable; + sensitive?: boolean | undefined; + } + )[] = []; + return panelToShow; + } + + /** + * Function to generate snap dialog panel. + * @param origin - The origin of where the call is being made from. + * @param network - The network the call is being made on. + * @param prompt - Prompt text of the metamask dialog box(eg. 'Are you sure you want to send VCs to the dApp?'). + * @returns Panel to be displayed in the snap dialog. + */ + public static async generateCommonPanel( + origin: string, + network: string, + prompt: any[], + ): Promise { + const panelToShow = [ + text(`Origin: **${origin}**`), + text(`Network: **${getNetworkNameFromChainId(network)}**`), + divider(), + ...prompt, + ]; + return panel(panelToShow); + } + + /** + * Function to generate snap dialog panel for VC related functions. + * + * @param origin - The origin of where the call is being made from. + * @param header - Header text of the metamask dialog box(eg. 'Retrieve Verifiable Credentials'). + * @param prompt - Prompt text of the metamask dialog box(eg. 'Are you sure you want to send VCs to the dApp?'). + * @param description - Description text of the metamask dialog box(eg. 'Some dApps are less secure than others and could save data from VCs against your will. Be careful where you send your private VCs! Number of VCs submitted is 2'). + * @param vcs - The Verifiable Credentials to show on the metamask dialog box. + */ + public static async generateVCPanel( + origin: string, + network: string, + header: string, + prompt: string, + description: string, + vcs: IDataManagerQueryResult[], + ): Promise { + const vcsToUse = cloneDeep(vcs); + const panelToShow = [ + text(`Origin: ${origin}`), + text(`Network: **${getNetworkNameFromChainId(network)}**`), + divider(), + heading(header), + text(prompt), + divider(), + text(description), + ]; + vcsToUse.forEach((vc, index) => { + const vcData = vc.data as VerifiableCredential; + delete vcData.credentialSubject.id; + delete vcData.credentialSubject.hederaAccountId; + panelToShow.push(divider()); + + const credentialNumber = (index + 1).toString(); + panelToShow.push(text(`Credential #${credentialNumber}`)); + panelToShow.push(divider()); + + panelToShow.push(text('ID: ')); + panelToShow.push( + text(_.isEmpty(vc.metadata.id) ? credentialNumber : vc.metadata.id), + ); + + panelToShow.push(text('STORAGE: ')); + panelToShow.push(text(vc.metadata.store as string)); + + panelToShow.push(text('TYPE:')); + panelToShow.push(text(JSON.stringify(vcData.type))); + + panelToShow.push(text('SUBJECT:')); + panelToShow.push(text(JSON.stringify(vcData.credentialSubject))); + + panelToShow.push(text('ISSUANCE DATE:')); + const issuanceDate = new Date( + vcData.issuanceDate as string, + ).toLocaleString(undefined, { + year: 'numeric', + month: '2-digit', + day: '2-digit', + weekday: 'long', + hour: '2-digit', + hour12: false, + minute: '2-digit', + second: '2-digit', + }); + panelToShow.push(text(issuanceDate)); + + panelToShow.push(text('EXPIRATION DATE:')); + let expirationDate = 'Does not expire'; + if (vcData.expirationDate) { + expirationDate = new Date( + vcData.expirationDate as string, + ).toLocaleString(undefined, { + year: 'numeric', + month: '2-digit', + day: '2-digit', + weekday: 'long', + hour: '2-digit', + hour12: false, + minute: '2-digit', + second: '2-digit', + }); + } + panelToShow.push(text(expirationDate)); + }); + return panel(panelToShow); + } + + /** + * Request Hedera Account Id. + * @param origin - Source. + * @param network - The network the call is being made on. + * @param mirrorNodeUrl - The mirror node url. + * @param publicKey - Public key. + * @param address - EVM address. + * @returns Hedera account id. + */ + public static async requestHederaAccountId( + origin: string, + network: string, + mirrorNodeUrl: string, + publicKey: string, + address: string, + ): Promise { + const dialogParamsForHederaAccountId: DialogParams = { + type: 'prompt', + content: await SnapUtils.generateCommonPanel(origin, network, [ + heading('Connect to Hedera Account'), + text( + `Enter your hedera account Id associated with the following account`, + ), + divider(), + text(`Public Key: ${publicKey}`), + text(`EVM Address: ${address}`), + divider(), + ]), + placeholder: '0.0.3658062', + }; + return (await SnapUtils.snapDialog( + dialogParamsForHederaAccountId, + )) as string; + } + + /** + * Function that opens snap dialog. + * @param params - Snap dialog params. + * @returns Response from the snap dialog. + */ + public static async snapDialog( + params: DialogParams, + ): Promise { + return (await snap.request({ + method: 'snap_dialog', + params, + })) as boolean; + } + + public static async snapCreateInteractiveUI( + panelToShow: Panel, + ): Promise<{ interfaceId: string; confirmed: boolean }> { + const interfaceId = await snap.request({ + method: 'snap_createInterface', + params: { + ui: panelToShow, + }, + }); + return { + interfaceId, + confirmed: (await snap.request({ + method: 'snap_dialog', + params: { + type: 'confirmation', + id: interfaceId, + }, + })) as boolean, + }; + } + + public static async snapUpdateInteractiveUI( + interfaceId: string, + panelToShow: Panel, + ) { + await snap.request({ + method: 'snap_updateInterface', + params: { + id: interfaceId, + ui: panelToShow, + }, + }); + } + + /** + * Function that sends notification to Metamask. + * @param content - Content to write. + */ + public static async snapNotification(content: string): Promise { + await snap.request({ + method: 'snap_notify', + params: { + type: NotificationType.InApp, + message: content, + }, + }); + await snap.request({ + method: 'snap_notify', + params: { + type: NotificationType.Native, + message: content, + }, + }); + } +} diff --git a/packages/hedera-identify-snap/packages/snap/src/utils/StateUtils.ts b/packages/hedera-identify-snap/packages/snap/src/utils/StateUtils.ts new file mode 100644 index 00000000..70ecbb59 --- /dev/null +++ b/packages/hedera-identify-snap/packages/snap/src/utils/StateUtils.ts @@ -0,0 +1,80 @@ +/*- + * + * 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 cloneDeep from 'lodash.clonedeep'; + +import { IIdentifier, IKey, W3CVerifiableCredential } from '@veramo/core'; +import { ManagedPrivateKey } from '@veramo/key-manager'; +import type { Account, HederaAccountInfo } from '../types/account'; +import type { + GoogleUserInfo, + IdentifyAccountConfig, + IdentifyAccountState, + IdentifySnapConfig, + IdentifySnapState, + KeyStore, +} from '../types/state'; + +export class StateUtils { + static readonly #emptyAccountState: IdentifyAccountState = { + keyStore: { + curve: '', + privateKey: '', + publicKey: '', + address: '', + hederaAccountId: '', + } as KeyStore, + accountInfo: {} as HederaAccountInfo, + + snapKeyStore: {} as Record, + snapPrivateKeyStore: {} as Record, + identifiers: {} as Record, + vcs: {} as Record, + accountConfig: { + identity: { + vcStore: 'snap', + googleUserInfo: {} as GoogleUserInfo, + }, + } as IdentifyAccountConfig, + } as IdentifyAccountState; + + public static getEmptyAccountState(): IdentifyAccountState { + return cloneDeep(this.#emptyAccountState); + } + + static readonly #initialSnapState: IdentifySnapState = { + currentAccount: {} as Account, + accountState: {} as Record>, + snapConfig: { + dApp: { + didMethod: 'did:pkh', + disablePopups: false, + friendlyDapps: [], + }, + snap: { + acceptedTerms: true, + }, + } as IdentifySnapConfig, + }; + + public static getInitialSnapState(): IdentifySnapState { + return cloneDeep(this.#initialSnapState); + } +} diff --git a/packages/hedera-identify-snap/packages/snap/src/utils/Utils.ts b/packages/hedera-identify-snap/packages/snap/src/utils/Utils.ts new file mode 100644 index 00000000..da76347b --- /dev/null +++ b/packages/hedera-identify-snap/packages/snap/src/utils/Utils.ts @@ -0,0 +1,107 @@ +/*- + * + * 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 { VerifiableCredential } from '@veramo/core'; +import { normalizeCredential } from 'did-jwt-vc'; +import cloneDeep from 'lodash.clonedeep'; +import { CodecName, MULTICODECS } from '../constants'; + +export class Utils { + public static timestampToString( + data: string | number | Date | null | undefined, + ): string { + if (!data) { + return ''; + } + + let timestamp: number; + if (data instanceof Date) { + timestamp = data.getTime() / 1000; + } else if (typeof data === 'string' || typeof data === 'number') { + timestamp = parseFloat(data.toString()); + } else { + return ''; + } + + return new Date(timestamp * 1000).toUTCString(); + } + + /** + * Adds the prefix to the EVM address. + * @param address - EVM Account address. + * @returns EVM address. + */ + public static ensure0xPrefix(address: string): string { + let result = address; + if (!address.startsWith('0x')) { + result = `0x${address}`; + } + return result.toLowerCase(); + } + + /** + * Capitalizes the first letter of the given string. + * @param string - The string to capitalize. + * @returns The string with the first letter capitalized. + */ + public static capitalizeFirstLetter(string: string): string { + return string.charAt(0).toUpperCase() + string.slice(1); + } + + /** + * Prefix a buffer with a multicodec-packed. + * + * @param {CodecName} multicodec + * @param {Uint8Array} data + * + * @returns {Uint8Array} + */ + public static addMulticodecPrefix = ( + multicodec: CodecName, + data: Uint8Array, + ): Uint8Array => { + let prefix; + + if (MULTICODECS[multicodec]) { + prefix = Buffer.from(MULTICODECS[multicodec]); + } else { + throw new Error('multicodec not recognized'); + } + + return Buffer.concat([prefix, data], prefix.length + data.length); + }; + + /** + * Function to decode JWT. + * + * @param jwt - JWT string. + * @returns Verifiable Credential. + */ + public static decodeJWT(jwt: string): VerifiableCredential { + try { + const normalizedVC = normalizeCredential(jwt); + const vc = cloneDeep(normalizedVC); + + return vc; + } catch (e) { + throw new Error('Invalid JWT'); + } + } +} diff --git a/packages/hedera-identify-snap/packages/snap/src/utils/config.ts b/packages/hedera-identify-snap/packages/snap/src/utils/config.ts deleted file mode 100644 index c012f4b2..00000000 --- a/packages/hedera-identify-snap/packages/snap/src/utils/config.ts +++ /dev/null @@ -1,69 +0,0 @@ -/*- - * - * 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 cloneDeep from 'lodash.clonedeep'; -import { - Account, - GoogleUserInfo, - IdentityAccountConfig, - IdentityAccountState, - IdentitySnapState, -} from '../interfaces'; -import { DEFAULTCOINTYPE, HEDERACOINTYPE } from '../types/constants'; - -const emptyAccountState = { - snapPrivateKeyStore: {}, - snapKeyStore: {}, - identifiers: {}, - vcs: {}, - index: 0, - accountConfig: { - identity: { - vcStore: 'snap', - googleUserInfo: {} as GoogleUserInfo, - }, - } as IdentityAccountConfig, -} as IdentityAccountState; - -export const getEmptyAccountState = () => { - return cloneDeep(emptyAccountState); -}; - -const initialSnapState: IdentitySnapState = { - currentAccount: {} as Account, - accountState: { - [DEFAULTCOINTYPE]: {}, - [HEDERACOINTYPE]: {}, - }, - snapConfig: { - dApp: { - didMethod: 'did:pkh', - disablePopups: false, - friendlyDapps: [], - }, - snap: { - acceptedTerms: true, - }, - }, -}; - -export const getInitialSnapState = () => { - return cloneDeep(initialSnapState); -}; diff --git a/packages/hedera-identify-snap/packages/snap/src/utils/init.ts b/packages/hedera-identify-snap/packages/snap/src/utils/init.ts deleted file mode 100644 index 2e2233cb..00000000 --- a/packages/hedera-identify-snap/packages/snap/src/utils/init.ts +++ /dev/null @@ -1,48 +0,0 @@ -/*- - * - * 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 { DialogParams, heading, text } from '@metamask/snaps-sdk'; -import { IdentitySnapState } from '../interfaces'; -import { generateCommonPanel, snapDialog } from '../snap/dialog'; -import { initState } from '../snap/state'; - -/** - * Init snap state. - * - * @param snap - Snap. - */ -export async function init( - origin: string, - network: string, -): Promise { - const dialogParams: DialogParams = { - type: 'alert', - content: await generateCommonPanel(origin, network, [ - heading('Risks about using Identify Snap'), - text( - 'Applications do NOT have access to your private keys. You are in control of what VCs and VPs you sign and what you use your DIDs for.', - ), - ]), - }; - - await snapDialog(dialogParams); - console.log('starting init'); - return await initState(); -} diff --git a/packages/hedera-identify-snap/packages/snap/src/utils/jwt.ts b/packages/hedera-identify-snap/packages/snap/src/utils/jwt.ts deleted file mode 100644 index 45ca014b..00000000 --- a/packages/hedera-identify-snap/packages/snap/src/utils/jwt.ts +++ /dev/null @@ -1,40 +0,0 @@ -/*- - * - * 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 { VerifiableCredential } from '@veramo/core'; -import { normalizeCredential } from 'did-jwt-vc'; -import cloneDeep from 'lodash.clonedeep'; - -/** - * Function to decode JWT. - * - * @param jwt - JWT string. - * @returns Verifiable Credential. - */ -export function decodeJWT(jwt: string): VerifiableCredential { - try { - const normalizedVC = normalizeCredential(jwt); - const vc = cloneDeep(normalizedVC); - - return vc; - } catch (e) { - throw new Error('Invalid JWT'); - } -} diff --git a/packages/hedera-identify-snap/packages/snap/src/utils/keyPair.ts b/packages/hedera-identify-snap/packages/snap/src/utils/keyPair.ts deleted file mode 100644 index 97431be8..00000000 --- a/packages/hedera-identify-snap/packages/snap/src/utils/keyPair.ts +++ /dev/null @@ -1,104 +0,0 @@ -/*- - * - * 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 { assertArgument, HDNodeWallet, Mnemonic } from 'ethers'; -import { publicKeyConvert } from 'secp256k1'; -import { DEFAULTCOINTYPE } from '../types/constants'; - -export const generateWallet = async ( - evmAddress: string, -): Promise => { - const entropy = await snap.request({ - method: 'snap_getEntropy', - params: { - version: 1, - salt: evmAddress, - }, - }); - - let nodeWallet = HDNodeWallet.fromMnemonic(Mnemonic.fromEntropy(entropy)); - nodeWallet = derivePathForWallet(nodeWallet, `m/44/${DEFAULTCOINTYPE}/0/0/0`); - - return nodeWallet; -}; - -export const derivePathForWallet = ( - node: HDNodeWallet, - path: string, -): HDNodeWallet => { - const components = path.split('/'); - - assertArgument( - components.length > 0 && (components[0] === 'm' || node.depth > 0), - 'invalid path', - 'path', - path, - ); - - if (components[0] === 'm') { - components.shift(); - } - - let result: HDNodeWallet = node; - - const HardenedBit = 0x80000000; - for (let i = 0; i < components.length; i++) { - const component = components[i]; - - if (component.match(/^[0-9]+'$/u)) { - const index = parseInt(component.substring(0, component.length - 1), 10); - - assertArgument( - index < HardenedBit, - 'invalid path index', - `path[${i}]`, - component, - ); - result = result.deriveChild(HardenedBit + index); - } else if (component.match(/^[0-9]+$/u)) { - const index = parseInt(component, 10); - assertArgument( - index < HardenedBit, - 'invalid path index', - `path[${i}]`, - component, - ); - result = result.deriveChild(index); - } else { - assertArgument(false, 'invalid path component', `path[${i}]`, component); - } - } - - return result; -}; - -export function uint8ArrayToHex(arr: Uint8Array) { - return Buffer.from(arr).toString('hex'); -} - -export function hexToUint8Array(str: string): Uint8Array { - return new Uint8Array(Buffer.from(str, 'hex')); -} - -export function getCompressedPublicKey(publicKey: string): string { - return uint8ArrayToHex( - publicKeyConvert(hexToUint8Array(publicKey.split('0x')[1]), true), - ); -} diff --git a/packages/hedera-identify-snap/packages/snap/src/utils/params.ts b/packages/hedera-identify-snap/packages/snap/src/utils/params.ts deleted file mode 100644 index 67b5c4e2..00000000 --- a/packages/hedera-identify-snap/packages/snap/src/utils/params.ts +++ /dev/null @@ -1,978 +0,0 @@ -/*- - * - * 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 { VerifiablePresentation, W3CVerifiableCredential } from '@veramo/core'; -import _ from 'lodash'; -import { - EvmAccountParams, - ExternalAccount, - GoogleToken, - HederaAccountParams, - IdentitySnapState, - MetamaskAccountParams, -} from '../interfaces'; -import { - IDataManagerClearArgs, - IDataManagerDeleteArgs, - IDataManagerQueryArgs, - IDataManagerSaveArgs, -} from '../plugins/veramo/verifiable-creds-manager'; -import { getAccountStateByCoinType } from '../snap/state'; -import { - HEDERACOINTYPE, - availableProofFormats, - availableVCStores, - isValidProofFormat, - isValidVCStore, -} from '../types/constants'; -import { CreateVCRequestParams, CreateVPRequestParams } from '../types/params'; - -/** - * Check whether the the account was imported using private key(external account). - * - * @param params - Request params. - * @returns Whether to treat it as an external account that was imported using private key. - */ -export function isExternalAccountFlagSet(params: unknown): boolean { - if ( - params !== null && - typeof params === 'object' && - 'externalAccount' in params && - params.externalAccount !== null && - typeof params.externalAccount === 'object' - ) { - const parameter = params as ExternalAccount; - if ('blockchainType' in parameter.externalAccount) { - if ( - (parameter.externalAccount.blockchainType === 'hedera' && - typeof parameter.externalAccount.data === 'object' && - typeof (parameter.externalAccount.data as HederaAccountParams) - .accountId === 'string') || - (parameter.externalAccount.blockchainType === 'evm' && - typeof parameter.externalAccount.data === 'object' && - typeof (parameter.externalAccount.data as EvmAccountParams) - .address === 'string') - ) { - return true; - } - } - } - return false; -} - -/** - * Check validation of Metamask account. - * - * @param params - Request params. - */ -export function isValidMetamaskAccountParams( - params: unknown, -): asserts params is MetamaskAccountParams { - if ( - !( - params !== null && - typeof params === 'object' && - 'metamaskAddress' in params && - typeof params.metamaskAddress === 'string' - ) - ) { - console.error( - 'Invalid Metamask Account Params passed. "metamaskAddress" must be passed as a parameter', - ); - throw new Error( - 'Invalid Metamask Account Params passed. "metamaskAddress" must be passed as a parameter', - ); - } -} - -/** - * Check if Hedera account was imported. - * - * @param state - IdentitySnapState. - * @param accountId - Hedera identifier. - * @param evmAddress - Ethereum address. - * @returns Result. - */ -export async function getHederaAccountIfExists( - state: IdentitySnapState, - accountId: string | undefined, - evmAddress: string | undefined, -): Promise { - let result = ''; - for (const address of Object.keys(state.accountState[HEDERACOINTYPE])) { - const accountState = await getAccountStateByCoinType(state, address); - const hederaAccountId = accountState.extraData; - if (evmAddress && evmAddress === address) { - result = hederaAccountId as string; - } - - if (accountId && hederaAccountId === accountId) { - result = address; - } - } - return result; -} - -type SwitchMethodRequestParams = { - didMethod: string; -}; - -/** - * Check Validation of Switch Method request. - * - * @param params - Request params. - */ -export function isValidSwitchMethodRequest( - params: unknown, -): asserts params is SwitchMethodRequestParams { - if (params === null || _.isEmpty(params)) { - console.error( - 'Invalid switchMethod Params passed. "didMethod" must be passed as a parameter', - ); - throw new Error( - 'Invalid switchMethod Params passed. "didMethod" must be passed as a parameter', - ); - } - const parameter = params as SwitchMethodRequestParams; - - if ( - 'didMethod' in parameter && - parameter.didMethod !== null && - typeof parameter.didMethod === 'string' - ) { - return; - } - - console.error( - 'Invalid switchMethod Params passed. "didMethod" must be passed as a parameter and it must be a string', - ); - throw new Error( - 'Invalid switchMethod Params passed. "didMethod" must be passed as a parameter and it must be a string', - ); -} - -type ResolveDIDRequestParams = { did?: string }; - -/** - * Check Validation of Resolve DID request. - * - * @param params - Request params. - */ -export function isValidResolveDIDRequest( - params: unknown, -): asserts params is ResolveDIDRequestParams { - const parameter = params as ResolveDIDRequestParams; - - if ( - 'did' in parameter && - (parameter.did === null || typeof parameter.did !== 'string') - ) { - console.error('Invalid resolveDID Params passed. "did" must be a string'); - throw new Error('Invalid resolveDID Params passed. "did" must be a string'); - } -} - -/** - * Check Validation of Get VCs request. - * - * @param params - Request params. - */ -export function isValidGetVCsRequest( - params: unknown, -): asserts params is IDataManagerQueryArgs { - const parameter = params as IDataManagerQueryArgs; - - // Check if filter is valid - if ( - 'filter' in parameter && - parameter.filter !== null && - typeof parameter.filter === 'object' - ) { - if ( - !( - 'type' in parameter.filter && - parameter.filter?.type !== null && - typeof parameter.filter?.type === 'string' - ) - ) { - console.error( - 'Invalid getVCs Params passed. "filter.type" is either missing or is not a string', - ); - throw new Error( - 'Invalid getVCs Params passed. "filter.type" is either missing or is not a string', - ); - } - - if (!('filter' in parameter.filter && parameter.filter?.filter !== null)) { - console.error('Invalid getVCs Params passed. "filter.filter" is missing'); - throw new Error( - 'Invalid getVCs Params passed. "filter.filter" is missing', - ); - } - } - - // Check if options is valid - if ( - 'options' in parameter && - parameter.options !== null && - typeof parameter.options === 'object' - ) { - if ('store' in parameter.options && parameter.options?.store !== null) { - if (typeof parameter.options?.store === 'string') { - if (!isValidVCStore(parameter.options?.store)) { - console.error( - `Invalid getVCs Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, - ); - throw new Error( - `Invalid getVCs Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, - ); - } - } else if ( - Array.isArray(parameter.options?.store) && - parameter.options?.store.length > 0 - ) { - (parameter.options?.store as [string]).forEach((store) => { - if (!isValidVCStore(store)) { - console.error( - `Invalid getVCs Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, - ); - throw new Error( - `Invalid getVCs Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, - ); - } - }); - } else { - console.error( - 'Invalid getVCs Params passed. "options.store" is not in a valid format. It must either be a string or an array of strings', - ); - throw new Error( - 'Invalid getVCs Params passed. "options.store" is not in a valid format. It must either be a string or an array of strings', - ); - } - } - - // Check if returnStore is valid - if ('returnStore' in parameter.options) { - if ( - !( - 'returnStore' in parameter.options && - parameter.options?.returnStore !== null && - typeof parameter.options?.returnStore === 'boolean' - ) - ) { - console.error( - 'Invalid getVCs Params passed. "options.returnStore" is not in a valid format. It must be a boolean', - ); - throw new Error( - 'Invalid getVCs Params passed. "options.store" is not in a valid format. It must be a boolean', - ); - } - } - } - - // Check if accessToken is valid - if ('accessToken' in parameter) { - if ( - !( - parameter.accessToken !== null && - typeof parameter.accessToken === 'string' - ) - ) { - console.error( - 'Invalid getVCs Params passed. "accessToken" is not in a valid format. It must be a string', - ); - throw new Error( - 'Invalid getVCs Params passed. "accessToken" is not in a valid format. It must be a string', - ); - } - } -} - -/** - * Check Validation of Save VC request. - * - * @param params - Request params. - */ -export function isValidSaveVCRequest( - params: unknown, -): asserts params is IDataManagerSaveArgs { - if (params === null || _.isEmpty(params) || !('data' in params)) { - console.error( - 'Invalid saveVC Params passed. "data" must be passed as a parameter', - ); - throw new Error( - 'Invalid saveVC Params passed. "data" must be passed as a parameter', - ); - } - - const parameter = params as IDataManagerSaveArgs; - - if ( - 'data' in parameter && - parameter.data !== null && - typeof parameter.data === 'object' - ) { - // Check if options is valid - if ( - 'options' in parameter && - parameter.options !== null && - typeof parameter.options === 'object' - ) { - if ('store' in parameter.options && parameter.options?.store !== null) { - if (typeof parameter.options?.store === 'string') { - if (!isValidVCStore(parameter.options?.store)) { - console.error( - `Invalid saveVC Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, - ); - throw new Error( - `Invalid saveVC Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, - ); - } - } else if ( - Array.isArray(parameter.options?.store) && - parameter.options?.store.length > 0 - ) { - (parameter.options?.store as [string]).forEach((store) => { - if (!isValidVCStore(store)) { - console.error( - `Invalid saveVC Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, - ); - throw new Error( - `Invalid saveVC Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, - ); - } - }); - } else { - console.error( - 'Invalid saveVC Params passed. "options.store" is not in a valid format. It must either be a string or an array of strings', - ); - throw new Error( - 'Invalid saveVC Params passed. "options.store" is not in a valid format. It must either be a string or an array of strings', - ); - } - } - } - - // Check if accessToken is valid - if ('accessToken' in parameter) { - if ( - !( - parameter.accessToken !== null && - typeof parameter.accessToken === 'string' - ) - ) { - console.error( - 'Invalid saveVC Params passed. "accessToken" is not in a valid format. It must be a string', - ); - throw new Error( - 'Invalid saveVC Params passed. "accessToken" is not in a valid format. It must be a string', - ); - } - } - } -} - -/** - * Check Validation of Create VC request. - * - * @param params - Request params. - */ -export function isValidCreateVCRequest( - params: unknown, -): asserts params is CreateVCRequestParams { - if (params === null || _.isEmpty(params) || !('vcValue' in params)) { - console.error( - 'Invalid createVC Params passed. "vcValue" must be passed as a parameter', - ); - throw new Error( - 'Invalid createVC Params passed. "vcValue" must be passed as a parameter', - ); - } - - const parameter = params as CreateVCRequestParams; - - if ( - 'vcValue' in parameter && - parameter.vcValue !== null && - typeof parameter.vcValue === 'object' - ) { - // Check if vcKey is valid - if ('vcKey' in parameter) { - if (!(parameter.vcKey !== null && typeof parameter.vcKey === 'string')) { - console.error( - 'Invalid createVC Params passed. "vcKey" is not in a valid format. It must be a string', - ); - throw new Error( - 'Invalid createVC Params passed. "vcKey" is not in a valid format. It must be a string', - ); - } - } - - // Check if credTypes is valid - if ('credTypes' in parameter) { - if ( - !( - parameter.credTypes !== null && - Array.isArray(parameter.credTypes) && - parameter.credTypes.length > 0 - ) - ) { - console.error( - 'Invalid createVC Params passed. "credTypes" is not in a valid format. It must be an array of strings', - ); - throw new Error( - 'Invalid createVC Params passed. "credTypes" is not in a valid format. It must be an array of strings', - ); - } - } - - // Check if options is valid - if ( - 'options' in parameter && - parameter.options !== null && - typeof parameter.options === 'object' - ) { - if ('store' in parameter.options && parameter.options?.store !== null) { - if (typeof parameter.options?.store === 'string') { - if (!isValidVCStore(parameter.options?.store)) { - console.error( - `Invalid createVC Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, - ); - throw new Error( - `Invalid createVC Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, - ); - } - } else if ( - Array.isArray(parameter.options?.store) && - parameter.options?.store.length > 0 - ) { - (parameter.options?.store as [string]).forEach((store) => { - if (!isValidVCStore(store)) { - console.error( - `Invalid createVC Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, - ); - throw new Error( - `Invalid createVC Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, - ); - } - }); - } else { - console.error( - 'Invalid createVC Params passed. "options.store" is not in a valid format. It must either be a string or an array of strings', - ); - throw new Error( - 'Invalid createVC Params passed. "options.store" is not in a valid format. It must either be a string or an array of strings', - ); - } - } - } - - // Check if accessToken is valid - if ('accessToken' in parameter) { - if ( - !( - parameter.accessToken !== null && - typeof parameter.accessToken === 'string' - ) - ) { - console.error( - 'Invalid createVC Params passed. "accessToken" is not in a valid format. It must be a string', - ); - throw new Error( - 'Invalid createVC Params passed. "accessToken" is not in a valid format. It must be a string', - ); - } - } - } -} - -type VerifyVCRequestParams = { verifiableCredential: W3CVerifiableCredential }; - -/** - * Check Validation of Verify VC request. - * - * @param params - Request params. - */ -export function isValidVerifyVCRequest( - params: unknown, -): asserts params is VerifyVCRequestParams { - if ( - params === null || - _.isEmpty(params) || - !('verifiableCredential' in params) - ) { - console.error( - 'Invalid verifyVC Params passed. "verifiableCredential" must be passed as a parameter', - ); - throw new Error( - 'Invalid verifyVC Params passed. "verifiableCredential" must be passed as a parameter', - ); - } - - const parameter = params as VerifyVCRequestParams; - - if ( - 'verifiableCredential' in parameter && - (parameter.verifiableCredential === null || - typeof parameter.verifiableCredential !== 'object') - ) { - console.error( - 'Invalid verifyVC Params passed. "verifiableCredential" must be passed an object', - ); - throw new Error( - 'Invalid verifyVC Params passed. "verifiableCredential" must be passed an object', - ); - } -} - -/** - * Check Validation of Remove VC request. - * - * @param params - Request params. - */ -export function isValidRemoveVCRequest( - params: unknown, -): asserts params is IDataManagerDeleteArgs { - if (params === null || _.isEmpty(params) || !('id' in params)) { - console.error( - 'Invalid removeVC Params passed. "id" must be passed as a parameter', - ); - throw new Error( - 'Invalid removeVC Params passed. "id" must be passed as a parameter', - ); - } - - const parameter = params as IDataManagerDeleteArgs; - - // Check if id exists - if ('id' in parameter && parameter.id !== null) { - // Check if id is valid - if (!Array.isArray(parameter.id) && !(typeof parameter.id === 'string')) { - console.error( - 'Invalid removeVC Params passed. "id" must be a string or an array of strings', - ); - throw new Error( - 'Invalid removeVC Params passed. "id" must be a string or an array of strings', - ); - } - - // Check if options is valid - if ( - 'options' in parameter && - parameter.options !== null && - typeof parameter.options === 'object' - ) { - if ('store' in parameter.options && parameter.options?.store !== null) { - if (typeof parameter.options?.store === 'string') { - if (!isValidVCStore(parameter.options?.store)) { - console.error( - `Invalid removeVC Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, - ); - throw new Error( - `Invalid removeVC Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, - ); - } - } else if ( - Array.isArray(parameter.options?.store) && - parameter.options?.store.length > 0 - ) { - (parameter.options?.store as [string]).forEach((store) => { - if (!isValidVCStore(store)) { - console.error( - `Invalid removeVC Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, - ); - throw new Error( - `Invalid removeVC Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, - ); - } - }); - } else { - console.error( - 'Invalid removeVC Params passed. "options.store" is not in a valid format. It must either be a string or an array of strings', - ); - throw new Error( - 'Invalid removeVC Params passed. "options.store" is not in a valid format. It must either be a string or an array of strings', - ); - } - } - } - - // Check if accessToken is valid - if ('accessToken' in parameter) { - if ( - !( - parameter.accessToken !== null && - typeof parameter.accessToken === 'string' - ) - ) { - console.error( - 'Invalid removeVC Params passed. "accessToken" is not in a valid format. It must be a string', - ); - throw new Error( - 'Invalid removeVC Params passed. "accessToken" is not in a valid format. It must be a string', - ); - } - } - } -} - -/** - * Check Validation of Delete all VCs request. - * - * @param params - Request params. - */ -export function isValidDeleteAllVCsRequest( - params: unknown, -): asserts params is IDataManagerClearArgs { - const parameter = params as IDataManagerClearArgs; - - // Check if filter is valid - if ( - 'filter' in parameter && - parameter.filter !== null && - typeof parameter.filter === 'object' - ) { - if ( - !( - 'type' in parameter.filter && - parameter.filter?.type !== null && - typeof parameter.filter?.type === 'string' - ) - ) { - console.error( - 'Invalid deleteAllVCs Params passed. "filter.type" is either missing or is not a string', - ); - throw new Error( - 'Invalid deleteAllVCs Params passed. "filter.type" is either missing or is not a string', - ); - } - - if (!('filter' in parameter.filter && parameter.filter?.filter !== null)) { - console.error( - 'Invalid deleteAllVCs Params passed. "filter.filter" is missing', - ); - throw new Error( - 'Invalid deleteAllVCs Params passed. "filter.filter" is missing', - ); - } - } - - // Check if options is valid - if ( - 'options' in parameter && - parameter.options !== null && - typeof parameter.options === 'object' - ) { - if ('store' in parameter.options && parameter.options?.store !== null) { - if (typeof parameter.options?.store === 'string') { - if (!isValidVCStore(parameter.options?.store)) { - console.error( - `Invalid deleteAllVCs Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, - ); - throw new Error( - `Invalid deleteAllVCs Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, - ); - } - } else if ( - Array.isArray(parameter.options?.store) && - parameter.options?.store.length > 0 - ) { - (parameter.options?.store as [string]).forEach((store) => { - if (!isValidVCStore(store)) { - console.error( - `Invalid deleteAllVCs Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, - ); - throw new Error( - `Invalid deleteAllVCs Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, - ); - } - }); - } else { - console.error( - 'Invalid deleteAllVCs Params passed. "options.store" is not in a valid format. It must either be a string or an array of strings', - ); - throw new Error( - 'Invalid deleteAllVCs Params passed. "options.store" is not in a valid format. It must either be a string or an array of strings', - ); - } - } - - // Check if accessToken is valid - if ('accessToken' in parameter) { - if ( - !( - parameter.accessToken !== null && - typeof parameter.accessToken === 'string' - ) - ) { - console.error( - 'Invalid deleteAllVCs Params passed. "accessToken" is not in a valid format. It must be a string', - ); - throw new Error( - 'Invalid deleteAllVCs Params passed. "accessToken" is not in a valid format. It must be a string', - ); - } - } - } -} - -/** - * Check Validation of Create VP request. - * - * @param params - Request params. - */ -export function isValidCreateVPRequest( - params: unknown, -): asserts params is CreateVPRequestParams { - if (params === null || _.isEmpty(params)) { - console.error( - 'Invalid createVP Params passed. "vcIds" or "vcs" must be passed as a parameter', - ); - throw new Error( - 'Invalid createVP Params passed. "vcIds" or "vcs" must be passed as a parameter', - ); - } - - const parameter = params as CreateVPRequestParams; - - // Ensure that either vcIds or vcs is passed - if (!('vcIds' in parameter || 'vcs' in parameter)) { - console.error( - 'Invalid createVP Params passed. Either "vcIds" or "vcs" must be passed as parameters', - ); - throw new Error( - 'Invalid createVP Params passed. Either "vcIds" or "vcs" must be passed as parameters', - ); - } - - // Check if vcIds is valid - if ( - 'vcIds' in parameter && - parameter.vcs !== null && - Array.isArray(parameter.vcs) - ) { - (parameter.vcIds as [string]).forEach((vcId) => { - // Check if vcId is valid - if (!(vcId !== null && typeof vcId === 'string')) { - console.error( - `Invalid createVP Params passed. vcId: '${vcId}' is not in a valid format. It must be a string`, - ); - throw new Error( - `Invalid createVP Params passed. vcId: '${vcId}' is not in a valid format. It must be a string`, - ); - } - }); - - // Check if options is valid - if ( - 'options' in parameter && - parameter.options !== null && - typeof parameter.options === 'object' - ) { - if ('store' in parameter.options && parameter.options?.store !== null) { - if (typeof parameter.options?.store === 'string') { - if (!isValidVCStore(parameter.options?.store)) { - console.error( - `Invalid createVP Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, - ); - throw new Error( - `Invalid createVP Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, - ); - } - } else if ( - Array.isArray(parameter.options?.store) && - parameter.options?.store.length > 0 - ) { - (parameter.options?.store as [string]).forEach((store) => { - if (!isValidVCStore(store)) { - console.error( - `Invalid createVP Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, - ); - throw new Error( - `Invalid createVP Params passed. "options.store" is not a valid store. The valid store is one of the following: ${availableVCStores}`, - ); - } - }); - } else { - console.error( - 'Invalid createVP Params passed. "options.store" is not in a valid format. It must either be a string or an array of strings', - ); - throw new Error( - 'Invalid createVP Params passed. "options.store" is not in a valid format. It must either be a string or an array of strings', - ); - } - } - } - } - - // Check if vcs is valid - if ( - 'vcs' in parameter && - parameter.vcs !== null && - Array.isArray(parameter.vcs) - ) { - (parameter.vcs as [string]).forEach((vc) => { - // Check if vc is valid - if (!(vc !== null && typeof vc === 'object')) { - console.error( - 'Invalid createVP Params passed. One of the vcs that was passed is not in a valid format. It must be an object', - ); - throw new Error( - 'Invalid createVP Params passed. One of the vcs that was passed is not in a valid format. It must be an object', - ); - } - }); - } - - // Check if proofInfo is valid - if ( - 'proofInfo' in parameter && - parameter.proofInfo !== null && - typeof parameter.proofInfo === 'object' - ) { - // Check if proofFormat is valid - if ( - 'proofFormat' in parameter.proofInfo && - parameter.proofInfo.proofFormat !== null && - !isValidProofFormat(parameter.proofInfo.proofFormat as string) - ) { - console.error( - `Invalid createVP Params passed. Proofformat '${parameter.proofInfo.proofFormat}' not supported. The supported proof formats are: ${availableProofFormats}`, - ); - throw new Error( - `Invalid createVP Params passed. Proofformat '${parameter.proofInfo.proofFormat}' not supported. The supported proof formats are: ${availableProofFormats}`, - ); - } - - // Check if type is a string - if ( - 'type' in parameter.proofInfo && - parameter.proofInfo.type !== null && - typeof parameter.proofInfo.type !== 'string' - ) { - console.error( - 'Invalid createVP Params passed. "proofInfo.type" is not in a valid format. It must be a string', - ); - throw new Error( - 'Invalid createVP Params passed. "proofInfo.type" is not in a valid format. It must be a string', - ); - } - - // Check if domain is a string - if ( - 'domain' in parameter.proofInfo && - parameter.proofInfo.domain !== null && - typeof parameter.proofInfo.domain !== 'string' - ) { - console.error( - 'Invalid createVP Params passed. "proofInfo.domain" is not in a valid format. It must be a string', - ); - throw new Error( - 'Invalid createVP Params passed. "proofInfo.domain" is not in a valid format. It must be a string', - ); - } - - // Check if challenge is a string - if ( - 'challenge' in parameter.proofInfo && - parameter.proofInfo.challenge !== null && - typeof parameter.proofInfo.challenge !== 'string' - ) { - console.error( - 'Invalid createVP Params passed. "proofInfo.challenge" is not in a valid format. It must be a string', - ); - throw new Error( - 'Invalid createVP Params passed. "proofInfo.challenge" is not in a valid format. It must be a string', - ); - } - } -} - -type VerifyVPRequestParams = { verifiablePresentation: VerifiablePresentation }; - -/** - * Check Validation of Verify VP request. - * - * @param params - Request params. - */ -export function isValidVerifyVPRequest( - params: unknown, -): asserts params is VerifyVPRequestParams { - if ( - params === null || - _.isEmpty(params) || - !('verifiablePresentation' in params) - ) { - console.error( - 'Invalid verifyVP Params passed. "verifiablePresentation" must be passed as a parameter', - ); - throw new Error( - 'Invalid verifyVP Params passed. "verifiablePresentation" must be passed as a parameter', - ); - } - - const parameter = params as VerifyVPRequestParams; - - if ( - 'verifiablePresentation' in parameter && - (parameter.verifiablePresentation === null || - typeof parameter.verifiablePresentation !== 'object') - ) { - console.error( - 'Invalid verifyVP Params passed. "verifiablePresentation" must be an object', - ); - throw new Error( - 'Invalid verifyVP Params passed. "verifiablePresentation" must be an object', - ); - } -} - -/** - * Check Validation of Configure google request. - * - * @param params - Request params. - */ -export function isValidConfigueGoogleRequest( - params: unknown, -): asserts params is GoogleToken { - if (params === null || _.isEmpty(params) || !('accessToken' in params)) { - console.error( - 'Invalid configureGoogleAccount Params passed. "accessToken" must be passed as a parameter', - ); - throw new Error( - 'Invalid configureGoogleAccount Params passed. "accessToken" must be passed as a parameter', - ); - } - - const parameter = params as GoogleToken; - - if ( - 'accessToken' in parameter && - (parameter.accessToken === null || - typeof parameter.accessToken !== 'string') - ) { - console.error( - 'Invalid configureGoogleAccount Params passed. "accessToken" must be a string', - ); - throw new Error( - 'Invalid configureGoogleAccount Params passed. "accessToken" must be a string', - ); - } -} diff --git a/packages/hedera-identify-snap/packages/snap/src/veramo/accountImport.ts b/packages/hedera-identify-snap/packages/snap/src/veramo/accountImport.ts deleted file mode 100644 index 87195ca5..00000000 --- a/packages/hedera-identify-snap/packages/snap/src/veramo/accountImport.ts +++ /dev/null @@ -1,210 +0,0 @@ -/*- - * - * 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 { MetaMaskInpageProvider } from '@metamask/providers'; -import { IIdentifier, MinimalImportableKey } from '@veramo/core'; -import { getDidKeyIdentifier } from '../did/key/keyDidUtils'; -import { HederaServiceImpl } from '../hedera'; -import { getHederaNetwork, validHederaChainID } from '../hedera/config'; -import { - Account, - AccountViaPrivateKey, - IdentitySnapState, -} from '../interfaces'; -import { getCurrentNetwork } from '../snap/network'; -import { - getCurrentCoinType, - initAccountState, - updateState, -} from '../snap/state'; -import { generateWallet } from '../utils/keyPair'; -import { convertChainIdFromHex } from '../utils/network'; -import { getHederaAccountIfExists } from '../utils/params'; -import { getVeramoAgent } from './agent'; - -export const getCurrentMetamaskAccount = async (): Promise => { - const metamask = (window as any).ethereum as MetaMaskInpageProvider; - const accounts = (await metamask.request({ - method: 'eth_requestAccounts', - })) as string[]; - return accounts[0]; -}; - -/** - * Veramo Import metamask account. - * - * @param snap - SnapsGlobalObject. - * @param state - IdentitySnapState. - * @param metamask - MetaMaskInpageProvider. - * @param evmAddress - Ethereum address. - * @param accountViaPrivateKey - Account info if imported via private key directly(only used for Hedera accounts currently). - * @returns Account. - */ -export async function veramoImportMetaMaskAccount( - network: string, - state: IdentitySnapState, - metamask: MetaMaskInpageProvider, - evmAddress: string, - accountViaPrivateKey?: AccountViaPrivateKey, -): Promise { - const chainId = await getCurrentNetwork(); - - let privateKey: string; - let publicKey: string; - let address: string = evmAddress.toLowerCase(); - let snapAddress = ''; - let hederaAccountId = ''; - - if (accountViaPrivateKey) { - privateKey = accountViaPrivateKey.privateKey; - publicKey = accountViaPrivateKey.publicKey; - address = accountViaPrivateKey.address.toLowerCase(); - snapAddress = address; - if (validHederaChainID(chainId)) { - hederaAccountId = accountViaPrivateKey.extraData as string; - } - } else { - // Verify if the user is connected to the correct wallet - const connectedAddress = await getCurrentMetamaskAccount(); - if (address !== connectedAddress) { - console.log( - `You are currently connected to '${connectedAddress}. Please connect to the account '${address}' via Metamask first`, - ); - throw new Error( - `You are currently connected to '${connectedAddress}. Please connect to the account '${address}' via Metamask first`, - ); - } - - const res = await generateWallet(address); - - if (!res) { - console.log('Failed to generate snap wallet for DID operations'); - throw new Error('Failed to generate snap wallet for DID operations'); - } - privateKey = res.privateKey; - publicKey = res.publicKey; - snapAddress = res.address.toLowerCase(); - - if (validHederaChainID(chainId)) { - hederaAccountId = await getHederaAccountIfExists( - state, - undefined, - address, - ); - - if (!hederaAccountId) { - const hederaService = new HederaServiceImpl(getHederaNetwork(network)); - const result = await hederaService.getAccountFromEvmAddres(address); - if (!result) { - console.error( - `Could not retrieve hedera account info for address '${address}'. Please make sure this account is activated on Hedera '${getHederaNetwork(network)}'`, - ); - throw new Error( - `Could not retrieve hedera account info for address '${address}'. Please make sure this account is activated on Hedera '${getHederaNetwork(network)}'`, - ); - } - hederaAccountId = result.account; - } - } - } - - // Initialize if not there - const coinType = (await getCurrentCoinType()).toString(); - if (address && !(address in state.accountState[coinType])) { - console.log( - `The address ${address} has NOT yet been configured in the Identify Snap. Configuring now...`, - ); - await initAccountState(state, coinType, address); - } - // eslint-disable-next-line - state.accountState[coinType][address].extraData = hederaAccountId; - - const method = state.snapConfig.dApp.didMethod; - - let did = ''; - if (method === 'did:pkh') { - did = `did:pkh:eip155:${convertChainIdFromHex(chainId)}:${snapAddress}`; - } else if (method === 'did:key') { - did = `did:key:${await getDidKeyIdentifier(publicKey)}`; - } else if (method === 'did:hedera') { - did = `did:hedera:${hederaAccountId}`; - } - - if (!did) { - console.log('Failed to generate DID'); - throw new Error('Failed to generate DID'); - } - - // eslint-disable-next-line - state.currentAccount.metamaskAddress = address; - state.currentAccount.snapAddress = snapAddress; - state.currentAccount.method = method; - state.currentAccount.privateKey = privateKey; - state.currentAccount.publicKey = publicKey; - state.currentAccount.extraData = hederaAccountId; - - // Get Veramo agent - const agent = await getVeramoAgent(state); - const controllerKeyId = `metamask-${address}`; - console.log( - `Importing using did=${did}, provider=${method}, controllerKeyId=${controllerKeyId}...`, - ); - - let identifier: IIdentifier; - // Get identifier if it exists - try { - identifier = await agent.didManagerGet({ - did, - }); - } catch (error) { - try { - identifier = await agent.didManagerImport({ - did, - provider: method, - controllerKeyId, - keys: [ - { - kid: controllerKeyId, - type: 'Secp256k1', - kms: 'snap', - privateKeyHex: privateKey.split('0x')[1], - publicKeyHex: publicKey.split('0x')[1], - } as MinimalImportableKey, - ], - }); - } catch (e) { - console.log(`Error while creating identifier: ${(e as Error).message}`); - throw new Error( - `Error while creating identifier: ${(e as Error).message}`, - ); - } - } - await updateState(state); - console.log('Identifier imported successfully: ', identifier); - - return { - metamaskAddress: address, - snapAddress, - method, - identifier, - privateKey, - publicKey, - } as Account; -} diff --git a/packages/hedera-identify-snap/packages/snap/src/veramo/agent.ts b/packages/hedera-identify-snap/packages/snap/src/veramo/agent.ts index 72635576..d8bfcb5d 100644 --- a/packages/hedera-identify-snap/packages/snap/src/veramo/agent.ts +++ b/packages/hedera-identify-snap/packages/snap/src/veramo/agent.ts @@ -20,14 +20,13 @@ import { createAgent, - ICredentialIssuer, + ICredentialPlugin, IDataStore, IDIDManager, IKeyManager, IResolver, TAgent, } from '@veramo/core'; -import { CredentialIssuerEIP712 } from '@veramo/credential-eip712'; import { CredentialPlugin, W3cMessageHandler } from '@veramo/credential-w3c'; import { JwtMessageHandler } from '@veramo/did-jwt'; @@ -38,15 +37,16 @@ import { KeyManager } from '@veramo/key-manager'; import { KeyManagementSystem } from '@veramo/kms-local'; import { MessageHandler } from '@veramo/message-handler'; import { Resolver } from 'did-resolver'; -import { getDidKeyResolver as keyDidResolver } from '../did/key/keyDidResolver'; +import { getDidHederaResolver } from '../did/hedera/hederaDidResolver'; + import { AbstractDataStore, DataManager, IDataManager, } from '../plugins/veramo/verifiable-creds-manager'; -import { KeyDIDProvider } from '../did/key/keyDidProvider'; -import { IdentitySnapState } from '../interfaces'; +import { getDidKeyResolver, KeyDIDProvider } from '@veramo/did-provider-key'; +import { HederaDIDProvider } from '../did/hedera/hederaDidProvider'; import { GoogleDriveVCStore } from '../plugins/veramo/google-drive-data-store'; import { SnapDIDStore, @@ -54,13 +54,14 @@ import { SnapPrivateKeyStore, SnapVCStore, } from '../plugins/veramo/snap-data-store/src/snapDataStore'; +import { IdentifySnapState } from '../types/state'; export type Agent = TAgent< IKeyManager & IDIDManager & IResolver & IDataManager & - ICredentialIssuer & + ICredentialPlugin & IDataStore >; @@ -71,12 +72,26 @@ export type Agent = TAgent< * @param state - IdentitySnapState. * @returns Agent. */ -export async function getVeramoAgent(state: IdentitySnapState): Promise { +export async function getVeramoAgent(state: IdentifySnapState): Promise { const didProviders: Record = {}; const vcStorePlugins: Record = {}; + // Initialize DID providers didProviders['did:pkh'] = new PkhDIDProvider({ defaultKms: 'snap' }); didProviders['did:key'] = new KeyDIDProvider({ defaultKms: 'snap' }); + didProviders['did:hedera'] = new HederaDIDProvider({ + defaultKms: 'snap', + state, + }); + + // Prepare the resolver map dynamically + const resolverMap = { + ...getDidPkhResolver(), + ...getDidKeyResolver(), + ...getDidHederaResolver(), + }; + + // Initialize VC store plugins vcStorePlugins.snap = new SnapVCStore(state); vcStorePlugins.googleDrive = new GoogleDriveVCStore(state); @@ -85,7 +100,7 @@ export async function getVeramoAgent(state: IdentitySnapState): Promise { IDIDManager & IResolver & IDataManager & - ICredentialIssuer & + ICredentialPlugin & IDataStore >({ plugins: [ @@ -97,18 +112,14 @@ export async function getVeramoAgent(state: IdentitySnapState): Promise { }), new DIDManager({ store: new SnapDIDStore(state), - defaultProvider: 'metamask', + defaultProvider: 'did:pkh', providers: didProviders, }), new DIDResolverPlugin({ - resolver: new Resolver({ - ...getDidPkhResolver(), - ...keyDidResolver(), - }), + resolver: new Resolver(resolverMap), }), new DataManager({ store: vcStorePlugins }), new CredentialPlugin(), - new CredentialIssuerEIP712(), new MessageHandler({ messageHandlers: [new JwtMessageHandler(), new W3cMessageHandler()], }), diff --git a/packages/hedera-identify-snap/packages/snap/tests/rpc/account/getAccountInfo.spec.disabled.ts b/packages/hedera-identify-snap/packages/snap/tests/rpc/account/getAccountInfo.spec.disabled.ts deleted file mode 100644 index 0150257e..00000000 --- a/packages/hedera-identify-snap/packages/snap/tests/rpc/account/getAccountInfo.spec.disabled.ts +++ /dev/null @@ -1,149 +0,0 @@ -/*- - * - * 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. - * - */ - -/* eslint-disable no-restricted-globals */ - -import { MetaMaskInpageProvider } from '@metamask/providers'; -import { PublicAccountInfo } from 'src/interfaces'; -import { onRpcRequest } from '../../../src'; -import { - ETH_ADDRESS, - ETH_CHAIN_ID, - EVM_ACCOUNT, - getDefaultSnapState, - HEDERA_ACCOUNT, - HEDERA_CHAIN_ID, -} from '../../testUtils/constants'; -import { getRequestParams } from '../../testUtils/helper'; -import { buildMockSnap, SnapMock } from '../../testUtils/snap.mock'; - -describe('getAccountInfo', () => { - let snapMock: SnapMock; - let metamask: MetaMaskInpageProvider; - - describe('getAccountInfo Ethereum', () => { - beforeAll(async () => { - snapMock = buildMockSnap(ETH_CHAIN_ID, ETH_ADDRESS); - metamask = snapMock as unknown as MetaMaskInpageProvider; - }); - - beforeEach(async () => { - snapMock.rpcMocks.snap_dialog.mockReturnValue(true); - snapMock.rpcMocks.snap_manageState.mockReturnValue(getDefaultSnapState()); - snapMock.rpcMocks.snap_manageState('update', getDefaultSnapState()); - }); - - it('getAccountInfo', async () => { - const accountInfoRequestParams = getRequestParams('getAccountInfo', {}); - - const accountInfo = (await onRpcRequest({ - origin: 'tests', - request: accountInfoRequestParams as any, - })) as PublicAccountInfo; - expect(accountInfo.metamaskAddress).toBe(ETH_ADDRESS); - expect(accountInfo.accountID).toBeUndefined(); - - expect.assertions(2); - }); - - it('should throw error when connecting to hedera account with ethereum chainId', async () => { - const accountInfoRequestParams = getRequestParams('getAccountInfo', { - externalAccount: { - network: 'hedera', - data: { - accountId: HEDERA_ACCOUNT.accountId, - }, - }, - }); - - await expect( - onRpcRequest({ - origin: 'tests', - request: accountInfoRequestParams as any, - }), - ).rejects.toThrow(); - expect.assertions(1); - }); - }); - - describe('getAccountInfo hedera', () => { - beforeAll(async () => { - snapMock = buildMockSnap(HEDERA_CHAIN_ID.testnet, HEDERA_ACCOUNT.address); - metamask = snapMock as unknown as MetaMaskInpageProvider; - }); - - it('should set hedera external account info', async () => { - snapMock.rpcMocks.snap_dialog.mockReturnValue(HEDERA_ACCOUNT.privateKey); - - const accountInfoRequestParams = getRequestParams('getAccountInfo', { - externalAccount: { - network: 'hedera', - data: { - accountId: HEDERA_ACCOUNT.accountId, - }, - }, - }); - - const accountInfo = (await onRpcRequest({ - origin: 'tests', - request: accountInfoRequestParams as any, - })) as PublicAccountInfo; - expect(accountInfo.metamaskAddress).toBe(HEDERA_ACCOUNT.address); - console.log(JSON.stringify(accountInfo)); - expect(accountInfo.accountID).toBe(HEDERA_ACCOUNT.accountId); - expect.assertions(2); - }); - }); - - describe('getAccountInfo polygon', () => { - beforeAll(async () => { - snapMock = buildMockSnap(EVM_ACCOUNT.chainId, EVM_ACCOUNT.address); - metamask = snapMock as unknown as MetaMaskInpageProvider; - }); - - // eslint-disable-next-line - it.skip('should set evm external account info', async () => { - snapMock.rpcMocks.snap_dialog.mockReturnValue(EVM_ACCOUNT.privatekey); - - const externalEvmAccount = { - externalAccount: { - network: 'evm', - data: { - address: EVM_ACCOUNT.address, - }, - }, - }; - - const accountInfoRequestParams = getRequestParams( - 'getAccountInfo', - externalEvmAccount, - ); - - const accountInfo = (await onRpcRequest({ - origin: 'tests', - request: accountInfoRequestParams as any, - })) as PublicAccountInfo; - expect(accountInfo.metamaskAddress).toBe(EVM_ACCOUNT.address); - console.log(JSON.stringify(accountInfo)); - expect(accountInfo.snapAddress).toBe(EVM_ACCOUNT.address); - expect.assertions(2); - }); - }); -}); diff --git a/packages/hedera-identify-snap/packages/snap/tests/rpc/did/getAvailableDIDMethods.spec.ts b/packages/hedera-identify-snap/packages/snap/tests/rpc/did/getAvailableDIDMethods.spec.ts deleted file mode 100644 index e8762868..00000000 --- a/packages/hedera-identify-snap/packages/snap/tests/rpc/did/getAvailableDIDMethods.spec.ts +++ /dev/null @@ -1,39 +0,0 @@ -/*- - * - * 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 { getAvailableDIDMethods } from '../../../src/rpc/did/getAvailableDIDMethods'; - -describe('GetAvailableMethods', () => { - it('should return all available methods', async () => { - // get - const getAvailableMethodsResult = getAvailableDIDMethods(); - expect(getAvailableMethodsResult.length).toBeGreaterThanOrEqual(1); - - expect.assertions(1); - }); - - it('should contains did:pkh in available methods', async () => { - // get - const getAvailableMethodsResult = getAvailableDIDMethods(); - expect(getAvailableMethodsResult).toContain('did:pkh'); - - expect.assertions(1); - }); -}); diff --git a/packages/hedera-identify-snap/packages/snap/tests/rpc/did/getCurrentDIDMethod.spec.disabled.ts b/packages/hedera-identify-snap/packages/snap/tests/rpc/did/getCurrentDIDMethod.spec.disabled.ts deleted file mode 100644 index 278b3ecd..00000000 --- a/packages/hedera-identify-snap/packages/snap/tests/rpc/did/getCurrentDIDMethod.spec.disabled.ts +++ /dev/null @@ -1,61 +0,0 @@ -/*- - * - * 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. - * - */ - -/* eslint-disable no-restricted-globals */ - -import { MetaMaskInpageProvider } from '@metamask/providers'; -import { onRpcRequest } from '../../../src'; -import { - ETH_ADDRESS, - ETH_CHAIN_ID, - getDefaultSnapState, -} from '../../testUtils/constants'; -import { getRequestParams } from '../../testUtils/helper'; -import { SnapMock, buildMockSnap } from '../../testUtils/snap.mock'; - -describe('GetCurrentDIDMethod', () => { - let snapMock: SnapMock; - let metamask: MetaMaskInpageProvider; - - beforeAll(async () => { - snapMock = buildMockSnap(ETH_CHAIN_ID, ETH_ADDRESS); - metamask = snapMock as unknown as MetaMaskInpageProvider; - }); - - beforeEach(async () => { - snapMock.rpcMocks.snap_dialog.mockReturnValue(true); - snapMock.rpcMocks.snap_manageState.mockReturnValue(getDefaultSnapState()); - snapMock.rpcMocks.snap_manageState('update', getDefaultSnapState()); - }); - - it('should return the current did method that the account is using', async () => { - const getCurrentDIDMethodRequestParams = getRequestParams( - 'getCurrentDIDMethod', - {}, - ); - - const request = onRpcRequest({ - origin: 'tests', - request: getCurrentDIDMethodRequestParams as any, - }); - await expect(request).resolves.toBe('did:pkh'); - expect.assertions(1); - }); -}); diff --git a/packages/hedera-identify-snap/packages/snap/tests/rpc/did/resolveDID.spec.disabled.ts b/packages/hedera-identify-snap/packages/snap/tests/rpc/did/resolveDID.spec.disabled.ts deleted file mode 100644 index a8c28dc1..00000000 --- a/packages/hedera-identify-snap/packages/snap/tests/rpc/did/resolveDID.spec.disabled.ts +++ /dev/null @@ -1,73 +0,0 @@ -/*- - * - * 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. - * - */ - -/* eslint-disable no-restricted-globals */ - -import { MetaMaskInpageProvider } from '@metamask/providers'; -import { DIDResolutionResult } from 'did-resolver'; -import { PublicAccountInfo } from 'src/interfaces'; -import { onRpcRequest } from '../../../src'; -import { - ETH_ADDRESS, - ETH_CHAIN_ID, - exampleDIDPkh, - getDefaultSnapState, -} from '../../testUtils/constants'; -import { getRequestParams } from '../../testUtils/helper'; -import { SnapMock, buildMockSnap } from '../../testUtils/snap.mock'; - -describe('resolveDID', () => { - let snapMock: SnapMock; - let metamask: MetaMaskInpageProvider; - - let currentDID = exampleDIDPkh; - - beforeAll(async () => { - snapMock = buildMockSnap(ETH_CHAIN_ID, ETH_ADDRESS); - metamask = snapMock as unknown as MetaMaskInpageProvider; - }); - - beforeEach(async () => { - snapMock.rpcMocks.snap_dialog.mockReturnValue(true); - snapMock.rpcMocks.snap_manageState.mockReturnValue(getDefaultSnapState()); - snapMock.rpcMocks.snap_manageState('update', getDefaultSnapState()); - - const getAccountInfoRequestParams = getRequestParams('getAccountInfo', {}); - - const accountInfo = (await onRpcRequest({ - origin: 'tests', - request: getAccountInfoRequestParams as any, - })) as PublicAccountInfo; - - currentDID = accountInfo.did; - }); - - it('should succeed returning current did resolved', async () => { - const resolveDIDRequestParams = getRequestParams('resolveDID', {}); - - const resolvedDID = (await onRpcRequest({ - origin: 'tests', - request: resolveDIDRequestParams as any, - })) as any; - - expect(resolvedDID.didDocument?.id).toBe(currentDID); - expect.assertions(1); - }); -}); diff --git a/packages/hedera-identify-snap/packages/snap/tests/rpc/did/switchDIDMethod.spec.disabled.ts b/packages/hedera-identify-snap/packages/snap/tests/rpc/did/switchDIDMethod.spec.disabled.ts deleted file mode 100644 index a7ceae0a..00000000 --- a/packages/hedera-identify-snap/packages/snap/tests/rpc/did/switchDIDMethod.spec.disabled.ts +++ /dev/null @@ -1,122 +0,0 @@ -/*- - * - * 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. - * - */ - -/* eslint-disable no-restricted-globals */ - -import { MetaMaskInpageProvider } from '@metamask/providers'; -import { onRpcRequest } from '../../../src'; -import { getAccountStateByCoinType } from '../../../src/snap/state'; -import { - ETH_ADDRESS, - ETH_CHAIN_ID, - getDefaultSnapState, -} from '../../testUtils/constants'; -import { getRequestParams } from '../../testUtils/helper'; -import { SnapMock, buildMockSnap } from '../../testUtils/snap.mock'; - -describe('SwitchDIDMethod', () => { - let snapMock: SnapMock; - let metamask: MetaMaskInpageProvider; - - beforeAll(async () => { - snapMock = buildMockSnap(ETH_CHAIN_ID, ETH_ADDRESS); - metamask = snapMock as unknown as MetaMaskInpageProvider; - }); - - beforeEach(async () => { - snapMock.rpcMocks.snap_dialog.mockReturnValue(true); - snapMock.rpcMocks.snap_manageState.mockReturnValue(getDefaultSnapState()); - snapMock.rpcMocks.snap_manageState('update', getDefaultSnapState()); - }); - - // enable test when there's more than a method - // eslint-disable-next-line - it.skip('should change snap state when switch to did:pkh methods', async () => { - const snapState = getDefaultSnapState(); - const accountType = await getAccountStateByCoinType(snapState, ETH_ADDRESS); - snapState.snapConfig.dApp.didMethod = 'did:key'; - - snapState.accountState['60'][ETH_ADDRESS] = accountType; - - snapMock.rpcMocks.snap_manageState.mockReturnValue(snapState); - - const switchDIDMethodRequestParams = getRequestParams('switchDIDMethod', { - didMethod: 'did:pkh', - }); - - const request = onRpcRequest({ - origin: 'tests', - request: switchDIDMethodRequestParams as any, - }); - await expect(request).resolves.toBe(true); - expect.assertions(1); - }); - - it('should do nothing when changing method to current did:pkh method', async () => { - const switchDIDMethodRequestParams = getRequestParams('switchDIDMethod', { - didMethod: 'did:pkh', - }); - - const request = onRpcRequest({ - origin: 'tests', - request: switchDIDMethodRequestParams as any, - }); - await expect(request).resolves.toBe(true); - expect.assertions(1); - }); - - it('should throw error when switch to invalid method', async () => { - const switchDIDMethodRequestParams = getRequestParams('switchDIDMethod', { - didMethod: 'did:inv', - }); - - await expect( - onRpcRequest({ - origin: 'tests', - request: switchDIDMethodRequestParams as any, - }), - ).rejects.toThrow(); - - expect.assertions(1); - }); - - it('should not switch method when user rejects', async () => { - snapMock.rpcMocks.snap_dialog.mockReturnValue(false); - - const snapState = getDefaultSnapState(); - const accountType = await getAccountStateByCoinType(snapState, ETH_ADDRESS); - snapState.snapConfig.dApp.didMethod = 'did:key'; - - snapState.accountState['60'][ETH_ADDRESS] = accountType; - - snapMock.rpcMocks.snap_manageState.mockReturnValue(snapState); - - const switchDIDMethodRequestParams = getRequestParams('switchDIDMethod', { - didMethod: 'did:pkh', - }); - - const request = onRpcRequest({ - origin: 'tests', - request: switchDIDMethodRequestParams as any, - }); - await expect(request).rejects.toThrow(); - expect.assertions(1); - }); -}); diff --git a/packages/hedera-identify-snap/packages/snap/tests/rpc/gdrive/configureGoogleAccount.spec.ts b/packages/hedera-identify-snap/packages/snap/tests/rpc/gdrive/configureGoogleAccount.spec.ts deleted file mode 100644 index bce44b9a..00000000 --- a/packages/hedera-identify-snap/packages/snap/tests/rpc/gdrive/configureGoogleAccount.spec.ts +++ /dev/null @@ -1,34 +0,0 @@ -/*- - * - * 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. - * - */ - -/* eslint-disable */ -import { MetaMaskInpageProvider } from '@metamask/providers'; -import { GoogleToken, IdentitySnapParams } from '../../../src/interfaces'; -import { SnapMock } from '../../testUtils/snap.mock'; - -describe('ConfigureGoogleAccount', () => { - let identitySnapParams: IdentitySnapParams; - let googleToken: GoogleToken; - let snapState: GoogleToken; - let snapMock: SnapMock; - let metamask: MetaMaskInpageProvider; - - it('should return true if configured properly', async () => {}); -}); diff --git a/packages/hedera-identify-snap/packages/snap/tests/rpc/hedera/connectHederaAccount.spec.ts b/packages/hedera-identify-snap/packages/snap/tests/rpc/hedera/connectHederaAccount.spec.ts deleted file mode 100644 index ab6316b5..00000000 --- a/packages/hedera-identify-snap/packages/snap/tests/rpc/hedera/connectHederaAccount.spec.ts +++ /dev/null @@ -1,34 +0,0 @@ -/*- - * - * 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. - * - */ - -/* eslint-disable */ -import { MetaMaskInpageProvider } from '@metamask/providers'; -import { IdentitySnapParams, IdentitySnapState } from '../../../src/interfaces'; -import { SnapMock } from '../../testUtils/snap.mock'; - -describe('ConnectHederaAccount', () => { - let identitySnapParams: IdentitySnapParams; - let snapState: IdentitySnapState; - let snapMock: SnapMock; - let metamask: MetaMaskInpageProvider; - let accountId: '0.0.15215'; - - it('should return true', async () => {}); -}); diff --git a/packages/hedera-identify-snap/packages/snap/tests/rpc/snap/togglePopups.spec.ts b/packages/hedera-identify-snap/packages/snap/tests/rpc/snap/togglePopups.spec.ts deleted file mode 100644 index ae520cdc..00000000 --- a/packages/hedera-identify-snap/packages/snap/tests/rpc/snap/togglePopups.spec.ts +++ /dev/null @@ -1,33 +0,0 @@ -/*- - * - * 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. - * - */ - -/* eslint-disable */ -import { MetaMaskInpageProvider } from '@metamask/providers'; -import { IdentitySnapParams, IdentitySnapState } from '../../../src/interfaces'; -import { SnapMock } from '../../testUtils/snap.mock'; - -describe('TogglePopups', () => { - let identitySnapParams: IdentitySnapParams; - let snapState: IdentitySnapState; - let snapMock: SnapMock; - let metamask: MetaMaskInpageProvider; - - it('should return true', async () => {}); -}); diff --git a/packages/hedera-identify-snap/packages/snap/tests/rpc/vc/createVC.spec.disabled.ts b/packages/hedera-identify-snap/packages/snap/tests/rpc/vc/createVC.spec.disabled.ts deleted file mode 100644 index 497bdbdb..00000000 --- a/packages/hedera-identify-snap/packages/snap/tests/rpc/vc/createVC.spec.disabled.ts +++ /dev/null @@ -1,84 +0,0 @@ -/*- - * - * 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. - * - */ - -/* eslint-disable no-restricted-globals */ - -import { MetaMaskInpageProvider } from '@metamask/providers'; -import { CreateVCResponseResult } from 'src/types/params'; -import { onRpcRequest } from '../../../src'; -import { getDefaultSnapState } from '../../testUtils/constants'; -import { getRequestParams } from '../../testUtils/helper'; -import { createMockSnap, SnapMock } from '../../testUtils/snap.mock'; - -describe('createVC', () => { - let snapMock: SnapMock; - let metamask: MetaMaskInpageProvider; - - beforeAll(async () => { - snapMock = createMockSnap(); - metamask = snapMock as unknown as MetaMaskInpageProvider; - }); - - beforeEach(async () => { - snapMock.rpcMocks.snap_dialog.mockReturnValue(true); - snapMock.rpcMocks.snap_manageState.mockReturnValue(getDefaultSnapState()); - snapMock.rpcMocks.snap_manageState('update', getDefaultSnapState()); - snapMock.rpcMocks.eth_chainId.mockReturnValue('0x1'); - }); - - it('should create VC', async () => { - const createVcRequest = getRequestParams('createVC', { - vcValue: { prop: 10 }, - credTypes: ['Login'], - }); - - const createVcResponse = (await onRpcRequest({ - origin: 'tests', - request: createVcRequest as any, - })) as CreateVCResponseResult; - expect(createVcResponse.data).not.toBeUndefined(); - expect.assertions(1); - }); - - it('should throw exception if user refuses confirmation', async () => { - snapMock.rpcMocks.snap_dialog.mockReturnValue(false); - - const createVcRequest = getRequestParams('createVC', { - vcValue: { prop: 20 }, - credTypes: ['Login'], - }); - await expect( - onRpcRequest({ origin: 'tests', request: createVcRequest as any }), - ).rejects.toThrow(); - expect.assertions(1); - }); - - it('should throw exception if parameters invalid', async () => { - snapMock.rpcMocks.snap_dialog.mockReturnValue(true); - - const createVcRequest = getRequestParams('createVC', { - errorParam: {}, - }); - await expect( - onRpcRequest({ origin: 'tests', request: createVcRequest as any }), - ).rejects.toThrow(); - expect.assertions(1); - }); -}); diff --git a/packages/hedera-identify-snap/packages/snap/tests/rpc/vc/createVP.spec.disabled.ts b/packages/hedera-identify-snap/packages/snap/tests/rpc/vc/createVP.spec.disabled.ts deleted file mode 100644 index 88e3a222..00000000 --- a/packages/hedera-identify-snap/packages/snap/tests/rpc/vc/createVP.spec.disabled.ts +++ /dev/null @@ -1,117 +0,0 @@ -/*- - * - * 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. - * - */ - -/* eslint-disable no-restricted-globals */ - -import { MetaMaskInpageProvider } from '@metamask/providers'; -import { VerifiablePresentation } from '@veramo/core'; -import { CreateVCResponseResult } from 'src/types/params'; -import { onRpcRequest } from '../../../src'; -import { - ETH_ADDRESS, - ETH_CHAIN_ID, - getDefaultSnapState, -} from '../../testUtils/constants'; -import { getRequestParams } from '../../testUtils/helper'; -import { SnapMock, buildMockSnap } from '../../testUtils/snap.mock'; - -describe('createVP', () => { - let snapMock: SnapMock; - let metamask: MetaMaskInpageProvider; - - const vcIds: string[] = []; - - beforeAll(async () => { - snapMock = buildMockSnap(ETH_CHAIN_ID, ETH_ADDRESS); - metamask = snapMock as unknown as MetaMaskInpageProvider; - }); - - beforeEach(async () => { - snapMock.rpcMocks.snap_dialog.mockReturnValue(true); - snapMock.rpcMocks.snap_manageState.mockReturnValue(getDefaultSnapState()); - snapMock.rpcMocks.snap_manageState('update', getDefaultSnapState()); - - const createVcRequest1 = getRequestParams('createVC', { - vcValue: { prop: 10 }, - credTypes: ['Login'], - }); - - const createVcRequest2 = getRequestParams('createVC', { - vcValue: { prop: 20 }, - credTypes: ['NotLogin'], - }); - - const createVcResponse1: CreateVCResponseResult = (await onRpcRequest({ - origin: 'tests', - request: createVcRequest1 as any, - })) as CreateVCResponseResult; - const createVcResponse2: CreateVCResponseResult = (await onRpcRequest({ - origin: 'tests', - request: createVcRequest2 as any, - })) as CreateVCResponseResult; - - vcIds.push(createVcResponse1.metadata.id); - vcIds.push(createVcResponse2.metadata.id); - }); - - it('should succeed creating VP from 1 VC', async () => { - const createVpRequest = getRequestParams('createVP', { - vcIds: [vcIds[0]], - }); - - const presentation = (await onRpcRequest({ - origin: 'tests', - request: createVpRequest as any, - })) as VerifiablePresentation; - expect(presentation).not.toBeUndefined(); - expect.assertions(1); - }); - - it('should succeed creating VP from 2 VCs', async () => { - const createVpRequest = getRequestParams('createVP', { - vcIds, - }); - - const presentation = (await onRpcRequest({ - origin: 'tests', - request: createVpRequest as any, - })) as VerifiablePresentation; - expect(presentation).not.toBeUndefined(); - expect(presentation.verifiableCredential?.length).toBe(2); - expect.assertions(2); - }); - - // eslint-disable-next-line - it.skip('should throw error when user rejects confirm', async () => { - snapMock.rpcMocks.snap_dialog.mockReturnValue(false); - - const createVpRequest = getRequestParams('createVP', { - vcs: [vcIds[0]], - }); - - await expect( - onRpcRequest({ - origin: 'tests', - request: createVpRequest as any, - }), - ).rejects.toThrow(); - expect.assertions(1); - }); -}); diff --git a/packages/hedera-identify-snap/packages/snap/tests/rpc/vc/deleteAllVCs.spec.disabled.ts b/packages/hedera-identify-snap/packages/snap/tests/rpc/vc/deleteAllVCs.spec.disabled.ts deleted file mode 100644 index 500fb253..00000000 --- a/packages/hedera-identify-snap/packages/snap/tests/rpc/vc/deleteAllVCs.spec.disabled.ts +++ /dev/null @@ -1,83 +0,0 @@ -/*- - * - * 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. - * - */ - -/* eslint-disable no-restricted-globals */ - -import { MetaMaskInpageProvider } from '@metamask/providers'; -import { onRpcRequest } from '../../../src'; -import { getDefaultSnapState } from '../../testUtils/constants'; -import { getRequestParams } from '../../testUtils/helper'; -import { createMockSnap, SnapMock } from '../../testUtils/snap.mock'; - -describe('delete all VCs', () => { - let snapMock: SnapMock; - let metamask: MetaMaskInpageProvider; - - beforeAll(async () => { - snapMock = createMockSnap(); - metamask = snapMock as unknown as MetaMaskInpageProvider; - }); - - beforeEach(async () => { - snapMock.rpcMocks.snap_dialog.mockReturnValue(true); - snapMock.rpcMocks.snap_manageState.mockReturnValue(getDefaultSnapState()); - snapMock.rpcMocks.snap_manageState('update', getDefaultSnapState()); - snapMock.rpcMocks.eth_chainId.mockReturnValue('0x1'); - - const createVcRequest1 = getRequestParams('createVC', { - vcValue: { prop: 10 }, - credTypes: ['Login'], - }); - - const createVcRequest2 = getRequestParams('createVC', { - vcValue: { prop: 20 }, - credTypes: ['NotLogin'], - }); - - await onRpcRequest({ origin: 'tests', request: createVcRequest1 as any }); - await onRpcRequest({ origin: 'tests', request: createVcRequest2 as any }); - }); - - it('should delete all VC', async () => { - const deleteAllVcsRequest = getRequestParams('deleteAllVCs', { - options: { store: 'snap' }, - }); - - await expect( - onRpcRequest({ origin: 'tests', request: deleteAllVcsRequest as any }), - ).resolves.not.toBeUndefined(); - - expect.assertions(1); - }); - - it('should throw exception if user refused confirmation', async () => { - snapMock.rpcMocks.snap_dialog.mockReturnValue(false); - - const deleteAllVcsRequest = getRequestParams('deleteAllVCs', { - options: { store: 'snap' }, - }); - - await expect( - onRpcRequest({ origin: 'tests', request: deleteAllVcsRequest as any }), - ).rejects.toThrow(); - - expect.assertions(1); - }); -}); diff --git a/packages/hedera-identify-snap/packages/snap/tests/rpc/vc/getSupportedProofFormats.spec.ts b/packages/hedera-identify-snap/packages/snap/tests/rpc/vc/getSupportedProofFormats.spec.ts deleted file mode 100644 index 08daddda..00000000 --- a/packages/hedera-identify-snap/packages/snap/tests/rpc/vc/getSupportedProofFormats.spec.ts +++ /dev/null @@ -1,34 +0,0 @@ -/*- - * - * 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 { getSupportedProofFormats } from '../../../src/rpc/vc/getSupportedProofFormats'; - -describe('GetSupportedProofFormats', () => { - it('should return all supported proof formats', async () => { - // get - const getAvailableMethodsResult = getSupportedProofFormats(); - expect(getAvailableMethodsResult.length).toBe(3); - expect(getAvailableMethodsResult).toContain('jwt'); - expect(getAvailableMethodsResult).toContain('lds'); - expect(getAvailableMethodsResult).toContain('EthereumEip712Signature2021'); - - expect.assertions(4); - }); -}); diff --git a/packages/hedera-identify-snap/packages/snap/tests/rpc/vc/getVCs.spec.disabled.ts b/packages/hedera-identify-snap/packages/snap/tests/rpc/vc/getVCs.spec.disabled.ts deleted file mode 100644 index 5ee97197..00000000 --- a/packages/hedera-identify-snap/packages/snap/tests/rpc/vc/getVCs.spec.disabled.ts +++ /dev/null @@ -1,141 +0,0 @@ -/*- - * - * 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. - * - */ - -/* eslint-disable no-restricted-globals */ - -import { MetaMaskInpageProvider } from '@metamask/providers'; -import { IDataManagerQueryResult } from 'src/plugins/veramo/verifiable-creds-manager'; -import { CreateVCResponseResult } from 'src/types/params'; -import { onRpcRequest } from '../../../src'; -import { getRequestParams } from '../../testUtils/helper'; -// import { connectHederaAccount } from '../../../src/rpc/hedera/connectHederaAccount'; -import { getDefaultSnapState } from '../../testUtils/constants'; -import { createMockSnap, SnapMock } from '../../testUtils/snap.mock'; - -describe('getVCs', () => { - let snapMock: SnapMock; - let metamask: MetaMaskInpageProvider; - - beforeAll(async () => { - snapMock = createMockSnap(); - metamask = snapMock as unknown as MetaMaskInpageProvider; - }); - - beforeEach(async () => { - snapMock.rpcMocks.snap_dialog.mockReturnValue(true); - snapMock.rpcMocks.snap_manageState.mockReturnValue(getDefaultSnapState()); - snapMock.rpcMocks.snap_manageState('update', getDefaultSnapState()); - snapMock.rpcMocks.eth_chainId.mockReturnValue('0x1'); - - const createVcRequest1 = getRequestParams('createVC', { - vcValue: { prop: 10 }, - credTypes: ['Login'], - }); - - const createVcRequest2 = getRequestParams('createVC', { - vcValue: { prop: 20 }, - credTypes: ['NotLogin'], - }); - - await onRpcRequest({ origin: 'tests', request: createVcRequest1 as any }); - await onRpcRequest({ origin: 'tests', request: createVcRequest2 as any }); - }); - - it('should succeed returning VCS without filter', async () => { - const getVcRequest = getRequestParams('getVCs', {}); - const vcsReturned: IDataManagerQueryResult[] = (await onRpcRequest({ - origin: 'tests', - request: getVcRequest as any, - })) as IDataManagerQueryResult[]; - - expect(vcsReturned.length).toBe(2); - - expect.assertions(1); - }); - - it('should filter Login Type VCs', async () => { - snapMock.rpcMocks.snap_dialog.mockReturnValue(true); - - const getVcRequest = getRequestParams('getVCs', { - options: {}, - filter: { type: 'vcType', filter: 'Login' }, - }); - - const vcsReturned: IDataManagerQueryResult[] = (await onRpcRequest({ - origin: 'tests', - request: getVcRequest as any, - })) as IDataManagerQueryResult[]; - - expect(vcsReturned.length).toBe(1); - expect.assertions(1); - }); - - it('should return empty if filter is invalid', async () => { - snapMock.rpcMocks.snap_dialog.mockReturnValue(true); - - const getVcRequest = getRequestParams('getVCs', { - options: {}, - filter: { type: 'invalidFilter', filter: 'Login' }, - }); - - await expect( - onRpcRequest({ origin: 'tests', request: getVcRequest as any }), - ).resolves.toStrictEqual([]); - - expect.assertions(1); - }); - - it('should filter VCs by id', async () => { - snapMock.rpcMocks.snap_dialog.mockReturnValue(true); - - const createVcRequest = getRequestParams('createVC', { - vcValue: { prop: 30 }, - }); - - const createVcResponse: CreateVCResponseResult = (await onRpcRequest({ - origin: 'tests', - request: createVcRequest as any, - })) as CreateVCResponseResult; - - const getVcRequest = getRequestParams('getVCs', { - options: {}, - filter: { type: 'id', filter: createVcResponse.metadata.id }, - }); - - const vcsReturned: IDataManagerQueryResult[] = (await onRpcRequest({ - origin: 'tests', - request: getVcRequest as any, - })) as IDataManagerQueryResult[]; - - expect(vcsReturned.length).toBe(1); - expect.assertions(1); - }); - - it('should return empty if user rejects confirm', async () => { - snapMock.rpcMocks.snap_dialog.mockReturnValue(false); - - const getVcRequest = getRequestParams('getVCs', {}); - await expect( - onRpcRequest({ origin: 'tests', request: getVcRequest as any }), - ).resolves.toStrictEqual([]); - - expect.assertions(1); - }); -}); diff --git a/packages/hedera-identify-snap/packages/snap/tests/rpc/vc/removeVC.spec.disabled.ts b/packages/hedera-identify-snap/packages/snap/tests/rpc/vc/removeVC.spec.disabled.ts deleted file mode 100644 index d13eb677..00000000 --- a/packages/hedera-identify-snap/packages/snap/tests/rpc/vc/removeVC.spec.disabled.ts +++ /dev/null @@ -1,120 +0,0 @@ -/*- - * - * 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. - * - */ - -/* eslint-disable no-restricted-globals */ - -import { MetaMaskInpageProvider } from '@metamask/providers'; -import { W3CVerifiableCredential } from '@veramo/core'; -import { - IDataManagerDeleteResult, - IDataManagerQueryResult, -} from 'src/plugins/veramo/verifiable-creds-manager'; -import { CreateVCResponseResult } from 'src/types/params'; -import { onRpcRequest } from '../../../src'; -import { - ETH_ADDRESS, - ETH_CHAIN_ID, - getDefaultSnapState, -} from '../../testUtils/constants'; -import { getRequestParams } from '../../testUtils/helper'; -import { SnapMock, buildMockSnap } from '../../testUtils/snap.mock'; - -describe('RemoveVC', () => { - let snapMock: SnapMock; - let metamask: MetaMaskInpageProvider; - - const vcs: W3CVerifiableCredential[] = []; - - beforeAll(async () => { - snapMock = buildMockSnap(ETH_CHAIN_ID, ETH_ADDRESS); - metamask = snapMock as unknown as MetaMaskInpageProvider; - }); - - beforeEach(async () => { - snapMock.rpcMocks.snap_dialog.mockReturnValue(true); - snapMock.rpcMocks.snap_manageState.mockReturnValue(getDefaultSnapState()); - snapMock.rpcMocks.snap_manageState('update', getDefaultSnapState()); - - const createVcRequest1 = getRequestParams('createVC', { - vcValue: { prop: 10 }, - credTypes: ['Login'], - }); - - const createVcRequest2 = getRequestParams('createVC', { - vcValue: { prop: 20 }, - credTypes: ['NotLogin'], - }); - - const createVcResponse1: CreateVCResponseResult = (await onRpcRequest({ - origin: 'tests', - request: createVcRequest1 as any, - })) as CreateVCResponseResult; - const createVcResponse2: CreateVCResponseResult = (await onRpcRequest({ - origin: 'tests', - request: createVcRequest2 as any, - })) as CreateVCResponseResult; - - vcs.push(createVcResponse1.metadata.id); - vcs.push(createVcResponse2.metadata.id); - }); - - it('should remove VC', async () => { - const removeVcRequest = getRequestParams('removeVC', { - id: vcs[0], - options: {}, - }); - - const removeVcResponse = (await onRpcRequest({ - origin: 'tests', - request: removeVcRequest as any, - })) as IDataManagerDeleteResult[]; - expect(removeVcResponse.length).toBe(1); - - // redo request - const getVcRequest = getRequestParams('getVCs', {}); - const vcsReturned: IDataManagerQueryResult[] = (await onRpcRequest({ - origin: 'tests', - request: getVcRequest as any, - })) as IDataManagerQueryResult[]; - - expect(vcsReturned.length).toBe(1); - - expect.assertions(2); - }); - - it('should throw exception if user refused confirmation', async () => { - // Setup - snapMock.rpcMocks.snap_dialog.mockReturnValue(false); - - const removeVcRequest = getRequestParams('removeVC', { - id: vcs[0], - options: {}, - }); - - await expect( - onRpcRequest({ - origin: 'tests', - request: removeVcRequest as any, - }), - ).rejects.toThrow(); - - expect.assertions(1); - }); -}); diff --git a/packages/hedera-identify-snap/packages/snap/tests/rpc/vc/saveVC.spec.disabled.ts b/packages/hedera-identify-snap/packages/snap/tests/rpc/vc/saveVC.spec.disabled.ts deleted file mode 100644 index f879f15c..00000000 --- a/packages/hedera-identify-snap/packages/snap/tests/rpc/vc/saveVC.spec.disabled.ts +++ /dev/null @@ -1,139 +0,0 @@ -/*- - * - * 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. - * - */ - -/* eslint-disable */ -import { MetaMaskInpageProvider } from '@metamask/providers'; -import { onRpcRequest } from '../../../src'; -import { getRequestParams } from '../../../tests/testUtils/helper'; -import { getDefaultSnapState } from '../../testUtils/constants'; -import { createMockSnap, SnapMock } from '../../testUtils/snap.mock'; - -describe('saveVC', () => { - let snapMock: SnapMock; - let metamask: MetaMaskInpageProvider; - - beforeAll(async () => { - snapMock = createMockSnap(); - metamask = snapMock as unknown as MetaMaskInpageProvider; - }); - - beforeEach(async () => { - snapMock.rpcMocks.snap_dialog.mockReturnValue(true); - snapMock.rpcMocks.snap_manageState.mockReturnValue(getDefaultSnapState()); - snapMock.rpcMocks.snap_manageState('update', getDefaultSnapState()); - snapMock.rpcMocks.eth_chainId.mockReturnValue('0x1'); - }); - - it('should succeed saving passing VC', async () => { - // Get Veramo agent - // const agent = new VeramoAgent(identitySnapParams); - // (identitySnapParams.snap as SnapMock).rpcMocks.snap_dialog.mockReturnValue( - // true, - // ); - // const identifier = await agent.agent.didManagerCreate({ - // kms: 'snap', - // provider: 'did:pkh', - // options: { chainId: '1' }, - // }); - // snapState.accountState[snapState.currentAccount].identifiers[ - // identifier.did - // ] = identifier; - // const credential = await getDefaultCredential(agent); - // const params: SaveVCRequestParams = { - // verifiableCredentials: [credential], - // options: {}, - // }; - // const result = await saveVC(identitySnapParams, params); - // expect(result.length).toBe(1); - // expect.assertions(1); - }); - - it('should succeed saving 2 VCs', async () => { - // // Get Veramo agent - // const agent = new VeramoAgent(identitySnapParams); - // (identitySnapParams.snap as SnapMock).rpcMocks.snap_dialog.mockReturnValue( - // true, - // ); - // const identifier = await agent.agent.didManagerCreate({ - // kms: 'snap', - // provider: 'did:pkh', - // options: { chainId: '1' }, - // }); - // snapState.accountState[snapState.currentAccount].identifiers[ - // identifier.did - // ] = identifier; - // const credential1 = await getDefaultCredential(agent, 'type1'); - // const credential2 = await getDefaultCredential(agent, 'type2'); - // const params: SaveVCRequestParams = { - // verifiableCredentials: [credential1, credential2], - // options: {}, - // }; - // const result = await saveVC(identitySnapParams, params); - // expect(result.length).toBe(2); - // expect.assertions(1); - }); - - it('should not save VCs which subject doesnt match current account', async () => { - // // Get Veramo agent - // const agent = new VeramoAgent(identitySnapParams); - // (identitySnapParams.snap as SnapMock).rpcMocks.snap_dialog.mockReturnValue( - // true, - // ); - // const identifier = await agent.agent.didManagerCreate({ - // kms: 'snap', - // provider: 'did:pkh', - // options: { chainId: '1' }, - // }); - // snapState.accountState[snapState.currentAccount].identifiers[ - // identifier.did - // ] = identifier; - // const credential1 = await getDefaultCredential(agent, 'type1'); - // let credential2: any = await getDefaultCredential(agent, 'type2'); - // console.log(JSON.stringify(credential2)); - // credential2 = JSON.parse(JSON.stringify(credential2, null, 4)); - // credential2.credentialSubject.id = - // '0x7d871f006d97498ea3382688756af94ab2e65caa'; - // const params: SaveVCRequestParams = { - // verifiableCredentials: [credential1, credential2], - // options: {}, - // }; - // const result = await saveVC(identitySnapParams, params); - // expect(result.length).toBe(1); - // expect.assertions(1); - }); - - it('should not save VCs when params invalid', async () => { - //let data: ISaveVC = [ vc: {} as VerifiedCredential - const createVcRequest = getRequestParams('saveVC', { - data: { - verifiableCredentials: [], - }, - }); - - await expect( - onRpcRequest({ - origin: 'tests', - request: createVcRequest as any, - }), - ).rejects.toThrowError(); - - expect.assertions(1); - }); -}); diff --git a/packages/hedera-identify-snap/packages/snap/tests/rpc/vc/syncGoogleVCs.spec.ts b/packages/hedera-identify-snap/packages/snap/tests/rpc/vc/syncGoogleVCs.spec.ts deleted file mode 100644 index d0c4af7e..00000000 --- a/packages/hedera-identify-snap/packages/snap/tests/rpc/vc/syncGoogleVCs.spec.ts +++ /dev/null @@ -1,33 +0,0 @@ -/*- - * - * 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. - * - */ - -/* eslint-disable */ -import { MetaMaskInpageProvider } from '@metamask/providers'; -import { IdentitySnapParams, IdentitySnapState } from '../../../src/interfaces'; -import { SnapMock } from '../../testUtils/snap.mock'; - -describe('SyncGoogleVCs', () => { - let identitySnapParams: IdentitySnapParams; - let snapState: IdentitySnapState; - let snapMock: SnapMock; - let metamask: MetaMaskInpageProvider; - - it('should return true', async () => {}); -}); diff --git a/packages/hedera-identify-snap/packages/snap/tests/rpc/vc/verifyVC.spec.disabled.ts b/packages/hedera-identify-snap/packages/snap/tests/rpc/vc/verifyVC.spec.disabled.ts deleted file mode 100644 index 0aff8771..00000000 --- a/packages/hedera-identify-snap/packages/snap/tests/rpc/vc/verifyVC.spec.disabled.ts +++ /dev/null @@ -1,104 +0,0 @@ -/*- - * - * 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. - * - */ - -/* eslint-disable no-restricted-globals */ - -import { MetaMaskInpageProvider } from '@metamask/providers'; -import { W3CVerifiableCredential } from '@veramo/core'; -import { IDataManagerQueryResult } from 'src/plugins/veramo/verifiable-creds-manager'; -import { onRpcRequest } from '../../../src'; -import { getDefaultSnapState } from '../../testUtils/constants'; -import { getRequestParams } from '../../testUtils/helper'; -import { createMockSnap, SnapMock } from '../../testUtils/snap.mock'; - -describe('VerifyVC', () => { - let snapMock: SnapMock; - let metamask: MetaMaskInpageProvider; - - let credentials: W3CVerifiableCredential[]; - - beforeAll(async () => { - snapMock = createMockSnap(); - metamask = snapMock as unknown as MetaMaskInpageProvider; - }); - - beforeEach(async () => { - snapMock.rpcMocks.snap_dialog.mockReturnValue(true); - snapMock.rpcMocks.snap_manageState.mockReturnValue(getDefaultSnapState()); - snapMock.rpcMocks.snap_manageState('update', getDefaultSnapState()); - snapMock.rpcMocks.eth_chainId.mockReturnValue('0x1'); - - const createVcRequest1 = getRequestParams('createVC', { - vcValue: { prop: 10 }, - credTypes: ['Login'], - }); - - const createVcRequest2 = getRequestParams('createVC', { - vcValue: { prop: 20 }, - credTypes: ['NotLogin'], - }); - - await onRpcRequest({ origin: 'tests', request: createVcRequest1 as any }); - await onRpcRequest({ origin: 'tests', request: createVcRequest2 as any }); - - const getVcRequest = getRequestParams('getVCs', {}); - const vcsReturned: IDataManagerQueryResult[] = (await onRpcRequest({ - origin: 'tests', - request: getVcRequest as any, - })) as IDataManagerQueryResult[]; - - credentials = vcsReturned.map((vc) => vc.data as W3CVerifiableCredential); - }); - - it('should verify VC', async () => { - const verifyVCRequest = getRequestParams('verifyVC', { - verifiableCredential: credentials[0], - }); - await expect( - onRpcRequest({ origin: 'tests', request: verifyVCRequest as any }), - ).resolves.toBe(true); - expect.assertions(1); - }); - - it('should reject if VC is tampered', async () => { - const tamperedVC = JSON.parse(JSON.stringify(credentials[0])); - - tamperedVC.issuer.id = - 'did:pkh:eip155:296:0x7d871f006d97498ea338268a956af94ab2e65cde'; - - const verifyVCRequest = getRequestParams('verifyVC', { - verifiableCredential: tamperedVC, - }); - await expect( - onRpcRequest({ origin: 'tests', request: verifyVCRequest as any }), - ).resolves.toBe(false); - expect.assertions(1); - }); - - it('should reject if request invalid', async () => { - const verifyVCRequest = getRequestParams('verifyVC', { - credential: credentials[0], - }); - await expect( - onRpcRequest({ origin: 'tests', request: verifyVCRequest as any }), - ).rejects.toThrow(); - expect.assertions(1); - }); -}); diff --git a/packages/hedera-identify-snap/packages/snap/tests/rpc/vc/verifyVP.spec.disabled.ts b/packages/hedera-identify-snap/packages/snap/tests/rpc/vc/verifyVP.spec.disabled.ts deleted file mode 100644 index 86eded22..00000000 --- a/packages/hedera-identify-snap/packages/snap/tests/rpc/vc/verifyVP.spec.disabled.ts +++ /dev/null @@ -1,119 +0,0 @@ -/*- - * - * 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. - * - */ - -/* eslint-disable no-restricted-globals */ - -import { MetaMaskInpageProvider } from '@metamask/providers'; -import { VerifiablePresentation } from '@veramo/core'; -import { CreateVCResponseResult } from 'src/types/params'; -import { buildMockSnap, SnapMock } from '../../testUtils/snap.mock'; - -import { onRpcRequest } from '../../../src'; -import { - ETH_ADDRESS, - ETH_CHAIN_ID, - getDefaultSnapState, -} from '../../testUtils/constants'; -import { getRequestParams } from '../../testUtils/helper'; - -describe('VerifyVP', () => { - let snapMock: SnapMock; - let metamask: MetaMaskInpageProvider; - - const vcIds: string[] = []; - let presentation: VerifiablePresentation; - - beforeAll(async () => { - snapMock = buildMockSnap(ETH_CHAIN_ID, ETH_ADDRESS); - metamask = snapMock as unknown as MetaMaskInpageProvider; - }); - - beforeEach(async () => { - snapMock.rpcMocks.snap_dialog.mockReturnValue(true); - snapMock.rpcMocks.snap_manageState.mockReturnValue(getDefaultSnapState()); - snapMock.rpcMocks.snap_manageState('update', getDefaultSnapState()); - - const createVcRequest1 = getRequestParams('createVC', { - vcValue: { prop: 10 }, - credTypes: ['Login'], - }); - - const createVcRequest2 = getRequestParams('createVC', { - vcValue: { prop: 20 }, - credTypes: ['NotLogin'], - }); - - const createVcResponse1: CreateVCResponseResult = (await onRpcRequest({ - origin: 'tests', - request: createVcRequest1 as any, - })) as CreateVCResponseResult; - const createVcResponse2: CreateVCResponseResult = (await onRpcRequest({ - origin: 'tests', - request: createVcRequest2 as any, - })) as CreateVCResponseResult; - - vcIds.push(createVcResponse1.metadata.id); - vcIds.push(createVcResponse2.metadata.id); - - const createVpRequest = getRequestParams('createVP', { - vcIds, - }); - - presentation = (await onRpcRequest({ - origin: 'tests', - request: createVpRequest as any, - })) as VerifiablePresentation; - }); - - it('should verify valid VP', async () => { - const verifyVPRequest = getRequestParams('verifyVP', { - verifiablePresentation: presentation, - }); - - const verifyVPResponse = onRpcRequest({ - origin: 'tests', - request: verifyVPRequest as any, - }); - - await expect(verifyVPResponse).resolves.toBe(true); - expect.assertions(1); - }); - - it('should refuse validation when VP is adultered', async () => { - // Setup - snapMock.rpcMocks.snap_dialog.mockReturnValue(false); - - const adultered = JSON.parse(JSON.stringify(presentation)); - - adultered.proof.jwt = - '1.eyJhbGciOiJFUzI1NksiLCJ0eXAiOiJKV1QifQ.eyJ2cCI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJ0eXBlIjpbIlZlcmlmaWFibGVQcmVzZW50YXRpb24iLCJDdXN0b20iXSwidmVyaWZpYWJsZUNyZWRlbnRpYWwiOlsiZXlKaGJHY2lPaUpGVXpJMU5rc2lMQ0owZVhBaU9pSktWMVFpZlEuZXlKbGVIQWlPakUzTURneU56a3lPRElzSW5aaklqcDdJa0JqYjI1MFpYaDBJanBiSW1oMGRIQnpPaTh2ZDNkM0xuY3pMbTl5Wnk4eU1ERTRMMk55WldSbGJuUnBZV3h6TDNZeElsMHNJblI1Y0dVaU9sc2lWbVZ5YVdacFlXSnNaVU55WldSbGJuUnBZV3dpWFN3aVkzSmxaR1Z1ZEdsaGJGTjFZbXBsWTNRaU9uc2lkbU5FWVhSaElqcDdJbkJ5YjNBaU9qRXdmU3dpYUdWa1pYSmhRV05qYjNWdWRFbGtJam9pTUM0d0xqRTFNakUxSW4xOUxDSnBjM04xWlhJaU9uc2lhR1ZrWlhKaFFXTmpiM1Z1ZEVsa0lqb2lNQzR3TGpFMU1qRTFJbjBzSW5OMVlpSTZJbVJwWkRwd2EyZzZaV2x3TVRVMU9qSTVOam93ZURka09EY3haakF3Tm1RNU56UTVPR1ZoTXpNNE1qWTRZVGsxTm1GbU9UUmhZakpsTmpWalpHUWlMQ0p1WW1ZaU9qRTJOelkzTkRNeU9ESXNJbWx6Y3lJNkltUnBaRHB3YTJnNlpXbHdNVFUxT2pJNU5qb3dlRGRrT0RjeFpqQXdObVE1TnpRNU9HVmhNek00TWpZNFlUazFObUZtT1RSaFlqSmxOalZqWkdRaWZRLmRySGdXTzhSM0lBaHZTbEJZTFNLNnlObnJkTjMwRDkwZFluSmUxN1FtclZ3cS1nU2psQ2REd1dnV1pzRFFxRXZlblRoSHpLeHZIRVVkbmN2Q0xyTkZnIl19LCJuYmYiOjE2NzY3NDMyODIsImlzcyI6ImRpZDpwa2g6ZWlwMTU1OjI5NjoweDdkODcxZjAwNmQ5NzQ5OGVhMzM4MjY4YTk1NmFmOTRhYjJlNjVjZGQifQ.3rRRKLtrTcsiAzXHgmQVjZOG2YZKBxomIwbZC6nFPaUNyaBTLOLEw8ZAmVpdWRXA-K7OjlPtcyaz4IMn-iaUlg'; - - const verifyVPRequest = getRequestParams('verifyVP', { - verifiablePresentation: adultered, - }); - - const verifyVPResponse = onRpcRequest({ - origin: 'tests', - request: verifyVPRequest as any, - }); - - await expect(verifyVPResponse).rejects.toThrow(); - }); -}); diff --git a/packages/hedera-identify-snap/packages/snap/tests/testUtils/constants.ts b/packages/hedera-identify-snap/packages/snap/tests/testUtils/constants.ts deleted file mode 100644 index d3891bbb..00000000 --- a/packages/hedera-identify-snap/packages/snap/tests/testUtils/constants.ts +++ /dev/null @@ -1,94 +0,0 @@ -/*- - * - * 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 cloneDeep from 'lodash.clonedeep'; -import { Account, IdentitySnapState } from '../../src/interfaces'; -import { getEmptyAccountState } from '../../src/utils/config'; - -export const privateKey = - '0x63ce0077f0d617dbf54d5f335de2983313c6356f25b45e0f68f85bee1490a6ae'; - -export const mnemonic = - 'final runway match relax bamboo carry budget guilt dish weapon magnet alarm'; -export const ETH_ADDRESS = '0xf49d65c80c3d2d98231654513b2da4652f09c9fe'; -export const ETH_CHAIN_ID = '0x1'; - -export const HEDERA_CHAIN_ID = { - mainnet: '0x127', - testnet: '0x128', - previewnet: '0x129', - localnet: '0x12a', -}; - -export const EVM_ACCOUNT = { - chainId: '137', // polygon - address: '0xf49d65c80c3d2d98231654513b2da4652f09c9fe', - privatekey: - 'd787278d71bc9b50e814705ca48fcf652e08fb5eb73773e98146c48846bde456', -}; - -export const HEDERA_ACCOUNT = { - accountId: '0.0.15215', - address: '0x7d871f006d97498ea338268a956af94ab2e65cdd', - privateKey: - '2386d1d21644dc65d4e4b9e2242c5f155cab174916cbc46ad85622cdaeac835c', -}; - -export const publicKey = - '0x0480a9cd48fd436f8c1f81b156eb615618cd573c3eb1e6d937a17b8222027cae850a9f561d414001a8bdefdb713c619d2caf08a0c9655b0cf42de065bc51e0169a'; -export const signedMsg = - '0x30eb4dbf93e7bfdb109ed03f7803f2378fa27d18ddc233cb3d121b5ba13253fe2515076d1ba66f3dc282c182479b843c925c62eb1f5a0676bcaf995e8e7552941c'; - -export const exampleDIDPkh = `did:pkh:eip155:4:${ETH_ADDRESS}`; - -const defaultSnapState: IdentitySnapState = { - currentAccount: { - metamaskAddress: ETH_ADDRESS, - method: '', - identifier: {} as any, - privateKey: '', - publicKey: '', - } as Account, - accountState: { - '60': { - '0xf49d65c80c3d2d98231654513b2da4652f09c9fe': getEmptyAccountState(), - }, - }, - snapConfig: { - dApp: { - didMethod: 'did:pkh', - disablePopups: false, - friendlyDapps: [], - }, - snap: { - acceptedTerms: true, - }, - }, -}; - -export const getDefaultSnapState = (): IdentitySnapState => { - // defaultSnapState.accountState[address].publicKey = publicKey; - return cloneDeep(defaultSnapState); -}; - -export const getSnapStateWithIdentifiers = (): IdentitySnapState => { - const snapStateWithIdentifiers = getDefaultSnapState(); - return snapStateWithIdentifiers; -}; diff --git a/packages/hedera-identify-snap/packages/snap/tests/testUtils/helper.ts b/packages/hedera-identify-snap/packages/snap/tests/testUtils/helper.ts deleted file mode 100644 index d2c4c4b8..00000000 --- a/packages/hedera-identify-snap/packages/snap/tests/testUtils/helper.ts +++ /dev/null @@ -1,52 +0,0 @@ -/*- - * - * 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. - * - */ - -// export const getDefaultCredential = async ( -// agent: VeramoAgent, -// type = 'Default', -// ): Promise => { -// const createVcResult = await agent.createVC( -// 'vcData', -// { name: 'Diego, the tester' }, -// 'snap', -// ['VerifiableCredential', type], -// ); -// const getVcsResult = await agent.getVCs( -// { store: 'snap' }, -// { type: 'id', filter: createVcResult[0].id }, -// ); -// return getVcsResult[0].data as VerifiableCredential; -// }; - -/** - * Get RPC Request params. - * - * @param method - RPC method to be executed. - * @param params - Params of the specific method. - * @returns JSON request object. - */ -export function getRequestParams(method: string, params: any) { - return { - jsonrpc: '2.0', - id: 'v7rOu495Q4NIBbo-8AqY3', - method, - params, - }; -} diff --git a/packages/hedera-identify-snap/packages/snap/tests/testUtils/snap.mock.ts b/packages/hedera-identify-snap/packages/snap/tests/testUtils/snap.mock.ts deleted file mode 100644 index 4a64f09a..00000000 --- a/packages/hedera-identify-snap/packages/snap/tests/testUtils/snap.mock.ts +++ /dev/null @@ -1,159 +0,0 @@ -/*- - * - * 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. - * - */ - -/* eslint-disable */ - -import { BIP44CoinTypeNode } from '@metamask/key-tree'; - -import { AlchemyProvider, Provider, Wallet } from 'ethers'; -import { IdentitySnapState } from '../../src/interfaces'; -import { ETH_ADDRESS, mnemonic, privateKey } from './constants'; -import { RequestArguments } from '@metamask/providers'; -import { Maybe } from '@metamask/providers/dist/utils.cjs'; - -type ISnapMock = { - request(args: RequestArguments): Promise>; - resetHistory(): void; -}; -type SnapManageState = { - operation: 'get' | 'update' | 'clear'; - newState: unknown; -}; - -export class SnapMock implements ISnapMock { - private snapState: IdentitySnapState | null = null; - - private snap: Wallet = new Wallet(privateKey); - - private snapManageState(params: SnapManageState): IdentitySnapState | null { - if (!params) { - return null; - } - - if (params.operation === 'get') { - return this.snapState; - } - - if (params.operation === 'update') { - this.snapState = params.newState as IdentitySnapState; - } else if (params.operation === 'clear') { - this.snapState = null; - } - - return null; - } - - private async snapPersonalSign(data: string[]): Promise { - const signature = await this.snap.signMessage(data[0]); - return signature; - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - private async snapEthCall(data: any[]): Promise { - const apiKey = 'NRFBwig_CLVL0WnQLY3dUo8YkPmW-7iN'; - const provider = new AlchemyProvider('goerli', apiKey); - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - return provider.call(data[0]); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - private async snapEthLogs(data: any[]): Promise { - const apiKey = 'NRFBwig_CLVL0WnQLY3dUo8YkPmW-7iN'; - const provider = new AlchemyProvider('goerli', apiKey); - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - return provider.getLogs(data[0]); - } - - readonly rpcMocks = { - snap_dialog: jest.fn(), - eth_requestAccounts: jest.fn().mockResolvedValue([ETH_ADDRESS]), - eth_chainId: jest.fn().mockResolvedValue('0x5'), - net_version: jest.fn().mockResolvedValue('5'), - snap_getBip44Entropy: jest - .fn() - .mockImplementation(async (params: { coinType: number }) => { - const node = await BIP44CoinTypeNode.fromDerivationPath([ - `bip39:${mnemonic}`, - `bip32:44'`, - `bip32:${params.coinType}'`, - ]); - - return node.toJSON(); - }), - snap_manageState: jest - .fn() - .mockImplementation((params: unknown) => - this.snapManageState(params as SnapManageState), - ), - personal_sign: jest.fn().mockImplementation(async (data: unknown) => { - return this.snapPersonalSign(data as string[]); - }), - eth_call: jest.fn().mockImplementation(async (data: unknown) => { - return this.snapEthCall(data as any[]); - }), - eth_getLogs: jest.fn().mockImplementation(async (data: unknown) => { - return this.snapEthLogs(data as any[]); - }), - eth_signTypedData_v4: jest - .fn() - .mockImplementation((...params: unknown[]) => { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unused-vars, @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any - const { domain, types, message } = JSON.parse(params[1] as any); - - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - delete types.EIP712Domain; - - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, no-underscore-dangle - return this.snap.signTypedData(domain, types, message); - }), - }; - - request(args: RequestArguments): Promise> { - const { method, params } = args; - // @ts-expect-error Args params won't cause an issue - // eslint-disable-next-line - return this.rpcMocks[method](params); - } - - resetHistory(): void { - Object.values(this.rpcMocks).forEach((mock) => mock.mockRestore()); - } -} - -/** - * Creates and returns a Mock Snap - * - * @returns {SnapsGlobalObject & SnapMock} SnapMock - */ -export function createMockSnap(): SnapMock { - return new SnapMock() as SnapMock; -} - -/** - * Creates and returns a Mock Snap - * - * @returns {SnapsGlobalObject & SnapMock} SnapMock - */ -export function buildMockSnap(chainId: string, address: string): SnapMock { - let snapMock = new SnapMock() as SnapMock; - snapMock.rpcMocks.eth_requestAccounts.mockResolvedValue([address]); - snapMock.rpcMocks.eth_chainId.mockResolvedValue(chainId); - return snapMock; -} diff --git a/packages/hedera-identify-snap/packages/snap/tests/testUtils/wallet.mock.ts b/packages/hedera-identify-snap/packages/snap/tests/testUtils/wallet.mock.ts deleted file mode 100644 index 8c28d3bd..00000000 --- a/packages/hedera-identify-snap/packages/snap/tests/testUtils/wallet.mock.ts +++ /dev/null @@ -1,96 +0,0 @@ -/*- - * - * 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 { Wallet } from 'ethers'; -import { IdentitySnapState } from '../../src/interfaces'; -import { ETH_ADDRESS, privateKey, signedMsg } from './constants'; -import { RequestArguments } from '@metamask/providers'; -import { Maybe } from '@metamask/providers/dist/utils.cjs'; - -type IWalletMock = { - request(args: RequestArguments): Promise>; - resetHistory(): void; -}; - -export class WalletMock implements IWalletMock { - #snapState: IdentitySnapState | null = null; - - readonly #wallet: Wallet = new Wallet(privateKey); - - #snapManageState(...params: unknown[]): IdentitySnapState | null { - if (params.length === 0) { - return null; - } - - if (params[0] === 'get') { - return this.#snapState; - } else if (params[0] === 'update') { - this.#snapState = params[1] as IdentitySnapState; - } else if (params[0] === 'clear') { - this.#snapState = null; - } - - return null; - } - - readonly rpcMocks = { - snap_confirm: jest.fn(), - eth_requestAccounts: jest.fn().mockResolvedValue([ETH_ADDRESS]), - eth_chainId: jest.fn().mockResolvedValue('4'), - snap_manageState: jest - .fn() - .mockImplementation((...params: unknown[]) => - this.#snapManageState(...params), - ), - personal_sign: jest.fn().mockResolvedValue(signedMsg), - eth_signTypedData_v4: jest - .fn() - .mockImplementation((...params: unknown[]) => { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unused-vars, @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any - const { domain, types, message } = JSON.parse(params[1] as any); - - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - delete types.EIP712Domain; - - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - return this.#wallet.signTypedData(domain, types, message); - }), - }; - - request(args: RequestArguments): Promise> { - const { method, params = [] } = args; - - // @ts-expect-error Args params won't cause an issue - // eslint-disable-next-line - return this.rpcMocks[method](...params); - } - - resetHistory(): void { - Object.values(this.rpcMocks).forEach((mock) => mock.mockRestore()); - } -} - -/** - * Create mock wallet. - * - * @returns Wallet mock. - */ -export function createMockWallet(): WalletMock { - return new WalletMock() as WalletMock; -} diff --git a/packages/hedera-identify-snap/packages/snap/tests/utils/init.spec.disabled.ts b/packages/hedera-identify-snap/packages/snap/tests/utils/init.spec.disabled.ts deleted file mode 100644 index 7c449c80..00000000 --- a/packages/hedera-identify-snap/packages/snap/tests/utils/init.spec.disabled.ts +++ /dev/null @@ -1,56 +0,0 @@ -/*- - * - * 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. - * - */ - -/* eslint-disable */ - -import { getInitialSnapState } from '../../src/utils/config'; -import { init } from '../../src/utils/init'; -import { createMockWallet, WalletMock } from '../testUtils/wallet.mock'; - -describe.skip('RPC handler [init]', () => { - let walletMock: WalletMock; - - beforeEach(() => { - walletMock = createMockWallet(); - }); - - it('should succeed for accepted terms and conditions', async () => { - const initialState = getInitialSnapState(); - walletMock.rpcMocks.snap_confirm.mockReturnValueOnce(true); - - await expect(init('test-origin', 'testnet')).resolves.toEqual(initialState); - expect(walletMock.rpcMocks.snap_manageState).toHaveBeenCalledWith( - 'update', - initialState, - ); - - expect.assertions(2); - }); - - it('should fail for rejected terms and conditions', async function () { - walletMock.rpcMocks.snap_confirm.mockReturnValueOnce(false); - - await expect(init('test-origin', 'testnet')).rejects.toThrow( - new Error('User did not accept terms and conditions!'), - ); - - expect.assertions(1); - }); -}); diff --git a/packages/hedera-identify-snap/packages/snap/tests/utils/snapUtils.spec.disabled.ts b/packages/hedera-identify-snap/packages/snap/tests/utils/snapUtils.spec.disabled.ts deleted file mode 100644 index f73e1bc4..00000000 --- a/packages/hedera-identify-snap/packages/snap/tests/utils/snapUtils.spec.disabled.ts +++ /dev/null @@ -1,285 +0,0 @@ -/*- - * - * 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. - * - */ - -/* eslint-disable */ - -// import { SnapProvider } from '@metamask/snap-types'; -// import { -// addFriendlyDapp, getCurrentNetwork, -// getPublicKey, -// removeFriendlyDapp, -// snapConfirm, -// togglePopups -// } from '../../src/rpc/snap/utils'; -// import { -// address, -// getDefaultSnapState, -// publicKey, -// snapConfirmParams -// } from '../testUtils/constants'; -// import { createMockWallet, WalletMock } from '../testUtils/wallet.mock'; - -// describe.skip('Utils [snap]', () => { -// let walletMock: SnapProvider & WalletMock; - -// beforeEach(() => { -// walletMock = createMockWallet(); -// }); - -// describe.skip('getCurrentNetwork', () => { -// it('should succeed for mainnet (0x1)', async () => { -// walletMock.rpcMocks.eth_chainId.mockResolvedValue('0x1'); - -// await expect(getCurrentNetwork(walletMock)).resolves.toEqual('0x1'); - -// expect(walletMock.rpcMocks.eth_chainId).toHaveBeenCalledTimes(1); - -// expect.assertions(2); -// }); - -// it('should succeed for rinkeby (0x4)', async () => { -// walletMock.rpcMocks.eth_chainId.mockResolvedValue('0x4'); - -// await expect(getCurrentNetwork(walletMock)).resolves.toEqual('0x4'); - -// expect(walletMock.rpcMocks.eth_chainId).toHaveBeenCalledTimes(1); - -// expect.assertions(2); -// }); -// }); - -// describe.skip('togglePopups', () => { -// it('should succeed and toggle popups (off -> on)', async () => { -// const initialState = getDefaultSnapState(); - -// await expect( -// togglePopups(walletMock, initialState), -// ).resolves.not.toThrow(); - -// // Call should be `update` with the correct arguments -// const expectedState = getDefaultSnapState(); -// expectedState.snapConfig.dApp.disablePopups = true; -// expect(walletMock.rpcMocks.snap_manageState).toHaveBeenCalledWith( -// 'update', -// expectedState, -// ); - -// expect.assertions(2); -// }); - -// it('should succeed and toggle popups (on -> off)', async () => { -// const initialState = getDefaultSnapState(); -// initialState.snapConfig.dApp.disablePopups = true; - -// await expect( -// togglePopups(walletMock, initialState), -// ).resolves.not.toThrow(); - -// // Call should be `update` with the correct arguments -// const expectedState = getDefaultSnapState(); -// expectedState.snapConfig.dApp.disablePopups = false; - -// expect(walletMock.rpcMocks.snap_manageState).toHaveBeenCalledWith( -// 'update', -// expectedState, -// ); - -// expect.assertions(2); -// }); -// }); - -// describe.skip('addFriendlyDapp', () => { -// it('should succeed adding dApp when friendlyDapps empty', async () => { -// const dApp = 'test_dApp_42'; -// const initialState = getDefaultSnapState(); - -// walletMock.rpcMocks.snap_manageState.mockResolvedValue(initialState); - -// await expect( -// addFriendlyDapp(walletMock, initialState, dApp), -// ).resolves.not.toThrow(); - -// const expectedState = getDefaultSnapState(); -// expectedState.snapConfig.dApp.friendlyDapps = [dApp]; - -// // Call should be `update` with the correct arguments -// expect(walletMock.rpcMocks.snap_manageState).toHaveBeenCalledWith( -// 'update', -// expectedState, -// ); - -// expect.assertions(2); -// }); - -// it('should succeed adding dApp when friendlyDapps not empty', async () => { -// const dApp = 'test_dApp_42'; -// const initialState = getDefaultSnapState(); -// initialState.snapConfig.dApp.friendlyDapps = [ -// 'test_dApp_1', -// 'test_dApp_2', -// 'test_dApp_3', -// ]; - -// walletMock.rpcMocks.snap_manageState.mockResolvedValue(initialState); - -// await expect( -// addFriendlyDapp(walletMock, initialState, dApp), -// ).resolves.not.toThrow(); - -// const expectedState = getDefaultSnapState(); -// expectedState.snapConfig.dApp.friendlyDapps = [ -// 'test_dApp_1', -// 'test_dApp_2', -// 'test_dApp_3', -// dApp, -// ]; - -// // Call should be `update` with the correct arguments -// expect(walletMock.rpcMocks.snap_manageState).toHaveBeenCalledWith( -// 'update', -// expectedState, -// ); - -// expect.assertions(2); -// }); -// }); - -// describe.skip('removeFriendlyDapp', () => { -// it('should succeed removing dApp when there is only one', async () => { -// const dApp = 'test_dApp_42'; -// const initialState = getDefaultSnapState(); -// initialState.snapConfig.dApp.friendlyDapps = [dApp]; - -// walletMock.rpcMocks.snap_manageState.mockResolvedValue(initialState); - -// await expect( -// removeFriendlyDapp(walletMock, initialState, dApp), -// ).resolves.not.toThrow(); - -// const expectedState = getDefaultSnapState(); - -// // Call should be `update` with the correct arguments -// expect(walletMock.rpcMocks.snap_manageState).toHaveBeenCalledWith( -// 'update', -// expectedState, -// ); - -// expect.assertions(2); -// }); - -// it('should succeed removing dApp when there are many', async () => { -// const dApp = 'test_dApp_42'; -// const initialState = getDefaultSnapState(); -// initialState.snapConfig.dApp.friendlyDapps = [ -// 'test_dApp_1', -// dApp, -// 'test_dApp_2', -// 'test_dApp_3', -// ]; - -// walletMock.rpcMocks.snap_manageState.mockResolvedValue(initialState); - -// await expect( -// removeFriendlyDapp(walletMock, initialState, dApp), -// ).resolves.not.toThrow(); - -// const expectedState = getDefaultSnapState(); -// expectedState.snapConfig.dApp.friendlyDapps = [ -// 'test_dApp_1', -// 'test_dApp_2', -// 'test_dApp_3', -// ]; - -// // Call should be `update` with the correct arguments -// expect(walletMock.rpcMocks.snap_manageState).toHaveBeenCalledWith( -// 'update', -// expectedState, -// ); - -// expect.assertions(2); -// }); -// }); - -// describe.skip('getPublicKey', async () => { -// // it('should succeed getting public key', async () => { -// // const initialState = getDefaultSnapState(); -// // initialState.accountState[address].publicKey = ''; - -// await expect( -// getPublicKey(walletMock, initialState, address), -// ).resolves.toEqual(publicKey); - -// // expect.assertions(1); -// // }); - -// it('should succeed getting public key (saved in snap state)', async () => { -// const initialState = getDefaultSnapState(); - -// await expect( -// getPublicKey(walletMock, initialState, address), -// ).resolves.toEqual(publicKey); - -// expect.assertions(1); -// }); - -// it('should fail getting public key (user denied)', async () => { -// const initialState = getDefaultSnapState(); -// initialState.accountState[address].publicKey = ''; - -// walletMock.rpcMocks.personal_sign.mockRejectedValue(new Error()); - -// await expect( -// getPublicKey(walletMock, initialState, address), -// ).rejects.toThrow(new Error('User denied request')); - -// expect.assertions(1); -// }); -// }); - -// describe('snapConfirm', () => { -// it('should return true', async () => { -// walletMock.rpcMocks.snap_confirm.mockResolvedValue(true); - -// await expect(snapConfirm(walletMock, snapConfirmParams)).resolves.toEqual( -// true, -// ); - -// expect(walletMock.rpcMocks.snap_confirm).toHaveBeenCalledWith( -// snapConfirmParams, -// ); - -// expect.assertions(2); -// }); - -// it('should return false', async () => { -// walletMock.rpcMocks.snap_confirm.mockResolvedValue(false); - -// await expect(snapConfirm(walletMock, snapConfirmParams)).resolves.toEqual( -// false, -// ); - -// expect(walletMock.rpcMocks.snap_confirm).toHaveBeenCalledWith( -// snapConfirmParams, -// ); - -// expect.assertions(2); -// }); -// }); -// }); diff --git a/packages/hedera-identify-snap/packages/snap/tests/utils/stateUtils.spec.disabled.ts b/packages/hedera-identify-snap/packages/snap/tests/utils/stateUtils.spec.disabled.ts deleted file mode 100644 index 08f7e564..00000000 --- a/packages/hedera-identify-snap/packages/snap/tests/utils/stateUtils.spec.disabled.ts +++ /dev/null @@ -1,122 +0,0 @@ -/*- - * - * 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. - * - */ - -/* eslint-disable */ - -import { DEFAULTCOINTYPE } from 'src/types/constants'; -import { - getStateUnchecked, - initAccountState, - initState, - updateState, -} from '../../src/snap/state'; -import { getInitialSnapState } from '../../src/utils/config'; -import { ETH_ADDRESS, getDefaultSnapState } from '../testUtils/constants'; -import { WalletMock, createMockWallet } from '../testUtils/wallet.mock'; - -describe.skip('Utils [state]', () => { - let walletMock: WalletMock; - - beforeEach(() => { - walletMock = createMockWallet(); - }); - - describe('updateSnapState', () => { - it('should succeed updating snap state with default state', async () => { - const initialState = getDefaultSnapState(); - - await expect(updateState(initialState)).resolves.not.toThrow(); - - expect(walletMock.rpcMocks.snap_manageState).toHaveBeenCalledWith( - 'update', - initialState, - ); - - expect.assertions(2); - }); - - it('should succeed updating snap state with empty state', async () => { - const emptyState = {}; - - await expect( - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any - updateState(emptyState as any), - ).resolves.not.toThrow(); - - expect(walletMock.rpcMocks.snap_manageState).toHaveBeenCalledWith( - 'update', - emptyState, - ); - - expect.assertions(2); - }); - }); - - describe('getSnapStateUnchecked', () => { - it('should return null if state is not initialized', async () => { - await expect(getStateUnchecked()).resolves.toEqual(null); - - expect.assertions(1); - }); - - it('should succeed getting initial snap state', async () => { - const initialState = getDefaultSnapState(); - walletMock.rpcMocks.snap_manageState.mockReturnValueOnce(initialState); - - await expect(getStateUnchecked()).resolves.toEqual(initialState); - - expect.assertions(1); - }); - }); - - describe('initSnapState', () => { - it('should succeed initializing snap state', async () => { - const initialState = getInitialSnapState(); - - await expect(initState()).resolves.toEqual(initialState); - - expect(walletMock.rpcMocks.snap_manageState).toHaveBeenCalledWith( - 'update', - initialState, - ); - - expect.assertions(2); - }); - }); - - describe('initAccountState', () => { - it('should succeed initializing empty account state', async () => { - const initialState = getInitialSnapState(); - const defaultState = getDefaultSnapState(); - // defaultState.accountState[address].publicKey = publicKey; - - await expect( - initAccountState(initialState, DEFAULTCOINTYPE.toString(), ETH_ADDRESS), - ).resolves.not.toThrow(); - - expect(walletMock.rpcMocks.snap_manageState).toHaveBeenCalledWith( - 'update', - defaultState, - ); - - expect.assertions(2); - }); - }); -}); diff --git a/packages/hedera-identify-snap/packages/snap/tsconfig.json b/packages/hedera-identify-snap/packages/snap/tsconfig.json index c9c43d1e..317025be 100644 --- a/packages/hedera-identify-snap/packages/snap/tsconfig.json +++ b/packages/hedera-identify-snap/packages/snap/tsconfig.json @@ -31,11 +31,7 @@ "jsxImportSource": "@metamask/snaps-sdk", "typeRoots": ["./node_modules/@types", "../../node_modules/@types"] }, - "files": ["./json-typings.d.ts"], - "include": [ - "**/*.ts", - "**/*.tsx", - "global.d.ts", - "../site/src/types/veramo.ts" - ] + "files": ["./global.d.ts"], + "exclude": ["./jest.config.js"], + "include": ["**/*.ts", "**/*.tsx"] } diff --git a/packages/hedera-identify-snap/yarn.lock b/packages/hedera-identify-snap/yarn.lock index 6c9cd77a..70f59bd3 100644 --- a/packages/hedera-identify-snap/yarn.lock +++ b/packages/hedera-identify-snap/yarn.lock @@ -2526,13 +2526,6 @@ __metadata: languageName: node linkType: hard -"@gar/promisify@npm:^1.1.3": - version: 1.1.3 - resolution: "@gar/promisify@npm:1.1.3" - checksum: 4059f790e2d07bf3c3ff3e0fec0daa8144fe35c1f6e0111c9921bd32106adaa97a4ab096ad7dab1e28ee6a9060083c4d1a4ada42a7f5f3f7a96b8812e2b757c1 - languageName: node - linkType: hard - "@gatsbyjs/parcel-namer-relative-to-cwd@npm:^2.13.1": version: 2.13.1 resolution: "@gatsbyjs/parcel-namer-relative-to-cwd@npm:2.13.1" @@ -2857,9 +2850,9 @@ __metadata: languageName: node linkType: hard -"@hashgraph/cryptography@npm:1.4.8-beta.8": - version: 1.4.8-beta.8 - resolution: "@hashgraph/cryptography@npm:1.4.8-beta.8" +"@hashgraph/cryptography@npm:1.4.8-beta.7": + version: 1.4.8-beta.7 + resolution: "@hashgraph/cryptography@npm:1.4.8-beta.7" dependencies: asn1js: ^3.0.5 bignumber.js: ^9.1.1 @@ -2883,7 +2876,7 @@ __metadata: optional: true expo-random: optional: true - checksum: a8688923bc9931957ea6d38ceec757016bd97d928761d2e8dd5f1e3f6a3f29da2cf259f7b0d09888081e08d32d5e4b9bed8fcb3f9c6376d8ba94b199b5b6b636 + checksum: fc4aca4182c16f23c8e7f73e2bbf628e33cf3e6a7b4f2be5ede3188e99c9a2a5f064d0b66d5039c36523fae4be3146523dc190b31fa37059b998b741652505f2 languageName: node linkType: hard @@ -2898,7 +2891,7 @@ __metadata: "@metamask/eslint-config-nodejs": ^12.1.0 "@metamask/eslint-config-typescript": ^12.1.0 "@metamask/providers": ^17.2.0 - "@react-oauth/google": ^0.7.0 + "@react-oauth/google": ^0.12.1 "@svgr/webpack": ^8.1.0 "@testing-library/dom": ^10.4.0 "@testing-library/jest-dom": ^6.5.0 @@ -2949,11 +2942,8 @@ __metadata: version: 0.0.0-use.local resolution: "@hashgraph/hedera-identify-snap@workspace:packages/snap" dependencies: - "@ethersproject/transactions": ^5.7.0 - "@hashgraph/sdk": ^2.51.0 - "@ipld/dag-pb": ^4.1.2 + "@hashgraph/sdk": ^2.50.0 "@jest/globals": ^29.7.0 - "@lavamoat/allow-scripts": ^2.0.3 "@metamask/auto-changelog": ^3.4.4 "@metamask/eslint-config": ^12.2.0 "@metamask/eslint-config-jest": ^12.1.0 @@ -2965,26 +2955,25 @@ __metadata: "@metamask/snaps-cli": ^6.3.1 "@metamask/snaps-jest": ^8.3.0 "@metamask/snaps-sdk": ^6.3.0 + "@tuum-tech/hedera-did-sdk-js": ^0.1.3 "@types/jest": ^29.5.12 "@types/jsonpath": ^0.2.4 "@types/lodash.clonedeep": ^4.5.9 "@types/secp256k1": ^4.0.6 - "@types/uuid": ^10.0.0 "@typescript-eslint/eslint-plugin": 5.62.0 "@typescript-eslint/parser": 5.62.0 "@veramo/core": 5.1.2 - "@veramo/credential-eip712": ^6.0.0 "@veramo/credential-w3c": 5.1.2 "@veramo/did-jwt": 5.1.2 "@veramo/did-manager": 5.1.2 + "@veramo/did-provider-key": 6.0.0 "@veramo/did-provider-pkh": 5.1.2 "@veramo/did-resolver": 5.1.2 "@veramo/key-manager": 5.1.2 "@veramo/kms-local": 5.1.2 "@veramo/message-handler": 5.1.2 bignumber.js: ^9.1.2 - caip: ^1.1.1 - did-jwt-vc: ^3.1.3 + did-jwt-vc: ^4.0.4 did-resolver: ^4.1.0 eslint: 8.38.0 eslint-config-prettier: ^9.1.0 @@ -3006,35 +2995,34 @@ __metadata: prettier: ^3.3.3 prettier-plugin-packagejson: ^2.5.2 rimraf: ^6.0.1 - secp256k1: ^5.0.0 + secp256k1: ^5.0.1 ts-jest: ^29.2.2 ts-node: ^10.9.2 typescript: ^5.6.2 - uuid: ^10.0.0 languageName: unknown linkType: soft -"@hashgraph/proto@npm:2.15.0-beta.4": - version: 2.15.0-beta.4 - resolution: "@hashgraph/proto@npm:2.15.0-beta.4" +"@hashgraph/proto@npm:2.15.0-beta.3": + version: 2.15.0-beta.3 + resolution: "@hashgraph/proto@npm:2.15.0-beta.3" dependencies: long: ^4.0.0 protobufjs: ^7.2.5 - checksum: 9470832e5de409a2d80706ff46d208465207cadd81cbd60bae366e269ac3054c71133a89e8bab4a4504012944bb15aea54c37d630860c7965c014229bba184b1 + checksum: e3f647905919b068c6eecee449ced13085968f4ee75d719ff32e609c0fe9fec1b909829523e52638bcd6de5122b06e660dddecaa68d725b77a73d09358988988 languageName: node linkType: hard -"@hashgraph/sdk@npm:^2.51.0": - version: 2.51.0 - resolution: "@hashgraph/sdk@npm:2.51.0" +"@hashgraph/sdk@npm:^2.50.0": + version: 2.50.0 + resolution: "@hashgraph/sdk@npm:2.50.0" dependencies: "@ethersproject/abi": ^5.7.0 "@ethersproject/bignumber": ^5.7.0 "@ethersproject/bytes": ^5.7.0 "@ethersproject/rlp": ^5.7.0 "@grpc/grpc-js": 1.8.2 - "@hashgraph/cryptography": 1.4.8-beta.8 - "@hashgraph/proto": 2.15.0-beta.4 + "@hashgraph/cryptography": 1.4.8-beta.7 + "@hashgraph/proto": 2.15.0-beta.3 axios: ^1.6.4 bignumber.js: ^9.1.1 bn.js: ^5.1.1 @@ -3051,7 +3039,7 @@ __metadata: peerDependenciesMeta: expo: optional: true - checksum: 7de736204a22763481c6d881c53a0afb684b7c996efd1fe8a50405ee6dda1a2732dbb449937b9ad85a26ce71bd3c14f5c3424c66ef305c4fde881845e848316c + checksum: 592f7d0106f4e9264612059e3b5210af7ddb210d8cfa6d78bbe7a1491462169d5ff65d8372ef2817beec945c75e808f505b37d7133c0c573bcfa37ba7670cc63 languageName: node linkType: hard @@ -3098,7 +3086,7 @@ __metadata: languageName: node linkType: hard -"@ipld/dag-pb@npm:^4.0.5, @ipld/dag-pb@npm:^4.1.2": +"@ipld/dag-pb@npm:^4.0.5": version: 4.1.2 resolution: "@ipld/dag-pb@npm:4.1.2" dependencies: @@ -3433,32 +3421,6 @@ __metadata: languageName: node linkType: hard -"@lavamoat/aa@npm:^3.1.5": - version: 3.1.5 - resolution: "@lavamoat/aa@npm:3.1.5" - dependencies: - resolve: ^1.22.3 - bin: - lavamoat-ls: src/cli.js - checksum: 46ced120a30e9472c2e448bd6af694911c644ee036d6cd0087692b47c553b5ac26748a9bff1e99f1220f672579bce664fb48cb2f39884fd0b1a45d16b9195e48 - languageName: node - linkType: hard - -"@lavamoat/allow-scripts@npm:^2.0.3": - version: 2.5.1 - resolution: "@lavamoat/allow-scripts@npm:2.5.1" - dependencies: - "@lavamoat/aa": ^3.1.5 - "@npmcli/run-script": ^6.0.0 - bin-links: 4.0.1 - npm-normalize-package-bin: ^3.0.0 - yargs: ^16.2.0 - bin: - allow-scripts: src/cli.js - checksum: 5f249803b13ad1d2900d1be7d500135b3898f700465e748e7f9d88a7c5a8b07829a0665910d4047ffd65877babe94a80c8e8c9ad8ee02e61446e89bcd4179611 - languageName: node - linkType: hard - "@lezer/common@npm:^1.0.0": version: 1.2.2 resolution: "@lezer/common@npm:1.2.2" @@ -3738,7 +3700,7 @@ __metadata: languageName: node linkType: hard -"@metamask/eth-sig-util@npm:^7.0.0, @metamask/eth-sig-util@npm:^7.0.3": +"@metamask/eth-sig-util@npm:^7.0.3": version: 7.0.3 resolution: "@metamask/eth-sig-util@npm:7.0.3" dependencies: @@ -4374,16 +4336,6 @@ __metadata: languageName: node linkType: hard -"@npmcli/fs@npm:^2.1.0": - version: 2.1.2 - resolution: "@npmcli/fs@npm:2.1.2" - dependencies: - "@gar/promisify": ^1.1.3 - semver: ^7.3.5 - checksum: 405074965e72d4c9d728931b64d2d38e6ea12066d4fad651ac253d175e413c06fe4350970c783db0d749181da8fe49c42d3880bd1cbc12cd68e3a7964d820225 - languageName: node - linkType: hard - "@npmcli/fs@npm:^3.1.0": version: 3.1.1 resolution: "@npmcli/fs@npm:3.1.1" @@ -4393,45 +4345,6 @@ __metadata: languageName: node linkType: hard -"@npmcli/move-file@npm:^2.0.0": - version: 2.0.1 - resolution: "@npmcli/move-file@npm:2.0.1" - dependencies: - mkdirp: ^1.0.4 - rimraf: ^3.0.2 - checksum: 52dc02259d98da517fae4cb3a0a3850227bdae4939dda1980b788a7670636ca2b4a01b58df03dd5f65c1e3cb70c50fa8ce5762b582b3f499ec30ee5ce1fd9380 - languageName: node - linkType: hard - -"@npmcli/node-gyp@npm:^3.0.0": - version: 3.0.0 - resolution: "@npmcli/node-gyp@npm:3.0.0" - checksum: fe3802b813eecb4ade7ad77c9396cb56721664275faab027e3bd8a5e15adfbbe39e2ecc19f7885feb3cfa009b96632741cc81caf7850ba74440c6a2eee7b4ffc - languageName: node - linkType: hard - -"@npmcli/promise-spawn@npm:^6.0.0": - version: 6.0.2 - resolution: "@npmcli/promise-spawn@npm:6.0.2" - dependencies: - which: ^3.0.0 - checksum: aa725780c13e1f97ab32ed7bcb5a207a3fb988e1d7ecdc3d22a549a22c8034740366b351c4dde4b011bcffcd8c4a7be6083d9cf7bc7e897b88837150de018528 - languageName: node - linkType: hard - -"@npmcli/run-script@npm:^6.0.0": - version: 6.0.2 - resolution: "@npmcli/run-script@npm:6.0.2" - dependencies: - "@npmcli/node-gyp": ^3.0.0 - "@npmcli/promise-spawn": ^6.0.0 - node-gyp: ^9.0.0 - read-package-json-fast: ^3.0.0 - which: ^3.0.0 - checksum: 7a671d7dbeae376496e1c6242f02384928617dc66cd22881b2387272205c3668f8490ec2da4ad63e1abf979efdd2bdf4ea0926601d78578e07d83cfb233b3a1a - languageName: node - linkType: hard - "@parcel/bundler-default@npm:2.8.3": version: 2.8.3 resolution: "@parcel/bundler-default@npm:2.8.3" @@ -5129,13 +5042,13 @@ __metadata: languageName: node linkType: hard -"@react-oauth/google@npm:^0.7.0": - version: 0.7.3 - resolution: "@react-oauth/google@npm:0.7.3" +"@react-oauth/google@npm:^0.12.1": + version: 0.12.1 + resolution: "@react-oauth/google@npm:0.12.1" peerDependencies: react: ">=16.8.0" react-dom: ">=16.8.0" - checksum: 3b5a17f30266bd8a1cf52cc69741c05025eb52e6243528f8b5d3660c220cadb3a9387c22e43e4774722dedba7dd079ade3148aea369acd8a1f3ad51e2de58029 + checksum: fff63a0fc0b367519b48422c39f6ebc73226e4442ae148d752d05083a185660e66d047fcc8d88b962de97980cfb71f726de0a96ff5f00af0d7e68f333560a7c3 languageName: node linkType: hard @@ -6022,13 +5935,6 @@ __metadata: languageName: node linkType: hard -"@tootallnate/once@npm:2": - version: 2.0.0 - resolution: "@tootallnate/once@npm:2.0.0" - checksum: ad87447820dd3f24825d2d947ebc03072b20a42bfc96cbafec16bff8bbda6c1a81fcb0be56d5b21968560c5359a0af4038a68ba150c3e1694fe4c109a063bed8 - languageName: node - linkType: hard - "@transmute/credentials-context@npm:^0.7.0-unstable.81, @transmute/credentials-context@npm:^0.7.0-unstable.82": version: 0.7.0-unstable.82 resolution: "@transmute/credentials-context@npm:0.7.0-unstable.82" @@ -6219,6 +6125,24 @@ __metadata: languageName: node linkType: hard +"@tuum-tech/hedera-did-sdk-js@npm:^0.1.3": + version: 0.1.3 + resolution: "@tuum-tech/hedera-did-sdk-js@npm:0.1.3" + dependencies: + "@hashgraph/sdk": ^2.50.0 + "@veramo/utils": ^6.0.0 + base58-js: ^2.0.0 + dayjs: ^1.11.13 + did-jwt: ^8.0.4 + did-resolver: ^4.1.0 + elliptic: ^6.6.1 + js-base64: ^3.7.7 + js-sha256: ^0.11.0 + multiformats: ^9.9.0 + checksum: 1091ecda1f96a3995567eae0b0f3264fc12b3a4928e66ae01b4ed3c54ccbb358a2709061a6ffffb1b1a9729a6b238230c781291cfd3d6e62113d7bec668a99c7 + languageName: node + linkType: hard + "@types/aria-query@npm:^5.0.1": version: 5.0.4 resolution: "@types/aria-query@npm:5.0.4" @@ -6534,7 +6458,7 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:*, @types/node@npm:>=10.0.0, @types/node@npm:>=12.12.47, @types/node@npm:>=13.7.0": +"@types/node@npm:*, @types/node@npm:22.7.5, @types/node@npm:>=10.0.0, @types/node@npm:>=12.12.47, @types/node@npm:>=13.7.0": version: 22.7.5 resolution: "@types/node@npm:22.7.5" dependencies: @@ -6682,13 +6606,6 @@ __metadata: languageName: node linkType: hard -"@types/uuid@npm:^10.0.0": - version: 10.0.0 - resolution: "@types/uuid@npm:10.0.0" - checksum: e3958f8b0fe551c86c14431f5940c3470127293280830684154b91dc7eb3514aeb79fe3216968833cf79d4d1c67f580f054b5be2cd562bebf4f728913e73e944 - languageName: node - linkType: hard - "@types/warning@npm:^3.0.0": version: 3.0.3 resolution: "@types/warning@npm:3.0.3" @@ -6974,19 +6891,6 @@ __metadata: languageName: node linkType: hard -"@veramo/credential-eip712@npm:^6.0.0": - version: 6.0.0 - resolution: "@veramo/credential-eip712@npm:6.0.0" - dependencies: - "@metamask/eth-sig-util": ^7.0.0 - "@veramo/core-types": ^6.0.0 - "@veramo/utils": ^6.0.0 - debug: ^4.3.3 - eip-712-types-generation: ^0.1.6 - checksum: efdadc25391fd7cd4c32670b1f664d65ff9118607907f1797e5a40ea97b4dd0374f38374327a1eb31f78f26986d1837b060dc384c6f6bfb28303368c02dd4035 - languageName: node - linkType: hard - "@veramo/credential-ld@npm:^5.1.2": version: 5.6.0 resolution: "@veramo/credential-ld@npm:5.6.0" @@ -7041,6 +6945,16 @@ __metadata: languageName: node linkType: hard +"@veramo/did-discovery@npm:^6.0.0": + version: 6.0.0 + resolution: "@veramo/did-discovery@npm:6.0.0" + dependencies: + "@veramo/core-types": ^6.0.0 + debug: ^4.3.3 + checksum: 29fb77c86129de3c6fcf2815009a0fc453c212211b152c255a89158cfeb6fa8066fd4ab2d3e13bc5c2aa5009227463cf3fea7c27e155d7ad1500de32fe1adaa1 + languageName: node + linkType: hard + "@veramo/did-jwt@npm:5.1.2": version: 5.1.2 resolution: "@veramo/did-jwt@npm:5.1.2" @@ -7074,6 +6988,30 @@ __metadata: languageName: node linkType: hard +"@veramo/did-manager@npm:^6.0.0": + version: 6.0.0 + resolution: "@veramo/did-manager@npm:6.0.0" + dependencies: + "@veramo/core-types": ^6.0.0 + "@veramo/did-discovery": ^6.0.0 + checksum: 59c690400d22d2e9a7a32c2d11130716cfcc9267fd6df0f44b93621f56145b6c48aaaf71ed8a08ba933f308209d2ab7a5fe88a8a259b5fb20d5683b8e2659fe2 + languageName: node + linkType: hard + +"@veramo/did-provider-key@npm:6.0.0": + version: 6.0.0 + resolution: "@veramo/did-provider-key@npm:6.0.0" + dependencies: + "@veramo/core-types": ^6.0.0 + "@veramo/did-manager": ^6.0.0 + "@veramo/utils": ^6.0.0 + debug: ^4.3.3 + did-resolver: ^4.1.0 + ethers: ^6.11.1 + checksum: 08ba16c07f3e3889a79059da9f8d46a65c156b6b244512e5c4dd775efca6205180a1470efb11e5593eefc751662917718f91909cb38ac693b2ce4db5f8b5e5f1 + languageName: node + linkType: hard + "@veramo/did-provider-pkh@npm:5.1.2": version: 5.1.2 resolution: "@veramo/did-provider-pkh@npm:5.1.2" @@ -7418,13 +7356,6 @@ __metadata: languageName: node linkType: hard -"abbrev@npm:^1.0.0": - version: 1.1.1 - resolution: "abbrev@npm:1.1.1" - checksum: a4a97ec07d7ea112c517036882b2ac22f3109b7b19077dc656316d07d308438aac28e4d9746dc4d84bf6b1e75b4a7b0a5f3cb30592419f128ca9a8cee3bcfa17 - languageName: node - linkType: hard - "abbrev@npm:^2.0.0": version: 2.0.0 resolution: "abbrev@npm:2.0.0" @@ -7560,15 +7491,6 @@ __metadata: languageName: node linkType: hard -"agent-base@npm:6, agent-base@npm:^6.0.2": - version: 6.0.2 - resolution: "agent-base@npm:6.0.2" - dependencies: - debug: 4 - checksum: f52b6872cc96fd5f622071b71ef200e01c7c4c454ee68bc9accca90c98cfb39f2810e3e9aa330435835eedc8c23f4f8a15267f67c6e245d2b33757575bdac49d - languageName: node - linkType: hard - "agent-base@npm:^7.0.2, agent-base@npm:^7.1.0, agent-base@npm:^7.1.1": version: 7.1.1 resolution: "agent-base@npm:7.1.1" @@ -7578,15 +7500,6 @@ __metadata: languageName: node linkType: hard -"agentkeepalive@npm:^4.2.1": - version: 4.5.0 - resolution: "agentkeepalive@npm:4.5.0" - dependencies: - humanize-ms: ^1.2.1 - checksum: 13278cd5b125e51eddd5079f04d6fe0914ac1b8b91c1f3db2c1822f99ac1a7457869068997784342fe455d59daaff22e14fb7b8c3da4e741896e7e31faf92481 - languageName: node - linkType: hard - "aggregate-error@npm:^3.0.0": version: 3.1.0 resolution: "aggregate-error@npm:3.1.0" @@ -7796,13 +7709,6 @@ __metadata: languageName: node linkType: hard -"aproba@npm:^1.0.3 || ^2.0.0": - version: 2.0.0 - resolution: "aproba@npm:2.0.0" - checksum: 5615cadcfb45289eea63f8afd064ab656006361020e1735112e346593856f87435e02d8dcc7ff0d11928bc7d425f27bc7c2a84f6c0b35ab0ff659c814c138a24 - languageName: node - linkType: hard - "are-docs-informative@npm:^0.0.2": version: 0.0.2 resolution: "are-docs-informative@npm:0.0.2" @@ -7810,16 +7716,6 @@ __metadata: languageName: node linkType: hard -"are-we-there-yet@npm:^3.0.0": - version: 3.0.1 - resolution: "are-we-there-yet@npm:3.0.1" - dependencies: - delegates: ^1.0.0 - readable-stream: ^3.6.0 - checksum: 52590c24860fa7173bedeb69a4c05fb573473e860197f618b9a28432ee4379049336727ae3a1f9c4cb083114601c1140cee578376164d0e651217a9843f9fe83 - languageName: node - linkType: hard - "arg@npm:^4.1.0": version: 4.1.3 resolution: "arg@npm:4.1.3" @@ -8567,6 +8463,13 @@ __metadata: languageName: node linkType: hard +"base58-js@npm:^2.0.0": + version: 2.0.0 + resolution: "base58-js@npm:2.0.0" + checksum: 1bd713f704d27b9639275966ad53e08a67f4f25be2258d2cce53ae60120c5848997acca2481e011a72a318fd3ff98261bf30fe0f5eddeae2562d02be82b96190 + languageName: node + linkType: hard + "base64-js@npm:*, base64-js@npm:^1.0.2, base64-js@npm:^1.3.0, base64-js@npm:^1.3.1": version: 1.5.1 resolution: "base64-js@npm:1.5.1" @@ -8643,18 +8546,6 @@ __metadata: languageName: node linkType: hard -"bin-links@npm:4.0.1": - version: 4.0.1 - resolution: "bin-links@npm:4.0.1" - dependencies: - cmd-shim: ^6.0.0 - npm-normalize-package-bin: ^3.0.0 - read-cmd-shim: ^4.0.0 - write-file-atomic: ^5.0.0 - checksum: a806561750039bcd7d4234efe5c0b8b7ba0ea8495086740b0da6395abe311e2cdb75f8324787354193f652d2ac5ab038c4ca926ed7bcc6ce9bc2001607741104 - languageName: node - linkType: hard - "binary-extensions@npm:^2.0.0": version: 2.3.0 resolution: "binary-extensions@npm:2.3.0" @@ -9065,32 +8956,6 @@ __metadata: languageName: node linkType: hard -"cacache@npm:^16.1.0": - version: 16.1.3 - resolution: "cacache@npm:16.1.3" - dependencies: - "@npmcli/fs": ^2.1.0 - "@npmcli/move-file": ^2.0.0 - chownr: ^2.0.0 - fs-minipass: ^2.1.0 - glob: ^8.0.1 - infer-owner: ^1.0.4 - lru-cache: ^7.7.1 - minipass: ^3.1.6 - minipass-collect: ^1.0.2 - minipass-flush: ^1.0.5 - minipass-pipeline: ^1.2.4 - mkdirp: ^1.0.4 - p-map: ^4.0.0 - promise-inflight: ^1.0.1 - rimraf: ^3.0.2 - ssri: ^9.0.0 - tar: ^6.1.11 - unique-filename: ^2.0.0 - checksum: d91409e6e57d7d9a3a25e5dcc589c84e75b178ae8ea7de05cbf6b783f77a5fae938f6e8fda6f5257ed70000be27a681e1e44829251bfffe4c10216002f8f14e6 - languageName: node - linkType: hard - "cacache@npm:^18.0.0": version: 18.0.4 resolution: "cacache@npm:18.0.4" @@ -9173,7 +9038,7 @@ __metadata: languageName: node linkType: hard -"caip@npm:^1.1.0, caip@npm:^1.1.1": +"caip@npm:^1.1.0": version: 1.1.1 resolution: "caip@npm:1.1.1" checksum: 37275b80a7d204d35a0fda129e2ac2148f64834e37f86485ba7c8d935b02934ed739df38a722dd02b022e2b3e20f0a69bd9e0ec728d9338fe1876a1652a44974 @@ -9513,17 +9378,6 @@ __metadata: languageName: node linkType: hard -"cliui@npm:^7.0.2": - version: 7.0.4 - resolution: "cliui@npm:7.0.4" - dependencies: - string-width: ^4.2.0 - strip-ansi: ^6.0.0 - wrap-ansi: ^7.0.0 - checksum: ce2e8f578a4813806788ac399b9e866297740eecd4ad1823c27fd344d78b22c5f8597d548adbcc46f0573e43e21e751f39446c5a5e804a12aace402b7a315d7f - languageName: node - linkType: hard - "cliui@npm:^8.0.1": version: 8.0.1 resolution: "cliui@npm:8.0.1" @@ -9569,13 +9423,6 @@ __metadata: languageName: node linkType: hard -"cmd-shim@npm:^6.0.0": - version: 6.0.3 - resolution: "cmd-shim@npm:6.0.3" - checksum: bd79ac1505fea77cba0caf271c16210ebfbe50f348a1907f4700740876ab2157e00882b9baa685a9fcf9bc92e08a87e21bd757f45a6938f00290422f80f7d27a - languageName: node - linkType: hard - "co@npm:^4.6.0": version: 4.6.0 resolution: "co@npm:4.6.0" @@ -9632,15 +9479,6 @@ __metadata: languageName: node linkType: hard -"color-support@npm:^1.1.3": - version: 1.1.3 - resolution: "color-support@npm:1.1.3" - bin: - color-support: bin.js - checksum: 9b7356817670b9a13a26ca5af1c21615463b500783b739b7634a0c2047c16cef4b2865d7576875c31c3cddf9dd621fa19285e628f20198b233a5cfdda6d0793b - languageName: node - linkType: hard - "color@npm:^4.2.3": version: 4.2.3 resolution: "color@npm:4.2.3" @@ -9842,13 +9680,6 @@ __metadata: languageName: node linkType: hard -"console-control-strings@npm:^1.1.0": - version: 1.1.0 - resolution: "console-control-strings@npm:1.1.0" - checksum: 8755d76787f94e6cf79ce4666f0c5519906d7f5b02d4b884cf41e11dcd759ed69c57da0670afd9236d229a46e0f9cf519db0cd829c6dca820bb5a5c3def584ed - languageName: node - linkType: hard - "constant-case@npm:^3.0.4": version: 3.0.4 resolution: "constant-case@npm:3.0.4" @@ -10564,6 +10395,13 @@ __metadata: languageName: node linkType: hard +"dayjs@npm:^1.11.13": + version: 1.11.13 + resolution: "dayjs@npm:1.11.13" + checksum: f388db88a6aa93956c1f6121644e783391c7b738b73dbc54485578736565c8931bdfba4bb94e9b1535c6e509c97d5deb918bbe1ae6b34358d994de735055cca9 + languageName: node + linkType: hard + "debug@npm:2.6.9, debug@npm:^2.6.0": version: 2.6.9 resolution: "debug@npm:2.6.9" @@ -10742,13 +10580,6 @@ __metadata: languageName: node linkType: hard -"delegates@npm:^1.0.0": - version: 1.0.0 - resolution: "delegates@npm:1.0.0" - checksum: a51744d9b53c164ba9c0492471a1a2ffa0b6727451bdc89e31627fdf4adda9d51277cfcbfb20f0a6f08ccb3c436f341df3e92631a3440226d93a8971724771fd - languageName: node - linkType: hard - "depd@npm:2.0.0": version: 2.0.0 resolution: "depd@npm:2.0.0" @@ -10915,7 +10746,7 @@ __metadata: languageName: node linkType: hard -"did-jwt-vc@npm:^3.1.0, did-jwt-vc@npm:^3.1.3, did-jwt-vc@npm:^3.2.10": +"did-jwt-vc@npm:^3.1.0, did-jwt-vc@npm:^3.2.10": version: 3.2.15 resolution: "did-jwt-vc@npm:3.2.15" dependencies: @@ -10925,7 +10756,7 @@ __metadata: languageName: node linkType: hard -"did-jwt-vc@npm:^4.0.0": +"did-jwt-vc@npm:^4.0.0, did-jwt-vc@npm:^4.0.4": version: 4.0.4 resolution: "did-jwt-vc@npm:4.0.4" dependencies: @@ -10972,7 +10803,7 @@ __metadata: languageName: node linkType: hard -"did-jwt@npm:^8.0.0": +"did-jwt@npm:^8.0.0, did-jwt@npm:^8.0.4": version: 8.0.4 resolution: "did-jwt@npm:8.0.4" dependencies: @@ -11255,15 +11086,6 @@ __metadata: languageName: node linkType: hard -"eip-712-types-generation@npm:^0.1.6": - version: 0.1.6 - resolution: "eip-712-types-generation@npm:0.1.6" - dependencies: - json-canonicalize: ^1.0.4 - checksum: 88cceca91b5b39c17732f743c28b0c324b5a6f9954b51e6743721e1d211deba68f719aeacdbe54c83ef1139f9026a9e13de1b78e9e967e5492a73b76cf7c948e - languageName: node - linkType: hard - "ejs@npm:^3.1.10": version: 3.1.10 resolution: "ejs@npm:3.1.10" @@ -11312,6 +11134,21 @@ __metadata: languageName: node linkType: hard +"elliptic@npm:^6.5.7, elliptic@npm:^6.6.1": + version: 6.6.1 + resolution: "elliptic@npm:6.6.1" + dependencies: + bn.js: ^4.11.9 + brorand: ^1.1.0 + hash.js: ^1.0.0 + hmac-drbg: ^1.0.1 + inherits: ^2.0.4 + minimalistic-assert: ^1.0.1 + minimalistic-crypto-utils: ^1.0.1 + checksum: 27b14a52f68bbbc0720da259f712cb73e953f6d2047958cd02fb0d0ade2e83849dc39fb4af630889c67df8817e24237428cf59c4f4c07700f755b401149a7375 + languageName: node + linkType: hard + "emittery@npm:^0.13.1": version: 0.13.1 resolution: "emittery@npm:0.13.1" @@ -12379,7 +12216,22 @@ __metadata: languageName: node linkType: hard -"ethers@npm:^6.11.1, ethers@npm:^6.13.2, ethers@npm:^6.9.0": +"ethers@npm:^6.11.1": + version: 6.13.4 + resolution: "ethers@npm:6.13.4" + dependencies: + "@adraffy/ens-normalize": 1.10.1 + "@noble/curves": 1.2.0 + "@noble/hashes": 1.3.2 + "@types/node": 22.7.5 + aes-js: 4.0.0-beta.5 + tslib: 2.7.0 + ws: 8.17.1 + checksum: a64ad0f05ed7f79bf3092cd54ac11c3ed4a0a3fe8ee00a81053b5b4a34d84728c12fa5aa9bf3e2cc5efabbf1a0a37f62cd3a1852cf780a1ab619421fa03c2713 + languageName: node + linkType: hard + +"ethers@npm:^6.13.2, ethers@npm:^6.9.0": version: 6.13.3 resolution: "ethers@npm:6.13.3" dependencies: @@ -13116,7 +12968,7 @@ __metadata: languageName: node linkType: hard -"fs-minipass@npm:^2.0.0, fs-minipass@npm:^2.1.0": +"fs-minipass@npm:^2.0.0": version: 2.1.0 resolution: "fs-minipass@npm:2.1.0" dependencies: @@ -13694,22 +13546,6 @@ __metadata: languageName: node linkType: hard -"gauge@npm:^4.0.3": - version: 4.0.4 - resolution: "gauge@npm:4.0.4" - dependencies: - aproba: ^1.0.3 || ^2.0.0 - color-support: ^1.1.3 - console-control-strings: ^1.1.0 - has-unicode: ^2.0.1 - signal-exit: ^3.0.7 - string-width: ^4.2.3 - strip-ansi: ^6.0.1 - wide-align: ^1.1.5 - checksum: 788b6bfe52f1dd8e263cda800c26ac0ca2ff6de0b6eee2fe0d9e3abf15e149b651bd27bf5226be10e6e3edb5c4e5d5985a5a1a98137e7a892f75eff76467ad2d - languageName: node - linkType: hard - "gensync@npm:^1.0.0-beta.2": version: 1.0.0-beta.2 resolution: "gensync@npm:1.0.0-beta.2" @@ -13910,19 +13746,6 @@ __metadata: languageName: node linkType: hard -"glob@npm:^8.0.1": - version: 8.1.0 - resolution: "glob@npm:8.1.0" - dependencies: - fs.realpath: ^1.0.0 - inflight: ^1.0.4 - inherits: 2 - minimatch: ^5.0.1 - once: ^1.3.0 - checksum: 92fbea3221a7d12075f26f0227abac435de868dd0736a17170663783296d0dd8d3d532a5672b4488a439bf5d7fb85cdd07c11185d6cd39184f0385cbdfb86a47 - languageName: node - linkType: hard - "global-modules@npm:^2.0.0": version: 2.0.0 resolution: "global-modules@npm:2.0.0" @@ -14192,13 +14015,6 @@ __metadata: languageName: node linkType: hard -"has-unicode@npm:^2.0.1": - version: 2.0.1 - resolution: "has-unicode@npm:2.0.1" - checksum: 1eab07a7436512db0be40a710b29b5dc21fa04880b7f63c9980b706683127e3c1b57cb80ea96d47991bdae2dfe479604f6a1ba410106ee1046a41d1bd0814400 - languageName: node - linkType: hard - "hash-base@npm:^3.0.0": version: 3.1.0 resolution: "hash-base@npm:3.1.0" @@ -14344,7 +14160,7 @@ __metadata: languageName: node linkType: hard -"http-cache-semantics@npm:^4.0.0, http-cache-semantics@npm:^4.1.0, http-cache-semantics@npm:^4.1.1": +"http-cache-semantics@npm:^4.0.0, http-cache-semantics@npm:^4.1.1": version: 4.1.1 resolution: "http-cache-semantics@npm:4.1.1" checksum: 83ac0bc60b17a3a36f9953e7be55e5c8f41acc61b22583060e8dedc9dd5e3607c823a88d0926f9150e571f90946835c7fe150732801010845c72cd8bbff1a236 @@ -14364,17 +14180,6 @@ __metadata: languageName: node linkType: hard -"http-proxy-agent@npm:^5.0.0": - version: 5.0.0 - resolution: "http-proxy-agent@npm:5.0.0" - dependencies: - "@tootallnate/once": 2 - agent-base: 6 - debug: 4 - checksum: e2ee1ff1656a131953839b2a19cd1f3a52d97c25ba87bd2559af6ae87114abf60971e498021f9b73f9fd78aea8876d1fb0d4656aac8a03c6caa9fc175f22b786 - languageName: node - linkType: hard - "http-proxy-agent@npm:^7.0.0": version: 7.0.2 resolution: "http-proxy-agent@npm:7.0.2" @@ -14412,16 +14217,6 @@ __metadata: languageName: node linkType: hard -"https-proxy-agent@npm:^5.0.0": - version: 5.0.1 - resolution: "https-proxy-agent@npm:5.0.1" - dependencies: - agent-base: 6 - debug: 4 - checksum: 571fccdf38184f05943e12d37d6ce38197becdd69e58d03f43637f7fa1269cf303a7d228aa27e5b27bbd3af8f09fd938e1c91dcfefff2df7ba77c20ed8dfc765 - languageName: node - linkType: hard - "https-proxy-agent@npm:^7.0.1": version: 7.0.5 resolution: "https-proxy-agent@npm:7.0.5" @@ -14446,15 +14241,6 @@ __metadata: languageName: node linkType: hard -"humanize-ms@npm:^1.2.1": - version: 1.2.1 - resolution: "humanize-ms@npm:1.2.1" - dependencies: - ms: ^2.0.0 - checksum: 9c7a74a2827f9294c009266c82031030eae811ca87b0da3dceb8d6071b9bde22c9f3daef0469c3c533cc67a97d8a167cd9fc0389350e5f415f61a79b171ded16 - languageName: node - linkType: hard - "iconv-lite@npm:0.4.24, iconv-lite@npm:^0.4.24": version: 0.4.24 resolution: "iconv-lite@npm:0.4.24" @@ -14569,13 +14355,6 @@ __metadata: languageName: node linkType: hard -"infer-owner@npm:^1.0.4": - version: 1.0.4 - resolution: "infer-owner@npm:1.0.4" - checksum: 181e732764e4a0611576466b4b87dac338972b839920b2a8cde43642e4ed6bd54dc1fb0b40874728f2a2df9a1b097b8ff83b56d5f8f8e3927f837fdcb47d8a89 - languageName: node - linkType: hard - "inflight@npm:^1.0.4": version: 1.0.6 resolution: "inflight@npm:1.0.6" @@ -15955,7 +15734,7 @@ __metadata: languageName: node linkType: hard -"js-base64@npm:^3.7.4": +"js-base64@npm:^3.7.4, js-base64@npm:^3.7.7": version: 3.7.7 resolution: "js-base64@npm:3.7.7" checksum: d1b02971db9dc0fd35baecfaf6ba499731fb44fe3373e7e1d6681fbd3ba665f29e8d9d17910254ef8104e2cb8b44117fe4202d3dc54c7cafe9ba300fe5433358 @@ -16050,13 +15829,6 @@ __metadata: languageName: node linkType: hard -"json-canonicalize@npm:^1.0.4": - version: 1.0.6 - resolution: "json-canonicalize@npm:1.0.6" - checksum: 50f0ea9a0b14fe4f4b67e2e90246b629410824228f42a132b97252ed3729b23b6c52334746770bc1cdd37eb952e2fe5e38aa2d546507f107fdb9d07ea3f3744f - languageName: node - linkType: hard - "json-loader@npm:^0.5.7": version: 0.5.7 resolution: "json-loader@npm:0.5.7" @@ -16071,13 +15843,6 @@ __metadata: languageName: node linkType: hard -"json-parse-even-better-errors@npm:^3.0.0": - version: 3.0.2 - resolution: "json-parse-even-better-errors@npm:3.0.2" - checksum: 6f04ea6c9ccb783630a59297959247e921cc90b917b8351197ca7fd058fccc7079268fd9362be21ba876fc26aa5039369dd0a2280aae49aae425784794a94927 - languageName: node - linkType: hard - "json-pointer@npm:^0.6.2": version: 0.6.2 resolution: "json-pointer@npm:0.6.2" @@ -16734,13 +16499,6 @@ __metadata: languageName: node linkType: hard -"lru-cache@npm:^7.7.1": - version: 7.18.3 - resolution: "lru-cache@npm:7.18.3" - checksum: e550d772384709deea3f141af34b6d4fa392e2e418c1498c078de0ee63670f1f46f5eee746e8ef7e69e1c895af0d4224e62ee33e66a543a14763b0f2e74c1356 - languageName: node - linkType: hard - "lru-queue@npm:^0.1.0": version: 0.1.0 resolution: "lru-queue@npm:0.1.0" @@ -16791,30 +16549,6 @@ __metadata: languageName: node linkType: hard -"make-fetch-happen@npm:^10.0.3": - version: 10.2.1 - resolution: "make-fetch-happen@npm:10.2.1" - dependencies: - agentkeepalive: ^4.2.1 - cacache: ^16.1.0 - http-cache-semantics: ^4.1.0 - http-proxy-agent: ^5.0.0 - https-proxy-agent: ^5.0.0 - is-lambda: ^1.0.1 - lru-cache: ^7.7.1 - minipass: ^3.1.6 - minipass-collect: ^1.0.2 - minipass-fetch: ^2.0.3 - minipass-flush: ^1.0.5 - minipass-pipeline: ^1.2.4 - negotiator: ^0.6.3 - promise-retry: ^2.0.1 - socks-proxy-agent: ^7.0.0 - ssri: ^9.0.0 - checksum: 2332eb9a8ec96f1ffeeea56ccefabcb4193693597b132cd110734d50f2928842e22b84cfa1508e921b8385cdfd06dda9ad68645fed62b50fff629a580f5fb72c - languageName: node - linkType: hard - "make-fetch-happen@npm:^13.0.0": version: 13.0.1 resolution: "make-fetch-happen@npm:13.0.1" @@ -17199,15 +16933,6 @@ __metadata: languageName: node linkType: hard -"minipass-collect@npm:^1.0.2": - version: 1.0.2 - resolution: "minipass-collect@npm:1.0.2" - dependencies: - minipass: ^3.0.0 - checksum: 14df761028f3e47293aee72888f2657695ec66bd7d09cae7ad558da30415fdc4752bbfee66287dcc6fd5e6a2fa3466d6c484dc1cbd986525d9393b9523d97f10 - languageName: node - linkType: hard - "minipass-collect@npm:^2.0.1": version: 2.0.1 resolution: "minipass-collect@npm:2.0.1" @@ -17217,21 +16942,6 @@ __metadata: languageName: node linkType: hard -"minipass-fetch@npm:^2.0.3": - version: 2.1.2 - resolution: "minipass-fetch@npm:2.1.2" - dependencies: - encoding: ^0.1.13 - minipass: ^3.1.6 - minipass-sized: ^1.0.3 - minizlib: ^2.1.2 - dependenciesMeta: - encoding: - optional: true - checksum: 3f216be79164e915fc91210cea1850e488793c740534985da017a4cbc7a5ff50506956d0f73bb0cb60e4fe91be08b6b61ef35101706d3ef5da2c8709b5f08f91 - languageName: node - linkType: hard - "minipass-fetch@npm:^3.0.0": version: 3.0.5 resolution: "minipass-fetch@npm:3.0.5" @@ -17274,7 +16984,7 @@ __metadata: languageName: node linkType: hard -"minipass@npm:^3.0.0, minipass@npm:^3.1.1, minipass@npm:^3.1.6": +"minipass@npm:^3.0.0": version: 3.3.6 resolution: "minipass@npm:3.3.6" dependencies: @@ -17380,7 +17090,7 @@ __metadata: languageName: node linkType: hard -"ms@npm:2.1.3, ms@npm:^2.0.0, ms@npm:^2.1.1, ms@npm:^2.1.3": +"ms@npm:2.1.3, ms@npm:^2.1.1, ms@npm:^2.1.3": version: 2.1.3 resolution: "ms@npm:2.1.3" checksum: aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d @@ -17475,7 +17185,7 @@ __metadata: languageName: node linkType: hard -"multiformats@npm:^9.4.2, multiformats@npm:^9.6.2, multiformats@npm:^9.6.5": +"multiformats@npm:^9.4.2, multiformats@npm:^9.6.2, multiformats@npm:^9.6.5, multiformats@npm:^9.9.0": version: 9.9.0 resolution: "multiformats@npm:9.9.0" checksum: d3e8c1be400c09a014f557ea02251a2710dbc9fca5aa32cc702ff29f636c5471e17979f30bdcb0a9cbb556f162a8591dc2e1219c24fc21394a56115b820bb84e @@ -17688,27 +17398,6 @@ __metadata: languageName: node linkType: hard -"node-gyp@npm:^9.0.0": - version: 9.4.1 - resolution: "node-gyp@npm:9.4.1" - dependencies: - env-paths: ^2.2.0 - exponential-backoff: ^3.1.1 - glob: ^7.1.4 - graceful-fs: ^4.2.6 - make-fetch-happen: ^10.0.3 - nopt: ^6.0.0 - npmlog: ^6.0.0 - rimraf: ^3.0.2 - semver: ^7.3.5 - tar: ^6.1.2 - which: ^2.0.2 - bin: - node-gyp: bin/node-gyp.js - checksum: 8576c439e9e925ab50679f87b7dfa7aa6739e42822e2ad4e26c36341c0ba7163fdf5a946f0a67a476d2f24662bc40d6c97bd9e79ced4321506738e6b760a1577 - languageName: node - linkType: hard - "node-gyp@npm:latest": version: 10.2.0 resolution: "node-gyp@npm:10.2.0" @@ -17760,17 +17449,6 @@ __metadata: languageName: node linkType: hard -"nopt@npm:^6.0.0": - version: 6.0.0 - resolution: "nopt@npm:6.0.0" - dependencies: - abbrev: ^1.0.0 - bin: - nopt: bin/nopt.js - checksum: 82149371f8be0c4b9ec2f863cc6509a7fd0fa729929c009f3a58e4eb0c9e4cae9920e8f1f8eb46e7d032fec8fb01bede7f0f41a67eb3553b7b8e14fa53de1dac - languageName: node - linkType: hard - "nopt@npm:^7.0.0": version: 7.2.1 resolution: "nopt@npm:7.2.1" @@ -17819,13 +17497,6 @@ __metadata: languageName: node linkType: hard -"npm-normalize-package-bin@npm:^3.0.0": - version: 3.0.1 - resolution: "npm-normalize-package-bin@npm:3.0.1" - checksum: de416d720ab22137a36292ff8a333af499ea0933ef2320a8c6f56a73b0f0448227fec4db5c890d702e26d21d04f271415eab6580b5546456861cc0c19498a4bf - languageName: node - linkType: hard - "npm-run-path@npm:^4.0.1": version: 4.0.1 resolution: "npm-run-path@npm:4.0.1" @@ -17844,18 +17515,6 @@ __metadata: languageName: node linkType: hard -"npmlog@npm:^6.0.0": - version: 6.0.2 - resolution: "npmlog@npm:6.0.2" - dependencies: - are-we-there-yet: ^3.0.0 - console-control-strings: ^1.1.0 - gauge: ^4.0.3 - set-blocking: ^2.0.0 - checksum: ae238cd264a1c3f22091cdd9e2b106f684297d3c184f1146984ecbe18aaa86343953f26b9520dedd1b1372bc0316905b736c1932d778dbeb1fcf5a1001390e2a - languageName: node - linkType: hard - "nth-check@npm:^2.0.1": version: 2.1.1 resolution: "nth-check@npm:2.1.1" @@ -19207,13 +18866,6 @@ __metadata: languageName: node linkType: hard -"promise-inflight@npm:^1.0.1": - version: 1.0.1 - resolution: "promise-inflight@npm:1.0.1" - checksum: 22749483091d2c594261517f4f80e05226d4d5ecc1fc917e1886929da56e22b5718b7f2a75f3807e7a7d471bc3be2907fe92e6e8f373ddf5c64bae35b5af3981 - languageName: node - linkType: hard - "promise-retry@npm:^2.0.1": version: 2.0.1 resolution: "promise-retry@npm:2.0.1" @@ -19762,13 +19414,6 @@ __metadata: languageName: node linkType: hard -"read-cmd-shim@npm:^4.0.0": - version: 4.0.0 - resolution: "read-cmd-shim@npm:4.0.0" - checksum: 2fb5a8a38984088476f559b17c6a73324a5db4e77e210ae0aab6270480fd85c355fc990d1c79102e25e555a8201606ed12844d6e3cd9f35d6a1518791184e05b - languageName: node - linkType: hard - "read-only-stream@npm:^2.0.0": version: 2.0.0 resolution: "read-only-stream@npm:2.0.0" @@ -19778,16 +19423,6 @@ __metadata: languageName: node linkType: hard -"read-package-json-fast@npm:^3.0.0": - version: 3.0.2 - resolution: "read-package-json-fast@npm:3.0.2" - dependencies: - json-parse-even-better-errors: ^3.0.0 - npm-normalize-package-bin: ^3.0.0 - checksum: 8d406869f045f1d76e2a99865a8fd1c1af9c1dc06200b94d2b07eef87ed734b22703a8d72e1cd36ea36cc48e22020bdd187f88243c7dd0563f72114d38c17072 - languageName: node - linkType: hard - "read@npm:^1.0.7": version: 1.0.7 resolution: "read@npm:1.0.7" @@ -20140,7 +19775,7 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.1.4, resolve@npm:^1.10.0, resolve@npm:^1.10.1, resolve@npm:^1.14.2, resolve@npm:^1.17.0, resolve@npm:^1.19.0, resolve@npm:^1.20.0, resolve@npm:^1.22.3, resolve@npm:^1.22.4, resolve@npm:^1.4.0": +"resolve@npm:^1.1.4, resolve@npm:^1.10.0, resolve@npm:^1.10.1, resolve@npm:^1.14.2, resolve@npm:^1.17.0, resolve@npm:^1.19.0, resolve@npm:^1.20.0, resolve@npm:^1.22.4, resolve@npm:^1.4.0": version: 1.22.8 resolution: "resolve@npm:1.22.8" dependencies: @@ -20166,7 +19801,7 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@^1.1.4#~builtin, resolve@patch:resolve@^1.10.0#~builtin, resolve@patch:resolve@^1.10.1#~builtin, resolve@patch:resolve@^1.14.2#~builtin, resolve@patch:resolve@^1.17.0#~builtin, resolve@patch:resolve@^1.19.0#~builtin, resolve@patch:resolve@^1.20.0#~builtin, resolve@patch:resolve@^1.22.3#~builtin, resolve@patch:resolve@^1.22.4#~builtin, resolve@patch:resolve@^1.4.0#~builtin": +"resolve@patch:resolve@^1.1.4#~builtin, resolve@patch:resolve@^1.10.0#~builtin, resolve@patch:resolve@^1.10.1#~builtin, resolve@patch:resolve@^1.14.2#~builtin, resolve@patch:resolve@^1.17.0#~builtin, resolve@patch:resolve@^1.19.0#~builtin, resolve@patch:resolve@^1.20.0#~builtin, resolve@patch:resolve@^1.22.4#~builtin, resolve@patch:resolve@^1.4.0#~builtin": version: 1.22.8 resolution: "resolve@patch:resolve@npm%3A1.22.8#~builtin::version=1.22.8&hash=07638b" dependencies: @@ -20441,15 +20076,15 @@ __metadata: languageName: node linkType: hard -"secp256k1@npm:^5.0.0": - version: 5.0.0 - resolution: "secp256k1@npm:5.0.0" +"secp256k1@npm:^5.0.1": + version: 5.0.1 + resolution: "secp256k1@npm:5.0.1" dependencies: - elliptic: ^6.5.4 + elliptic: ^6.5.7 node-addon-api: ^5.0.0 node-gyp: latest node-gyp-build: ^4.2.0 - checksum: a0719dff4687c38d385b5e0b7e811c51a4ea24893128be9d097aee99f879eb0ea52582590deb15a49da627a3db23c6b028ad5c9c6ac1fca92ce760153b8cf21c + checksum: e21fb801502fe03a233f04c294cfdf16bd6087c36caa6514ccc5eac38ebd5ff50090a59d0ee7d50adf87f6d508a8211d09b905290fac97b4d43751967b7dfd9e languageName: node linkType: hard @@ -20884,17 +20519,6 @@ __metadata: languageName: node linkType: hard -"socks-proxy-agent@npm:^7.0.0": - version: 7.0.0 - resolution: "socks-proxy-agent@npm:7.0.0" - dependencies: - agent-base: ^6.0.2 - debug: ^4.3.3 - socks: ^2.6.2 - checksum: 720554370154cbc979e2e9ce6a6ec6ced205d02757d8f5d93fe95adae454fc187a5cbfc6b022afab850a5ce9b4c7d73e0f98e381879cf45f66317a4895953846 - languageName: node - linkType: hard - "socks-proxy-agent@npm:^8.0.3": version: 8.0.4 resolution: "socks-proxy-agent@npm:8.0.4" @@ -20906,7 +20530,7 @@ __metadata: languageName: node linkType: hard -"socks@npm:^2.6.2, socks@npm:^2.8.3": +"socks@npm:^2.8.3": version: 2.8.3 resolution: "socks@npm:2.8.3" dependencies: @@ -21082,15 +20706,6 @@ __metadata: languageName: node linkType: hard -"ssri@npm:^9.0.0": - version: 9.0.1 - resolution: "ssri@npm:9.0.1" - dependencies: - minipass: ^3.1.1 - checksum: fb58f5e46b6923ae67b87ad5ef1c5ab6d427a17db0bead84570c2df3cd50b4ceb880ebdba2d60726588272890bae842a744e1ecce5bd2a2a582fccd5068309eb - languageName: node - linkType: hard - "stable@npm:^0.1.8": version: 0.1.8 resolution: "stable@npm:0.1.8" @@ -21254,7 +20869,7 @@ __metadata: languageName: node linkType: hard -"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^1.0.2 || 2 || 3 || 4, string-width@npm:^4.0.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.2, string-width@npm:^4.2.3": +"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.0.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.2, string-width@npm:^4.2.3": version: 4.2.3 resolution: "string-width@npm:4.2.3" dependencies: @@ -21766,7 +21381,7 @@ __metadata: languageName: node linkType: hard -"tar@npm:^6.1.11, tar@npm:^6.1.2, tar@npm:^6.2.1": +"tar@npm:^6.1.11, tar@npm:^6.2.1": version: 6.2.1 resolution: "tar@npm:6.2.1" dependencies: @@ -22086,6 +21701,13 @@ __metadata: languageName: node linkType: hard +"tslib@npm:2.7.0, tslib@npm:^2.0.0, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.4.0, tslib@npm:^2.6.1, tslib@npm:^2.6.2, tslib@npm:^2.7.0": + version: 2.7.0 + resolution: "tslib@npm:2.7.0" + checksum: 1606d5c89f88d466889def78653f3aab0f88692e80bb2066d090ca6112ae250ec1cfa9dbfaab0d17b60da15a4186e8ec4d893801c67896b277c17374e36e1d28 + languageName: node + linkType: hard + "tslib@npm:^1.10.0, tslib@npm:^1.8.1, tslib@npm:^1.9.0": version: 1.14.1 resolution: "tslib@npm:1.14.1" @@ -22093,13 +21715,6 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.0.0, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.4.0, tslib@npm:^2.6.1, tslib@npm:^2.6.2, tslib@npm:^2.7.0": - version: 2.7.0 - resolution: "tslib@npm:2.7.0" - checksum: 1606d5c89f88d466889def78653f3aab0f88692e80bb2066d090ca6112ae250ec1cfa9dbfaab0d17b60da15a4186e8ec4d893801c67896b277c17374e36e1d28 - languageName: node - linkType: hard - "tslib@npm:~2.4.0": version: 2.4.1 resolution: "tslib@npm:2.4.1" @@ -22499,15 +22114,6 @@ __metadata: languageName: node linkType: hard -"unique-filename@npm:^2.0.0": - version: 2.0.1 - resolution: "unique-filename@npm:2.0.1" - dependencies: - unique-slug: ^3.0.0 - checksum: 807acf3381aff319086b64dc7125a9a37c09c44af7620bd4f7f3247fcd5565660ac12d8b80534dcbfd067e6fe88a67e621386dd796a8af828d1337a8420a255f - languageName: node - linkType: hard - "unique-filename@npm:^3.0.0": version: 3.0.0 resolution: "unique-filename@npm:3.0.0" @@ -22517,15 +22123,6 @@ __metadata: languageName: node linkType: hard -"unique-slug@npm:^3.0.0": - version: 3.0.0 - resolution: "unique-slug@npm:3.0.0" - dependencies: - imurmurhash: ^0.1.4 - checksum: 49f8d915ba7f0101801b922062ee46b7953256c93ceca74303bd8e6413ae10aa7e8216556b54dc5382895e8221d04f1efaf75f945c2e4a515b4139f77aa6640c - languageName: node - linkType: hard - "unique-slug@npm:^4.0.0": version: 4.0.0 resolution: "unique-slug@npm:4.0.0" @@ -22704,15 +22301,6 @@ __metadata: languageName: node linkType: hard -"uuid@npm:^10.0.0": - version: 10.0.0 - resolution: "uuid@npm:10.0.0" - bin: - uuid: dist/bin/uuid - checksum: 4b81611ade2885d2313ddd8dc865d93d8dccc13ddf901745edca8f86d99bc46d7a330d678e7532e7ebf93ce616679fb19b2e3568873ac0c14c999032acb25869 - languageName: node - linkType: hard - "uuid@npm:^8.3.2": version: 8.3.2 resolution: "uuid@npm:8.3.2" @@ -23056,7 +22644,7 @@ __metadata: languageName: node linkType: hard -"which@npm:^2.0.1, which@npm:^2.0.2": +"which@npm:^2.0.1": version: 2.0.2 resolution: "which@npm:2.0.2" dependencies: @@ -23067,17 +22655,6 @@ __metadata: languageName: node linkType: hard -"which@npm:^3.0.0": - version: 3.0.1 - resolution: "which@npm:3.0.1" - dependencies: - isexe: ^2.0.0 - bin: - node-which: bin/which.js - checksum: adf720fe9d84be2d9190458194f814b5e9015ae4b88711b150f30d0f4d0b646544794b86f02c7ebeec1db2029bc3e83a7ff156f542d7521447e5496543e26890 - languageName: node - linkType: hard - "which@npm:^4.0.0": version: 4.0.0 resolution: "which@npm:4.0.0" @@ -23089,15 +22666,6 @@ __metadata: languageName: node linkType: hard -"wide-align@npm:^1.1.5": - version: 1.1.5 - resolution: "wide-align@npm:1.1.5" - dependencies: - string-width: ^1.0.2 || 2 || 3 || 4 - checksum: d5fc37cd561f9daee3c80e03b92ed3e84d80dde3365a8767263d03dacfc8fa06b065ffe1df00d8c2a09f731482fcacae745abfbb478d4af36d0a891fad4834d3 - languageName: node - linkType: hard - "widest-line@npm:^3.1.0": version: 3.1.0 resolution: "widest-line@npm:3.1.0" @@ -23183,16 +22751,6 @@ __metadata: languageName: node linkType: hard -"write-file-atomic@npm:^5.0.0": - version: 5.0.1 - resolution: "write-file-atomic@npm:5.0.1" - dependencies: - imurmurhash: ^0.1.4 - signal-exit: ^4.0.1 - checksum: 8dbb0e2512c2f72ccc20ccedab9986c7d02d04039ed6e8780c987dc4940b793339c50172a1008eed7747001bfacc0ca47562668a069a7506c46c77d7ba3926a9 - languageName: node - linkType: hard - "ws@npm:8.17.1, ws@npm:~8.17.1": version: 8.17.1 resolution: "ws@npm:8.17.1" @@ -23322,13 +22880,6 @@ __metadata: languageName: node linkType: hard -"yargs-parser@npm:^20.2.2": - version: 20.2.9 - resolution: "yargs-parser@npm:20.2.9" - checksum: 8bb69015f2b0ff9e17b2c8e6bfe224ab463dd00ca211eece72a4cd8a906224d2703fb8a326d36fdd0e68701e201b2a60ed7cf81ce0fd9b3799f9fe7745977ae3 - languageName: node - linkType: hard - "yargs-parser@npm:^21.1.1": version: 21.1.1 resolution: "yargs-parser@npm:21.1.1" @@ -23355,21 +22906,6 @@ __metadata: languageName: node linkType: hard -"yargs@npm:^16.2.0": - version: 16.2.0 - resolution: "yargs@npm:16.2.0" - dependencies: - cliui: ^7.0.2 - escalade: ^3.1.1 - get-caller-file: ^2.0.5 - require-directory: ^2.1.1 - string-width: ^4.2.0 - y18n: ^5.0.5 - yargs-parser: ^20.2.2 - checksum: b14afbb51e3251a204d81937c86a7e9d4bdbf9a2bcee38226c900d00f522969ab675703bee2a6f99f8e20103f608382936034e64d921b74df82b63c07c5e8f59 - languageName: node - linkType: hard - "yargs@npm:^17.0.1, yargs@npm:^17.3.1, yargs@npm:^17.7.1, yargs@npm:^17.7.2": version: 17.7.2 resolution: "yargs@npm:17.7.2"