Skip to main content

Quickstart

The quickstart demonstrates creating a delegator account and completing the delegation lifecycle, i.e. creating, signing, and redeeming a delegation.

Prerequisites

Install and set up the MetaMask Delegation Toolkit.

Steps

1. Set up a Public Client

Set up a Public Client using the createPublicClient function from Viem. This client will let MetamaskSmartAccount query the signer's account state and interact with smart contracts.

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

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

2. Set up a Bundler Client

Set up a 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.

import { createBundlerClient } from "viem/account-abstraction";

const bundlerClient = createBundlerClient({
client: publicClient,
transport: http("https://your-bundler-rpc.com"),
});

3. Create a delegator account

Create a delegator account to set up the root delegation. The delegator must be a smart account.

This quickstart example uses the Hybrid implementation for the delegator account.

import {
Implementation,
toMetaMaskSmartAccount,
} from "@metamask-private/delegator-core-viem";
import { privateKeyToAccount } from "viem/accounts";

const delegatorAccount = privateKeyToAccount("0x...");

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

4. Create a delegate account

After creating the delegator account, you also need a delegate account to receive the root delegation. The delegate can be either a smart account or an EOA.

This quickstart example uses a smart account.

import {
Implementation,
toMetaMaskSmartAccount,
} from "@metamask-private/delegator-core-viem";
import { privateKeyToAccount } from "viem/accounts";

const delegateAccount = privateKeyToAccount("0x...");

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

5. Create a delegation

Create a root delegation from the delegatorSmartAccount to the delegateSmartAccount.

This quickstart example passes an empty caveats array, which means the delegate can perform any action on the delegator's behalf. To restrict the delegation, add caveat enforcers.

import { createRootDelegation } from "@metamask-private/delegator-core-viem";

const delegation = createRootDelegation(
delegateSmartAccount.address,
delegatorSmartAccount.address,
[] // Empty caveats array - we recommend adding appropriate restrictions.
);

6. Sign the delegation

Once the delegation is created, sign it using the signDelegation method from MetamaskSmartAccount. Alternatively, you can use the Delegation Toolkit's signDelegation utility. The signed delegation will be used later to perform actions on behalf of the delegator.

const signature = await delegatorSmartAccount.signDelegation({
delegation
});

const signedDelegation = {
...delegation,
signature,
};

7. Redeem the delegation

After signing the delegation, the delegateSmartAccount can redeem it. The redeem transaction is sent to the DelegationManager contract, which validates the delegation and executes actions on the delegator's behalf.

To prepare the call data for the redeem transaction, use the redeemDelegation utility function from the Delegation Toolkit.

Learn more about redeeming a delegation.

import {
createExecution,
createRootDelegation,
DelegationFramework,
SINGLE_DEFAULT_MODE,
} from "@metamask-private/delegator-core-viem";
import { zeroAddress } from "viem";

const delegations = [ signedDelegation ];

const executions = [{
target: zeroAddress,
value: 0n,
callData: "0x"
}];

const redeemDelegationCalldata = DelegationFramework.encode.redeemDelegations(
[ delegations ],
[ SINGLE_DEFAULT_MODE ],
[ executions ]
);

const userOperationHash = await bundlerClient.sendUserOperation({
account: delegateSmartAccount,
calls: [
{
to: delegateSmartAccount.address,
data: redeemDelegationCalldata
}
],
maxFeePerGas: 1n,
maxPriorityFeePerGas: 1n,
});