Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

Commit

Permalink
feat: add ability to accept dialogs (#138) (#164)
Browse files Browse the repository at this point in the history
* feat:add ability to accept dialogs (#138)

* fix lint

* move installSnap, invokeSnap to dappeteer api; add bringToFront call to dialog methods

* pair improvements

* fix lint

* add methods snap

* exclude flask tests from global

* exclude flask tests from global

Co-authored-by: Bernard <bero4net@gmail.com>
  • Loading branch information
2 people authored and mpetrunic committed Dec 15, 2022
1 parent 17f2849 commit f777a9a
Show file tree
Hide file tree
Showing 16 changed files with 3,595 additions and 38 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"lint": "eslint --color --ext .ts src/ test/",
"lint:fix": "yarn run lint --fix",
"test": "yarn run test:*",
"test:mm": "mocha --require ts-node/register --require test/global.ts",
"test:mm": "mocha --require ts-node/register --require test/global.ts --exclude test/flask/snaps.spec.ts",
"test:flask": "mocha --require ts-node/register --require test/global_flask.ts"
},
"repository": {
Expand Down
15 changes: 12 additions & 3 deletions src/metamask/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import { Browser, Page } from "puppeteer";

import { Dappeteer } from "..";

import { acceptDialog } from "../snap/acceptDialog";
import { rejectDialog } from "../snap/rejectDialog";
import { installSnap, invokeSnap } from "../snap";
import { addNetwork } from "./addNetwork";
import { addToken } from "./addToken";
import { approve } from "./approve";
Expand Down Expand Up @@ -32,7 +35,7 @@ export const getMetaMask = (page: Page): Promise<Dappeteer> => {
: true
);

return new Promise<Dappeteer>((resolve) =>
return new Promise<Dappeteer>((resolve) => {
resolve({
addNetwork: addNetwork(page),
approve: approve(page),
Expand All @@ -49,9 +52,15 @@ export const getMetaMask = (page: Page): Promise<Dappeteer> => {
deleteAccount: deleteAccount(page),
deleteNetwork: deleteNetwork(page),
},
snaps: {
acceptDialog: acceptDialog(page),
rejectDialog: rejectDialog(page),
invokeSnap,
installSnap,
},
page,
})
);
});
});
};

