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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: 'lts/*'
node-version: '22'

- name: Install dependencies
run: yarn install
Expand Down
270 changes: 269 additions & 1 deletion CHANGELOG.md

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zenchain-protocol/zazen",
"version": "0.0.6",
"version": "0.0.7",
"description": "Zenchain snap for securely storing validator cloud access keys within Metamask.",
"repository": {
"type": "git",
Expand Down Expand Up @@ -70,6 +70,6 @@
"registry": "https://registry.npmjs.org/"
},
"dependencies": {
"@metamask/snaps-sdk": "^6.12.0"
"@metamask/snaps-sdk": "^10.0.0"
}
}
7 changes: 4 additions & 3 deletions snap.manifest.dev.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"version": "0.0.6",
"version": "0.0.7",
"description": "Zenchain snap for securely storing validator cloud access keys within Metamask.",
"proposedName": "Zazen",
"repository": {
"type": "git",
"url": "git+https://github.com/zenchain-protocol/zazen.git"
},
"source": {
"shasum": "tCAJNv7Lvgt24ZCC0C8tWNlWBxhcwSkkhQuNe5bti6s=",
"shasum": "JfUd+JRXe9vwdo8nK7zXNvGqj5boaeWOKWpcJiEo7VE=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand All @@ -29,5 +29,6 @@
},
"snap_manageState": {}
},
"platformVersion": "10.1.0",
"manifestVersion": "0.1"
}
}
8 changes: 5 additions & 3 deletions snap.manifest.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"version": "0.0.6",
"version": "0.0.7",
"description": "Zenchain snap for securely storing validator cloud access keys within Metamask.",
"proposedName": "Zazen",
"repository": {
"type": "git",
"url": "git+https://github.com/zenchain-protocol/zazen.git"
},
"source": {
"shasum": "tCAJNv7Lvgt24ZCC0C8tWNlWBxhcwSkkhQuNe5bti6s=",
"shasum": "JfUd+JRXe9vwdo8nK7zXNvGqj5boaeWOKWpcJiEo7VE=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand All @@ -24,7 +24,9 @@
"npm:@zenchain-protocol/zazen"
]
},
"snap_manageState": {}
"snap_manageState": {},
"snap_dialog": {}
},
"platformVersion": "10.1.0",
"manifestVersion": "0.1"
}
113 changes: 104 additions & 9 deletions src/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { expect } from '@jest/globals';
import { installSnap } from '@metamask/snaps-jest';
import {installSnap, SnapConfirmationInterface, SnapInterfaceActions} from '@metamask/snaps-jest';

