---
# System prepended metadata

title: Getting Started with MetaMask Snaps

---

# Getting Started with MetaMask Snaps
_Devcon VI Workshop_

Kudos to [@Mrtenz](https://github.com/Mrtenz) for the Snap implementation.

## Resources

- [Snaps landing page](https://metamask.io/snaps)
- [Snaps documentation](https://docs.metamask.io/guide/snaps.html)
- [Snaps discussion board](https://github.com/MetaMask/snaps-monorepo/discussions)
- [Template snap monorepo](https://github.com/MetaMask/template-snap-monorepo)
- [Completed workshop code](https://github.com/rekmarks/devcon-2022-snap/tree/completed)

## Before We Begin

1. Ensure that you have the following dependencies:
    - A Chromium browser or Firefox
    - `git`
    - [Node.js](https://nodejs.org/en/) `^16.0.0`
    - [`yarn`](https://yarnpkg.com/getting-started/install)
      - `yarn` can also be installed after cloning the repository, see below.
2. Install [MetaMask Flask](https://metamask.io/flask/).
    - _During Devcon VI, a prerelease build of MetaMask Flask was required, but this is no longer the case._
3. Add an existing secret recovery phrase that you aren't afraid to lose, or create a new one.

## The Workshop

Our goal is to create a snap that decodes contract call function names and parameters using the [4byte API](https://www.4byte.directory/docs/) and [`@metamask/abi-utils`](https://npmjs.com/package/@metamask/abi-utils).

1. Create a new GitHub repository using the [template snap monorepo](https://github.com/MetaMask/template-snap-monorepo).
    - This template contains two workspaces, one for the snap and one for the website / dapp that will serve as its user interface.
    - `git clone` your new repository.
    - Run `yarn install` at the monorepo root.
      - If you do not already have `yarn` installed, you can start the installation by directly running `.yarn/releases/yarn-3.2.1.cjs install` from the monorepo root.

2. Boot the snap and website by running `yarn start` at the monorepo root.
    - This will set up live reloading for both the snap and the website.
3. Install the example snap by clicking `Connect` on the website, then try out its functionality by clicking `Send message`.
    - The snap will be executed by MetaMask in a sandboxed environment and display a 
    confirmation.
4. Rewrite the snap to use the transaction insights API.
    - In `snap.manifest.json`, replace the `snap_confirm` permission with `endowment:transaction-insight`.
    - In `index.ts`, replace the `onRpcRequest` handler with an `onTransaction` handler. Make sure it is exported.
    - We will add some basic validation. Add [`@metamask/utils`](https://npmjs.com/package/@metamask/utils) as a dependency of your snap and import it in your snap's `index.ts` file.
5. Customize the website to suit your snap, and rewrite the `Send message` logic to create an Ethereum transaction instead.
    - Don't forget to add logic for requesting Ethereum accounts so that you are able to submit transactions to MetaMask.
    - You can use the address from `TransactionConstants` as the recipient address. See [Transaction Snippets](#Transaction-Snippets) below.
6. Click `Reconnect` in the UI to reinstall the snap, then click `Send message` to observe your "decoded transaction".
7. Call the 4byte API from within your snap.
    - Snaps do not get network access by default. Add `endowment:network-access` to your snap's permissions.
    - Copy the [4byte Snippet](#4byte-Snippet) below for the API URL and some helpful types.
    - Return some of the data from 4byte as an "insight" so we know that it works.
8. Finally, decode the parameters using [`@metamask/abi-utils`](https://npmjs.com/package/@metamask/abi-utils).
    - Add `@metamask/abi-utils` as a dependency of your snap and import it in your snap's `index.ts` file.
    - Copy the `normalizeAbiValue` function from the [Value Normalizer Snippet](#Value-Normalizer-Snippet) and paste it into your snap's `index.ts` file.
    - Extract the parameter types, decode them using `@metamaks/abi-utils`, and return your your completed insights.

### Transaction Snippets

We will demonstrate our snap's functionality by decoding some mock contract transactions that we create. You can of course try some contract interactions on your own as well.

```typescript
enum TransactionConstants {
  // The address of an arbitrary contract that will reject any transactions it receives
  Address = '0x08A8fDBddc160A7d5b957256b903dCAb1aE512C5',
  // Some example encoded contract transaction data
  UpdateWithdrawalAccount = '0x83ade3dc00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000047170ceae335a9db7e96b72de630389669b334710000000000000000000000006b175474e89094c44da98b954eedeac495271d0f',
  UpdateMigrationMode = '0x2e26065e0000000000000000000000000000000000000000000000000000000000000000',
  UpdateCap = '0x85b2c14a00000000000000000000000047170ceae335a9db7e96b72de630389669b334710000000000000000000000000000000000000000000000000de0b6b3a7640000',
}
```

```typescript
// A function that sends contract transactions
export const sendContractTransaction = async (data: string) => {
  // Get the user's account from MetaMask.
  const [from] = (await window.ethereum.request({
    method: 'eth_requestAccounts',
  })) as string[];

  // Send a transaction to MetaMask.
  await window.ethereum.request({
    method: 'eth_sendTransaction',
    params: [
      {
        from,
        to: TransactionConstants.Address,
        value: '0x0',
        data,
      },
    ],
  });
};
```

### 4byte Snippet

We will use the [4byte API](https://www.4byte.directory/docs/) in our Snap. Here is a snippet with some constants:

```typescript
// The API endpoint to get a list of functions by 4 byte signature.
const API_ENDPOINT =
  'https://www.4byte.directory/api/v1/signatures/?hex_signature=';

/* eslint-disable camelcase */
type FourByteSignature = {
  id: number;
  created_at: string;
  text_signature: string;
  hex_signature: string;
  bytes_signature: string;
};
/* eslint-enable camelcase */
```

### Value Normalizer Snippet

```typescript
/**
 * The ABI decoder returns certain which are not JSON serializable. This
 * function converts them to strings.
 *
 * @param value - The value to convert.
 * @returns The converted value.
 */
function normalizeAbiValue(value: unknown): Json {
  if (Array.isArray(value)) {
    return value.map(normalizeAbiValue);
  }

  if (value instanceof Uint8Array) {
    return bytesToHex(value);
  }

  if (typeof value === 'bigint') {
    return value.toString();
  }

  return value as Json;
}
```
