Skip to content

Commit

Permalink
Add Permissionless Quickstart guide (#442)
Browse files Browse the repository at this point in the history
  • Loading branch information
germartinez authored Apr 18, 2024
1 parent ac99a73 commit bfef541
Show file tree
Hide file tree
Showing 4 changed files with 222 additions and 4 deletions.
2 changes: 1 addition & 1 deletion .github/styles/config/vocabularies/default/accept.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
[Oo]ffchain
[Oo]nboarding
[Oo]nchain
[Pp]ermissionless
[Pp]olygon
[Pp]luggable
[Rr]eact
Expand Down Expand Up @@ -229,7 +230,6 @@ multichain
npm
onboarding
onchain
permissionless
pluggable
saltNonce
textWrap
Expand Down
4 changes: 2 additions & 2 deletions pages/home/4337-guides/_meta.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"permissionless-quickstart": "Permissionless quickstart",
"permissionless-detailed": "Permissionless detailed"
"permissionless-quickstart": "Permissionless.js Quickstart",
"permissionless-detailed": "Permissionless.js Detailed"
}
4 changes: 3 additions & 1 deletion pages/home/4337-guides/permissionless-detailed.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Callout, Steps, Tabs } from 'nextra/components'

# Permissionless.js detailed guide
# Permissionless.js Detailed Guide

