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

chore: Update documentation and Readme #202

Merged
merged 10 commits into from
Dec 6, 2022
Merged
Show file tree
Hide file tree
Changes from 6 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
89 changes: 77 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,93 @@ $ 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() {
const { metaMask, browser } = await dappeteer.bootstrap({ metaMaskFlask: true });
Tbaut marked this conversation as resolved.
Show resolved Hide resolved

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

// create a new page and visit your dapp
mpetrunic marked this conversation as resolved.
Show resolved Hide resolved
const dappPage = browser.newPage();
await dappPage.goto('http://my-dapp.com');

// install your snap
const snapId = await metaMask.snaps.installSnap(builtSnapDir, {
hasPermissions: true,
hasKeyPermissions: false,
});

// do something in your dapp that invokes a method in your snap
// you could alternatively call metaMask.snaps.invokeSnap(dappPage, snapId, "my-method")
Tbaut marked this conversation as resolved.
Show resolved Hide resolved
const invokeSnapButton = await dappPage.$('#invoke-snap');
await addTokenButton.click();

// 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
const emitNotificationButton = await dappPage.$('#emit-notification');
await emitNotificationButton.click();

// 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
180 changes: 130 additions & 50 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,32 @@ For additional information read root [readme](../README.md)
- [Setup MetaMask](#setup)
- [Bootstrap dAppeteer](#bootstrap)
- [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 +46,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 +71,170 @@ 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 `dappeteer.launch` and `dappeteer.setup` and returns an object with metaMask, metaMaskPage and browser.

<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`
```typescript
/**
* Installs snap. Function will throw if there is an error while installing the snap.
* @param snapIdOrLocation either the snapId or the full path to your snap directory
* where we can find the bundled snap (you need to ensure the snap is built)
* @param opts {Object} the snap method you want to invoke
* @param opts.hasPermissions Set to true if the snap uses some permissions
* @param opts.hasKeyPermissions Set to true if the snap uses the key permissions
* @param installationSnapUrl the url of your dapp. Defaults to google.com
*/
) => Promise<string>;
```

<a name="invokeSnap"></a>
## `metaMask.snaps.invokeSnap<Result = unknown, Params extends Serializable = Serializable>(page: DappeteerPage,snapId: string,method: string,params?: Params): Promise<Partial<Result>>`
invoke a MetaMask snap method. Function will throw if there is an error while invoking snap.

```typescript
/**
* Use generic params to override result and parameter types.
* @param page Browser page where injected MetaMask provider will be available.
* For most snaps, openning google.com will suffice.
* @param snapId id of your installed snap (result of invoking `installSnap` method)
* @param method snap method you want to invoke
* @param params required parameters of snap method
*/
```

Tbaut marked this conversation as resolved.
Show resolved Hide resolved
<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