Skip to main content

Delegation Toolkit documentation

Embed MetaMask Smart Accounts into your dapp, enabling new user experiences.

EIP-7715 quickstart

This page demonstrates how to use ERC-7715 to request permissions from a wallet, and execute transactions on a user's behalf.

Prerequisites

Steps

1. Set up a Wallet Client

Set up a Viem Wallet Client using Viem's createWalletClient function. This client will help you interact with MetaMask Flask.

Then, extend the Wallet Client functionality using erc7715ProviderActions. These actions enable you to request ERC-7715 permissions from the user.

import { createWalletClient, custom } from "viem";
import { erc7715ProviderActions } from "@metamask/delegation-toolkit/experimental";

const walletClient = createWalletClient({
transport: custom(window.ethereum),
}).extend(erc7715ProviderActions());

2. Set up a Public Client

Set up a Viem Public Client using Viem's createPublicClient function. This client will help you query the account state and interact with blockchain networks.

import { createPublicClient, http } from "viem";
import { sepolia as chain } from "viem/chains";

const publicClient = createPublicClient({
chain,
transport: http(),
});

3. Set up a session account

Set up a session account which can either be a smart account or an externally owned account (EOA) to request ERC-7715 permissions. This account is responsible for executing transactions on behalf of the user.

This example uses MetaMask Smart Accounts as a session account.

import { privateKeyToAccount } from "viem/accounts";
import {
toMetaMaskSmartAccount,
Implementation
} from "@metamask/delegation-toolkit";

const privateKey = "0x...";
const account = privateKeyToAccount(privateKey);

const sessionAccount = await toMetaMaskSmartAccount({
client: publicClient,
implementation: Implementation.Hybrid,
deployParams: [account.address, [], [], []],
deploySalt: "0x",
signatory: { account },
});

4. Request ERC-7715 permissions

Request ERC-7715 permissions from the user. Currently, only the native-token-stream permission type is supported, which allows the dapp to stream native tokens from the user's wallet.

const expiry = Math.floor(Date.now() / 1000 + 604_800); // 1 week from now.
const currentTime = Math.floor(Date.now() / 1000); // now

const grantedPermissions = await walletClient.grantPermissions([{
chainId: chain.id,
expiry,
signer: {
type: "account",
data: {
address: sessionAccount.address,
},
},
permission: {
type: "native-token-stream",
data: {
initialAmount: 1n, // 1 wei
amountPerSecond: 1n, // 1 wei per second
maxAmount: 10n, // 10 wei
startTime: currentTime,
justification: "Payment for a week long subscription",
},
},
}]);

5. Set up a Bundler Client

Set up a Viem Bundler Client using Viem's createBundlerClient function. This lets you use the bundler service to estimate gas for user operations and submit transactions to the network.

Then, extend the Bundler Client functionality using erc7710BundlerActions. These actions enable you to redeem ERC-7715 permissions, and execute transactions on a user's behalf.

import { createBundlerClient } from "viem/account-abstraction";
import { erc7710BundlerActions } from "@metamask/delegation-toolkit/experimental";

const bundlerClient = createBundlerClient({
client: publicClient,
transport: http("https://your-bundler-rpc.com"),
// Allows you to use the same Bundler Client as paymaster.
paymaster: true
}).extend(erc7710BundlerActions());

6. Redeem ERC-7715 permissions

The session account can now redeem the delegation. The redeem transaction is sent to the DelegationManager contract, which validates the delegation and executes actions on the user's behalf.

To redeem the permissions, you can use the sendUserOperationWithDelegation bundler client action.

// These properties must be extracted from the permission response.
const permissionsContext = grantedPermissions[0].context;
const delegationManager = grantedPermissions[0].signerMeta.delegationManager;
const accountMetadata = grantedPermissions[0].accountMeta;

// Calls without permissionsContext and delegationManager will be executed
// as a normal user operation.
const userOperationHash = await bundlerClient.sendUserOperationWithDelegation({
publicClient,
account: sessionAccount,
calls: [
{
to: sessionAccount.address,
data: "0x",
value: 1n,
permissionsContext,
delegationManager,
},
],
// Appropriate values must be used for fee-per-gas.
maxFeePerGas: 1n,
maxPriorityFeePerGas: 1n
accountMetadata,
});