In this guide, you will learn how to sponsor the deployment of an ERC-4337 Safe account and its user operations using [Pimlico](https://pimlico.io) infrastructure and the [permissionless](https://npmjs.com/permissionless) library.

Expand Down Expand Up @@ -101,6 +101,8 @@ pnpm install viem permissionless
const signer = privateKeyToAccount(PRIVATE_KEY as Hash)
```

{ /* <!-- vale on --> */}

### Initialize the clients

We need to create a few client instances to query the blockchain network and operate with Pimlico infrastructure.
Expand Down
216 changes: 216 additions & 0 deletions pages/home/4337-guides/permissionless-quickstart.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
import { Callout, Steps } from 'nextra/components'

# Permissionless.js Quickstart Guide

In this guide, you will learn how to sponsor the deployment of an ERC-4337 Safe account and its user operations using [Pimlico](https://pimlico.io) infrastructure and the [permissionless](https://npmjs.com/permissionless) library.

This guide focuses high-level on deploying and using Safe accounts configured with the `Safe4337Module` enabled. For a more detailed guide that shows what happens under the hood, check the [Permissionless detailed guide](./permissionless-detailed).

Pimlico is one of the most popular ERC-4337 account abstraction infrastructure platforms, which provides a suite of tools and services to help build, deploy, and manage smart accounts on EVM-compatible chains.

`permissionless` is a TypeScript library focused on building with the ERC-4337 stack, including smart accounts, bundlers, paymasters, and user operations. Some of its core principles are providing a great developer experience and avoiding vendor lock-in by supporting different providers and ERC-4337 smart accounts, including Safe.

<Callout type='info' emoji='ℹ️'>
If you are already building with the Safe\{Core\} SDK, you may want to follow the [Safe4337Pack guide](../../sdk/relay-kit/guides/safe4337pack.mdx) instead of integrating the `permissionless` library directly into your application.
</Callout>

## Prerequisites

- [Node.js and npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm).
- A [Pimlico account](https://dashboard.pimlico.io) and an API key.

## Install dependencies

Install [viem](https://npmjs.com/viem) and [permissionless](https://npmjs.com/permissionless) dependencies by running the following command:

```bash
pnpm install viem permissionless
```

## Steps

<Steps>

### Imports

These are all the imports required in the script we are building for this guide, which includes `permissionless` and `viem` packages.

{/* <!-- vale off --> */}

```typescript
import 'dotenv/config'
import { ENTRYPOINT_ADDRESS_V06, createSmartAccountClient } from 'permissionless'
import { signerToSafeSmartAccount } from 'permissionless/accounts'
import {
createPimlicoBundlerClient,
createPimlicoPaymasterClient,
} from 'permissionless/clients/pimlico'
import { createPublicClient, http, Hex, encodeFunctionData, parseEther } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import { gnosis } from 'viem/chains'
```

{/* <!-- vale on --> */}

### Setup

These are the constants that will be used in this guide. We have selected Gnosis as the chain, but other [supported networks](../4337-supported-networks.md) can be used. We also need the Pimlico API key and a private key we will use for the owner of the Safe.

{/* <!-- vale off --> */}

```typescript
// Network
const chain = gnosis
const chainName = 'gnosis'
const SPONSORSHIP_POLICY_ID = '<insert_pimlico_sponsorship_policy_id>'

// Keys
const PIMLICO_API_KEY = process.env.PIMLICO_API_KEY
const PRIVATE_KEY = process.env.PRIVATE_KEY as Hex
```

{/* <!-- vale on --> */}

### Create the signer

First, we need a signer instance that will be the owner of the Safe account once it is deployed.

{/* <!-- vale off --> */}

```typescript
const signer = privateKeyToAccount(PRIVATE_KEY as Hash)
```

{ /* <!-- vale on --> */}

### Initialize the clients

We need to create a few client instances to query the blockchain network and operate with Pimlico infrastructure.

Firstly, we instantiate a standard `publicClient` instance for regular Ethereum RPC calls. To do this, we must first define the corresponding RPC URL, depending on our network.

{/* <!-- vale off --> */}

```typescript
const publicClient = createPublicClient({
transport: http(`https://rpc.ankr.com/${chainName}`)
})
```

{ /* <!-- vale on --> */}

Secondly, we instantiate the `bundlerClient` using the Pimlico API `v1`, which is dedicated to the Bundler methods. This API requires a `PIMLICO_API_KEY` that we can get from their [dashboard](https://dashboard.pimlico.io/apikeys).

{/* <!-- vale off --> */}

```typescript
const bundlerClient = createPimlicoBundlerClient({
transport: http(`https://api.pimlico.io/v1/${chainName}/rpc?apikey=${PIMLICO_API_KEY}`),
entryPoint: ENTRYPOINT_ADDRESS_V06
})
```

{ /* <!-- vale on --> */}

Lastly, we instantiate the `paymasterClient` using the Pimlico API `v2`, which is dedicated to the Paymaster methods and responsible for interacting with Pimlico's Verifying Paymaster endpoint and requesting sponsorship.

{/* <!-- vale off --> */}

```typescript
const paymasterClient = createPimlicoPaymasterClient({
transport: http(`https://api.pimlico.io/v2/${chainName}/rpc?apikey=${PIMLICO_API_KEY}`),
entryPoint: ENTRYPOINT_ADDRESS_V06
})
```

{/* <!-- vale on --> */}

### Fetch the gas price

We fetch the current gas price values to add them later to our transaction.

{/* <!-- vale off --> */}

```typescript
const gasPrices = await bundlerClient.getUserOperationGasPrice()
```

### Create the Safe account

We can create a Safe account based on our `signer` address, the `entryPoint` address, an optional `saltNonce` that will affect the resulting address of the deployed Safe, and the `safeVersion` (that must be `1.4.1` or higher).

{/* <!-- vale off --> */}

```typescript
const safeAccount = await signerToSafeSmartAccount(publicClient, {
entryPoint: ENTRYPOINT_ADDRESS_V06,
signer: signer,
saltNonce: 0n, // Optional
safeVersion: '1.4.1',
address: '0x...' // Optional. Only for existing Safe accounts.
})
```

{/* <!-- vale on --> */}

The optional `address` parameter is only used when we already have a Safe account and want to initialize it. Deployments of new Safe accounts should remove this parameter.

### Create the Safe account client

We need to create the smart account client with the following parameters:

{/* <!-- vale off --> */}

```typescript
const safeAccountClient = createSmartAccountClient({
account: safeAccount,
entryPoint: ENTRYPOINT_ADDRESS_V06,
chain: chain,
bundlerTransport: http(`https://api.pimlico.io/v1/${chainName}/rpc?apikey=${PIMLICO_API_KEY}`),
middleware: {
gasPrice: async () => (await bundlerClient.getUserOperationGasPrice()).fast,
sponsorUserOperation: paymasterClient.sponsorUserOperation
}
})
```

{/* <!-- vale on --> */}

In case we want to sponsor the transactions of this Safe using a Pimlico sponsorship id, we need to replace the `sponsorUserOperation` passed in the `middleware` property like this:

{/* <!-- vale off --> */}

```typescript
sponsorUserOperation: ({ userOperation }) => {
return paymasterClient.sponsorUserOperation({
userOperation,
sponsorshipPolicyId: SPONSORSHIP_POLICY_ID
})
}
```

{/* <!-- vale on --> */}

### Submit a transaction

Finally, we call the `sendTransaction` method to submit our transaction.

{/* <!-- vale off --> */}

```typescript
const txHash = await safeAccountClient.sendTransaction({
to: safeAccount.address,
value: parseEther('0'),
data: encodeFunctionData({
abi: '',
functionName: '',
args: []
}),
maxFeePerGas: gasPrices.fast.maxFeePerGas,
maxPriorityFeePerGas: gasPrices.fast.maxPriorityFeePerGas
})
```

{/* <!-- vale on --> */}

</Steps>

0 comments on commit bfef541

Please sign in to comment.