/**
Expand Down
8 changes: 8 additions & 0 deletions src/snap/acceptDialog.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Page } from "puppeteer";
import { clickOnButton } from "../helpers";

export const acceptDialog = (page: Page) => async (): Promise<void> => {
await page.bringToFront();
await page.reload();
await clickOnButton(page, "Approve");
};
5 changes: 4 additions & 1 deletion src/snap/install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
openProfileDropdown,
} from "../helpers";
import { flaskOnly } from "./utils";
import { InstallSnapResult } from "./types";

declare let window: { ethereum: MetaMaskInpageProvider };

Expand All @@ -22,7 +23,7 @@ export async function installSnap(
version?: string;
},
installationSnapUrl: string = "https://google.com"
): Promise<void> {
): Promise<InstallSnapResult> {
flaskOnly(page);
//need to open page to access window.ethereum
const installPage = await page.browser().newPage();
Expand Down Expand Up @@ -67,6 +68,8 @@ export async function installSnap(
if (!(snapId in result.snaps)) {
throw new Error("Failed to install snap");
}

return result as InstallSnapResult;
}

export async function isSnapInstalled(
Expand Down
2 changes: 1 addition & 1 deletion src/snap/invokeSnap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export async function invokeSnap<
page: Page,
snapId: string,
method: string,
params: P
params?: P
): ReturnType<typeof window.ethereum.request<R>> {
flaskOnly(page);
return page.evaluate(
Expand Down
8 changes: 8 additions & 0 deletions src/snap/rejectDialog.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Page } from "puppeteer";
import { clickOnButton } from "../helpers";

export const rejectDialog = (page: Page) => async (): Promise<void> => {
await page.bringToFront();
await page.reload();
await clickOnButton(page, "Reject");
};
20 changes: 20 additions & 0 deletions src/snap/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export interface InstallSnapResult {
accounts: [];
permissions: [
{
id: string;
parentCapability: string;
invoker: string;
caveats: null;
date: number;
}
];
snaps: {
[id: string]: {
permissionName: string;
id: string;
initialPermissions: Object;
version: string;
};
};
}
24 changes: 24 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import * as puppeteer from "puppeteer";

import { MetaMaskInpageProvider } from "@metamask/providers";
import { Page, Serializable } from "puppeteer";
import { Path } from "./setup/metaMaskDownloader";

import { InstallStep } from "./snap/install";
import { InstallSnapResult } from "./snap/types";
import { RECOMMENDED_METAMASK_VERSION } from "./index";

declare global {
Expand Down Expand Up @@ -76,5 +79,26 @@ export type Dappeteer = {
deleteAccount: (accountNumber: number) => Promise<void>;
deleteNetwork: (name: string) => Promise<void>;
};
snaps: {
invokeSnap: <R = unknown, P extends Serializable = Serializable>(
page: Page,
snapId: string,
method: string,
params?: P
) => Promise<Partial<R>>;
installSnap: (
page: Page,
snapId: string,
opts: {
hasPermissions: boolean;
hasKeyPermissions: boolean;
customSteps?: InstallStep[];
version?: string;
},
installationSnapUrl?: string
) => Promise<InstallSnapResult>;
acceptDialog: () => Promise<void>;
rejectDialog: () => Promise<void>;
};
page: puppeteer.Page;
};
2 changes: 2 additions & 0 deletions test/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,15 @@ export enum Snaps {
BASE_SNAP = "base-snap",
KEYS_SNAP = "keys-snap",
PERMISSIONS_SNAP = "permissions-snap",
METHODS_SNAP = "methods-snap",
}

export async function startSnapServers(): Promise<Record<Snaps, http.Server>> {
return {
[Snaps.BASE_SNAP]: await startSnapServer(Snaps.BASE_SNAP),
[Snaps.KEYS_SNAP]: await startSnapServer(Snaps.KEYS_SNAP),
[Snaps.PERMISSIONS_SNAP]: await startSnapServer(Snaps.PERMISSIONS_SNAP),
[Snaps.METHODS_SNAP]: await startSnapServer(Snaps.METHODS_SNAP),
};
}

Expand Down
65 changes: 65 additions & 0 deletions test/flask/methods-snap/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<!doctype html>
<html>
</head>
<title>Hello, Snaps!</title>
<link rel="icon" type="image/svg" href="./images/icon.svg"/>
</head>

<body>
<h1>Hello, Snaps!</h1>
<details>
<summary>Instructions</summary>
<ul>
<li>First, click "Connect". Then, try out the other buttons!</li>
<li>Please note that:</li>
<ul>
<li>
The <code>snap.manifest.json</code> and <code>package.json</code> must be located in the server root directory...
</li>
<li>
The Snap bundle must be hosted at the location specified by the <code>location</code> field of <code>snap.manifest.json</code>.
</li>
</ul>
</ul>
</details>
<br/>

<button class="connect">Connect</button>
<button class="sendHello">Send Hello</button>
</body>

<script>
const snapId = `local:${window.location.href}`;

const connectButton = document.querySelector('button.connect')
const sendButton = document.querySelector('button.sendHello')

connectButton.addEventListener('click', connect)
sendButton.addEventListener('click', send)

// here we get permissions to interact with and install the snap
async function connect () {
await ethereum.request({
method: 'wallet_enable',
params: [{
wallet_snap: { [snapId]: {} },
}]
})
}

// here we call the snap's "hello" method
async function send () {
try {
await ethereum.request({
method: 'wallet_invokeSnap',
params: [snapId, {
method: 'hello'
}]
})
} catch (err) {
console.error(err)
alert('Problem happened: ' + err.message || err)
}
}
</script>
</html>
18 changes: 18 additions & 0 deletions test/flask/methods-snap/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "base-snap",
"version": "1.0.0",
"description": "",
"main": "src/index.ts",
"scripts": {
"prefix": "mm-snap build",
"fix": "mm-snap manifest --fix"
},
"devDependencies": {
"@metamask/snaps-cli": "^0.22.0"
},
"author": "",
"license": "ISC",
"dependencies": {
"@metamask/snap-types": "^0.22.0"
}
}
6 changes: 6 additions & 0 deletions test/flask/methods-snap/snap.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
cliOptions: {
src: './src/index.ts',
port: 8080,
},
};
20 changes: 20 additions & 0 deletions test/flask/methods-snap/snap.manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"version": "1.0.0",
"description": "An example Snap written in TypeScript.",
"proposedName": "Methods Snap\n",
"source": {
"shasum": "BCwqoDyQltaL0dh0IrzN1A66D1jPMLAseJOHmqI1faU=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
"packageName": "base-snap",
"registry": "https://registry.npmjs.org/"
}
}
},
"initialPermissions": {
"snap_confirm": {},
"snap_notify": {}
},
"manifestVersion": "0.1"
}
21 changes: 21 additions & 0 deletions test/flask/methods-snap/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { OnRpcRequestHandler } from "@metamask/snap-types";

export const onRpcRequest: OnRpcRequestHandler = ({ origin, request }) => {
switch (request.method) {
case "confirm":
return wallet.request({
method: "snap_confirm",
params: [
{
prompt: `Hello, ${origin}!`,
description:
"This custom confirmation is just for display purposes.",
textAreaContent:
"But you can edit the snap source code to make it do something, if you want to!",
},
],
});
default:
throw new Error("Method not found.");
}
};
Loading

0 comments on commit f777a9a

Please sign in to comment.