describe('onRpcRequest', () => {
it('throws an error if the requested method does not exist', async () => {
Expand Down Expand Up @@ -39,7 +39,10 @@ describe('onRpcRequest', () => {
},
};

expect(await request(params)).toRespondWith(true);
const response = request(params);
const ui = await response.getInterface() as (SnapConfirmationInterface & SnapInterfaceActions);
await ui.ok();
expect(await response).toRespondWith(true);

const state = await request({
method: 'getState',
Expand All @@ -57,6 +60,46 @@ describe('onRpcRequest', () => {
},
});
});

it('does not set the state when the user cancels the confirmation', async () => {
const { request } = await installSnap();

const params = {
method: 'setState',
params: {
staking: {
nodeCloudAccessKeys: {
onFinality: {
accessKey: 'barAccessKey',
secretKey: 'barSecretKey',
workspaceId: 'barWorkspaceId',
},
},
},
},
};

const response = request(params);
const ui = await response.getInterface() as (SnapConfirmationInterface & SnapInterfaceActions);
await ui.cancel();
await response;

const state = await request({
method: 'getState',
});

expect(state).toRespondWith({
staking: {
nodeCloudAccessKeys: {
onFinality: {
accessKey: '',
secretKey: '',
workspaceId: '',
},
},
},
});
});
});

describe('getState', () => {
Expand Down Expand Up @@ -98,7 +141,10 @@ describe('onRpcRequest', () => {
},
};

await request(setStateParams);
const setStateResponse = request(setStateParams);
const setStateUi = await setStateResponse.getInterface() as (SnapConfirmationInterface & SnapInterfaceActions);
await setStateUi.ok();
await setStateResponse;

const response = await request({
method: 'getState',
Expand All @@ -122,7 +168,7 @@ describe('onRpcRequest', () => {
it('clears the state', async () => {
const { request } = await installSnap();

await request({
const setStateResponse = request({
method: 'setState',
params: {
staking: {
Expand All @@ -136,12 +182,16 @@ describe('onRpcRequest', () => {
},
},
});
const setStateUi = await setStateResponse.getInterface() as (SnapConfirmationInterface & SnapInterfaceActions);
await setStateUi.ok();
await setStateResponse;

expect(
await request({
method: 'clearState',
}),
).toRespondWith(true);
const clearResponse = request({
method: 'clearState',
});
const clearUi = await clearResponse.getInterface() as (SnapConfirmationInterface & SnapInterfaceActions);
await clearUi.ok();
expect(await clearResponse).toRespondWith(true);

expect(
await request({
Expand All @@ -159,5 +209,50 @@ describe('onRpcRequest', () => {
},
});
});

it('does not clear the state when the user cancels the confirmation', async () => {
const { request } = await installSnap();

const setStateResponse = request({
method: 'setState',
params: {
staking: {
nodeCloudAccessKeys: {
onFinality: {
accessKey: 'bazAccessKey',
secretKey: 'bazSecretKey',
workspaceId: 'bazWorkspaceId',
},
},
},
},
});
const setStateUi = await setStateResponse.getInterface() as (SnapConfirmationInterface & SnapInterfaceActions);
await setStateUi.ok();
await setStateResponse;

const clearResponse = request({
method: 'clearState',
});
const clearUi = await clearResponse.getInterface() as (SnapConfirmationInterface & SnapInterfaceActions);
await clearUi.cancel();
await clearResponse;

const state = await request({
method: 'getState',
});

expect(state).toRespondWith({
staking: {
nodeCloudAccessKeys: {
onFinality: {
accessKey: 'bazAccessKey',
secretKey: 'bazSecretKey',
workspaceId: 'bazWorkspaceId',
},
},
},
});
});
});
});
53 changes: 41 additions & 12 deletions src/utils.ts → src/utils.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ManageStateOperation } from '@metamask/snaps-sdk';
import { Box, Text, Heading } from "@metamask/snaps-sdk/jsx";

export type State = {
staking: {
Expand Down Expand Up @@ -58,14 +59,28 @@ export async function getState(encrypted: boolean = true): Promise<State> {
* @see https://docs.metamask.io/snaps/reference/rpc-api/#snap_managestate
*/
export async function setState(newState: State, encrypted: boolean = true) {
await snap.request({
method: 'snap_manageState',
const result = await snap.request({
method:"snap_dialog",
params: {
operation: ManageStateOperation.UpdateState,
newState,
encrypted,
},
type: "confirmation",
content: (
<Box>
<Heading>Save your OnFinality keys</Heading>
<Text>The ZenChain Node Station app would like to save your OnFinality keys in your MetaMask wallet's encrypted storage. Your keys will remain securely encrypted on your device, protected by the security of your MetaMask wallet. Your keys will be used to authenticate with OnFinality, allowing the Node Station app to configure your node and tell you its status. Your keys are never sent to ZenChain or any third party.</Text>
</Box>
)
}
});
if (result === true) {
await snap.request({
method: 'snap_manageState',
params: {
operation: ManageStateOperation.UpdateState,
newState,
encrypted,
},
});
}
}

/**
Expand All @@ -78,11 +93,25 @@ export async function setState(newState: State, encrypted: boolean = true) {
* @see https://docs.metamask.io/snaps/reference/rpc-api/#snap_managestate
*/
export async function clearState(encrypted: boolean = true) {
await snap.request({
method: 'snap_manageState',
const result = await snap.request({
method:"snap_dialog",
params: {
operation: ManageStateOperation.ClearState,
encrypted,
},
});
type: "confirmation",
content: (
<Box>
<Heading>Delete your OnFinality keys</Heading>
<Text>The ZenChain Node Station app would like to permanently delete your OnFinality keys from your MetaMask wallet's encrypted storage. Although your OnFinality keys are securely encrypted in your wallet, we still recommend deleting them when they will no longer be used by the ZenChain Node Station app.</Text>
</Box>
)
}
});
if (result === true) {
await snap.request({
method: 'snap_manageState',
params: {
operation: ManageStateOperation.ClearState,
encrypted,
},
});
}
}
7 changes: 4 additions & 3 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"compilerOptions": {
"baseUrl": "./"
},
"include": ["src", "snap.config.ts"]
"baseUrl": "./",
"jsx": "react-jsx",
"jsxImportSource": "@metamask/snaps-sdk"
}
}
Loading