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

Commit

Permalink
chore: Update documentation and Readme (#202)
Browse files Browse the repository at this point in the history
* docs

* change to metaMask accross the board and use bootstrap in our test

* MetaMMMMMMask

* snaps methods in API.md

* snaps usage in Readme.md

* Update README.md

Co-authored-by: Marin Petrunić <[email protected]>

* address comment

* add forgotten menu item for initSnapEnv

* removing jsdoc

Co-authored-by: Marin Petrunić <[email protected]>
  • Loading branch information
Tbaut and mpetrunic committed Dec 15, 2022
1 parent fee2e83 commit 2161636
Show file tree
Hide file tree
Showing 18 changed files with 342 additions and 184 deletions.
85 changes: 73 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,89 @@ $ yarn add @chainsafe/dappeteer
## Usage

```js
import puppeteer from 'puppeteer';
import dappeteer from '@chainsafe/dappeteer';

async function main() {
const [metamask, page] = await dappeteer.bootstrap(puppeteer, { metaMaskVersion: 'v10.15.0' });
const { metaMask, browser } = await dappeteer.bootstrap();

// create a new page and visit your dapp
const dappPage = browser.newPage();
await dappPage.goto('http://my-dapp.com');

// you can change the network if you want
await metamask.switchNetwork('ropsten');
await metaMask.switchNetwork('goerli');

// you can import a token
await metamask.addToken({
tokenAddress: '0x4f96fe3b7a6cf9725f59d353f723c1bdb64ca6aa',
symbol: 'KAKI',
});
// do something in your dapp that prompts MetaMask to add a Token
const addTokenButton = await dappPage.$('#add-token');
await addTokenButton.click();
// instruct MetaMask to accept this request
await metaMask.acceptAddToken();

// go to a dapp and do something that prompts MetaMask to confirm a transaction
await page.goto('http://my-dapp.com');
const payButton = await page.$('#pay-with-eth');
// do something that prompts MetaMask to confirm a transaction
const payButton = await dappPage.$('#pay-with-eth');
await payButton.click();

// 🏌
await metamask.confirmTransaction();
await metaMask.confirmTransaction();
}

main();
```

## Usage with Snaps

```js
import dappeteer from '@chainsafe/dappeteer';
import { exec } from "child_process";

async function buildSnap(): Promise<string> {
console.log(`Building my-snap...`);
await new Promise((resolve, reject) => {
exec(`cd ./my-snap && npx mm-snap build`, (error, stdout) => {
if (error) {
reject(error);
return;
}
resolve(stdout);
});
});

return "./my-snap";
}

async function main() {
// you need to have a webpage open to interact with MetaMask, you can also visit a dApp page
const dappPage = browser.newPage();
await dappPage.goto('http://example.org/');

// build your local snap
const builtSnapDir = await buildSnap()

// setup dappateer and install your snap
const { snapId, metaMask, dappPage } = await dappeteer.initSnapEnv({
snapIdOrLocation: builtSnapDir
hasPermissions: true,
hasKeyPermissions: false,
});

// invoke a method from your snap that promps users with approve/reject dialog
metaMask.snaps.invokeSnap(dappPage, snapId, "my-method")

// instruct MetaMask to accept this request
await metaMask.snaps.acceptDialog();

// get the notification emitter and the promise that will receive the notifications
const emitter = await metaMask.snaps.getNotificationEmitter();
const notificationPromise = emitter.waitForNotification();

// do something that prompts you snap to emit notifications
await metaMask.snaps.invokeSnap(dappPage, snapId, "notify");

// Make sure the notification promise has resolved
await notificationPromise;

// You can now read the snap notifications and run tests against them
const notifications = await metaMask.snaps.getAllNotifications();
}

main();
Expand Down
194 changes: 144 additions & 50 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,34 @@ For additional information read root [readme](../README.md)
- [Launch dAppeteer](#launch)
- [Setup MetaMask](#setup)
- [Bootstrap dAppeteer](#bootstrap)
- [Initialize Snap Environment](#initSnapEnv)
- [Get MetaMask Window](#getMetaMask)
- [dAppeteer methods](#methods)
- [metaMask methods](#methods)
- [switchAccount](#switchAccount)
- [importPK](#importPK)
- [lock](#lock)
- [unlock](#unlock)
- [switchNetwork](#switchNetwork)
- [addNetwork](#addNetwork)
- [addToken](#addToken)
- [acceptAddNetwork](#acceptAddNetwork)
- [rejectAddNetwork](#rejectAddNetwork)
- [acceptAddToken](#acceptAddToken)
- [rejectAddToken](#rejectAddToken)
- [confirmTransaction](#confirmTransaction)
- [sign](#sign)
- [signTypedData](#signTypedData)
- [approve](#approve)
- [helpers](#helpers)
- [getTokenBalance](#getTokenBalance)
- [deleteAccount](#deleteAccount)
- [deleteNetwork](#deleteNetwork)
- [page](#page)
- [snaps methods](#snaps-methods)
- [installSnap](#installSnap)
- [invokeSnap](#invokeSnap)
- [acceptDialog](#acceptDialog)
- [rejectDialog](#rejectDialog)
- [getNotificationEmitter](#getNotificationEmitter)
- [getAllNotifications](#getAllNotifications)

# dAppeteer setup methods

Expand All @@ -36,17 +47,18 @@ interface OfficialOptions {

type Path = string | { download: string; extract: string; };
```
or
or
```typescript
interface CustomOptions {
metamaskPath: string;
metaMaskPath: string;
};
```

returns an instance of `browser` same as `puppeteer.launch`, but it also installs the MetaMask extension. [It supports all the regular `puppeteer.launch` options](https://github.com/puppeteer/puppeteer/blob/v5.5.0/docs/api.md#puppeteerlaunchoptions)

<a name="setup"></a>
## `dappeteer.setupMetaMask(browser: Browser, options: MetaMaskOptions = {}, steps: Step[]): Promise<Dappeteer>`

```typescript
interface MetaMaskOptions {
seed?: string;
Expand All @@ -60,101 +72,183 @@ type Step = (page: Page, options?: Options) => void;
```

<a name="bootstrap"><a/>
## `dappeteer.bootstrap(puppeteerLib: typeof puppeteer, options: OfficialOptions & MetaMaskOptions): Promise<[Dappeteer, Page, Browser]>`
## `dappeteer.bootstrap(options: DappeteerLaunchOptions & MetaMaskOptions): Promise<{
metaMask: Dappeteer;
browser: DappeteerBrowser;
metaMaskPage: DappeteerPage;
}>`

```typescript
interface OfficialOptions {
metaMaskVersion: 'latest' | string;
type DappeteerLaunchOptions = {
metaMaskVersion?:
| "latest"
| "local"
| string;
metaMaskLocation?: Path;
metaMaskPath?: string;
metaMaskFlask?: boolean;
automation?: "puppeteer" | "playwright";
browser: "chrome";
puppeteerOptions?: Omit<Parameters<typeof puppeteerLaunch>[0], "headless">;
playwrightOptions?: Omit<PlaywrightLaunchOptions, "headless">;
};

type MetaMaskOptions = {
seed?: string;
password?: string;
showTestNets?: boolean;
};
```
it runs `dappeteer.launch` and `dappeteer.setup` and return array with dappetter, page and browser

it runs it runs `dappeteer.launch` and `dappeteer.setupMetaMask` and returns an object with metaMask, metaMaskPage and browser.

<a name="initSnapEnv"></a>
## `dappeteer.initSnapEnv( opts: DappeteerLaunchOptions & MetaMaskOptions & InstallSnapOptions & { snapIdOrLocation: string }): Promise<{ metaMask: Dappeteer; browser: DappeteerBrowser; metaMaskPage: DappeteerPage; snapId: string;}`

```typescript
type DappeteerLaunchOptions = {
metaMaskVersion?:
| "latest"
| "local"
| string;
metaMaskLocation?: Path;
metaMaskPath?: string;
metaMaskFlask?: boolean;
automation?: "puppeteer" | "playwright";
browser: "chrome";
puppeteerOptions?: Omit<Parameters<typeof puppeteerLaunch>[0], "headless">;
playwrightOptions?: Omit<PlaywrightLaunchOptions, "headless">;
};

type MetaMaskOptions = {
seed?: string;
password?: string;
showTestNets?: boolean;
};

type InstallSnapOptions = {
hasPermissions: boolean;
hasKeyPermissions: boolean;
customSteps?: InstallStep[];
version?: string;
installationSnapUrl?: string;
}
```
it runs `dappeteer.launch` and `dappeteer.setupMetamask` and `snaps.installSnap` and returns an object with metaMask, metaMaskPage, browser and snapId.
<a name="getMetaMask"></a>
## `dappeteer.getMetaMaskWindow(browser: Browser, version?: string): Promise<Dappeteer>`
<a name="methods"></a>
# dAppeteer methods
`metamask` is used as placeholder for dAppeteer returned by [`setupMetaMask`](setup) or [`getMetaMaskWindow`](getMetaMask)

# metaMask methods
`metaMask` is used as placeholder for dAppeteer returned by [`setupMetaMask`](setup) or [`getMetaMaskWindow`](getMetaMask)
<a name="switchAccount"></a>
## `metamask.switchAccount(accountNumber: number): Promise<void>`
## `metaMask.switchAccount(accountNumber: number): Promise<void>`
it commands MetaMask to switch to a different account, by passing the index/position of the account in the accounts list.
<a name="importPK"></a>
## `metamask.importPK(privateKey: string): Promise<void>`
## `metaMask.importPK(privateKey: string): Promise<void>`
it commands MetaMask to import an private key. It can only be used while you haven't signed in yet, otherwise it throws.
<a name="lock"></a>
## `metamask.lock(): Promise<void>`
## `metaMask.lock(): Promise<void>`
signs out from MetaMask. It can only be used if you arelady signed it, otherwise it throws.
<a name="unlock"></a>
## `metamask.unlock(password: string): Promise<void>`
## `metaMask.unlock(password: string): Promise<void>`
it unlocks the MetaMask extension. It can only be used in you locked/signed out before, otherwise it throws. The password is optional, it defaults to `password1234`.
<a name="switchNetwork"></a>
## `metamask.switchNetwork(network: string): Promise<void>`
it changes the current selected network. `networkName` can take the following values: `"main"`, `"ropsten"`, `"rinkeby"`, `"kovan"`, `"localhost"`.
## `metaMask.switchNetwork(network: string): Promise<void>`
it changes the current selected network. `networkName` can take the following values: `"mainnet"`, `"goerli"`, `"sepolia"`, `"ropsten"`, `"rinkeby"`, `"kovan"`, `"localhost"`.
<a name="addNetwork"></a>
## `metamask.addNetwork(options: AddNetwork): Promise<void>`
```typescript
interface AddNetwork {
networkName: string;
rpc: string;
chainId: number;
symbol: string;
}
```
it adds a custom network to MetaMask.
<a name="acceptAddNetwork"></a>
## `metaMask.acceptAddNetwork(shouldSwitch?: boolean): Promise<void>`
<a name="addToken"></a>
## `metamask.addToken(tokenAddress: string): Promise<void>`
```typescript
interface AddToken {
tokenAddress: string;
symbol?: string;
decimals?: number;
}
```
it adds a custom token to MetaMask.
commands MetaMask to accept a Network addition. For this to work MetaMask has to be in a Network addition state (basically prompting the user to accept/reject a Network addition). You can optionnaly tell MetaMask to switch to this network by passing the `true` parameter (default to `false`).
<a name="rejectAddNetwork"></a>
## `metaMask.rejectAddNetwork(): Promise<void>`
commands MetaMask to reject a Network addition. For this to work MetaMask has to be in a Network addition state (basically prompting the user to accept/reject a Network addition).
<a name="acceptAddToken"></a>
## `metaMask.acceptAddToken(): Promise<void>`
commands MetaMask to accept a Token addition. For this to work MetaMask has to be in a Token addition state (basically prompting the user to accept/reject a Token addition).
<a name="rejectAddToken"></a>
## `metaMask.rejectAddToken(): Promise<void>`
commands MetaMask to reject a Token addition. For this to work MetaMask has to be in a Token addition state (basically prompting the user to accept/reject a Token addition).
<a name="confirmTransaction"></a>
## `metamask.confirmTransaction(options?: TransactionOptions): Promise<void>`
## `metaMask.confirmTransaction(options?: TransactionOptions): Promise<void>`
```typescript
interface TransactionOptions {
gas?: number;
gasLimit?: number;
priority?: number;
}
```
commands MetaMask to submit a transaction. For this to work MetaMask has to be in a transaction confirmation state (basically promting the user to submit/reject a transaction). You can (optionally) pass an object with `gas` and/or `gasLimit`, by default they are `20` and `50000` respectively.

commands MetaMask to submit a transaction. For this to work MetaMask has to be in a transaction confirmation state (basically prompting the user to submit/reject a transaction). You can (optionally) pass an object with `gas` and/or `gasLimit`, by default they are `20` and `50000` respectively.

<a name="sign"></a>
## `metamask.sign(): Promise<void>`
## `metaMask.sign(): Promise<void>`
commands MetaMask to sign a message. For this to work MetaMask must be in a sign confirmation state.

<a name="signTypedData"></a>
## `metaMask.signTypedData(): Promise<void>`
commands MetaMask to sign a message. For this to work MetaMask must be in a sign typed data confirmation state.

<a name="approve"></a>
## `metamask.approve(): Promise<void>`
## `metaMask.approve(): Promise<void>`
enables the app to connect to MetaMask account in privacy mode

<a name="helpers"></a>
## `metamask.helpers`
# Helpers methods

<a name="getTokenBalance"></a>
### `metamask.helpers.getTokenBalance(tokenSymbol: string): Promise<number>`
## `metaMask.helpers.getTokenBalance(tokenSymbol: string): Promise<number>`
get balance of specific token

<a name="deleteAccount"></a>
### `metamask.helpers.deleteAccount(accountNumber: number): Promise<void>`
## `metaMask.helpers.deleteAccount(accountNumber: number): Promise<void>`
deletes account containing name with specified number

<a name="deleteNetwork"></a>
### `metamask.helpers.deleteNetwork(): Promise<void>`
deletes custom network from metamask
## `metaMask.helpers.deleteNetwork(): Promise<void>`
deletes custom network from metaMask

<a name="page"></a>
## `metamask.page` is MetaMask plugin `Page`
## `metaMask.page` is the MetaMask plugin `Page`
**for advanced usages** in case you need custom features.

<a name="snaps_methods"></a>
# Snaps methods

<a name="installSnap"></a>
## `metaMask.snaps.installSnap: (snapIdOrLocation: string, opts: { hasPermissions: boolean; hasKeyPermissions: boolean; customSteps?: InstallStep[]; version?: string;},installationSnapUrl?: string`) => Promise<string>;
installs the snap. The `snapIdOrLocation` param is either the snapId or the full path to your snap directory.

<a name="invokeSnap"></a>
## `metaMask.snaps.invokeSnap<Result = unknown, Params extends Serializable = Serializable>(page: DappeteerPage,snapId: string,method: string,params?: Params): Promise<Partial<Result>>`
invokes a MetaMask snap method. The snapId is the id of your installed snap (result of invoking `installSnap` method). This function will throw if there is an error while invoking snap.

<a name="acceptDialog"></a>
## `metaMask.snaps.acceptDialog(): Promise<void>`
accepts a snap_confirm dialog

<a name="rejectDialog"></a>
## `metaMask.snaps.rejectDialog(): Promise<void>`
rejects snap_confirm dialog

<a name="getNotificationEmitter"></a>
## `metaMask.snaps.getNotificationEmitter(): Promise<NotificationsEmitter>`
returns emitter to listen for notifications appearance in notification page

<a name="getAllNotifications"></a>
## `metaMask.snaps.getAllNotifications(): Promise<NotificationList>`
Returns all notifications in MetaMask notifications page
Loading

0 comments on commit 2161636

Please sign in to comment.