Create a delegation
The MetaMask Delegation Toolkit enables you to create delegations from a delegator account to a delegate account.
Delegations are compatible with ERC-7710 and ERC-7715, to support a standardized minimal interface. Requesting ERC-7715 permissions and redeeming ERC-7710 delegations are experimental features.
The examples on this page demonstrate delegations without any restrictions. Unrestricted delegations grant complete control over the account to the delegate, which can pose significant security risks. It is crucial to add caveats to limit the delegated authority. Learn how to restrict a delegation using caveat enforcers.
Prerequisites
- Install and set up the Delegation Toolkit.
- Configure the Delegation Toolkit.
- Create a delegator account.
Create a root delegation
A root delegation is a delegation that doesn't derive its authority from another delegation. It is when a delegator delegates its own authority away, as opposed to a redelegation. Create a root delegation as follows:
- example.ts
- config.ts
import { createDelegation } from "@metamask/delegation-toolkit";
import { delegatorSmartAccount } from "./config.ts";
// The address to which the delegation is granted. It can be an EOA address, or
// smart account address.
const delegate = "0x2FcB88EC2359fA635566E66415D31dD381CF5585";
const delegation = createDelegation({
to: delegate,
from: delegatorSmartAccount.address,
caveats: [] // Empty caveats array - we recommend adding appropriate restrictions.
});
import { createPublicClient, http } from "viem";
import { lineaSepolia as chain } from "viem/chains";
import {
Implementation,
toMetaMaskSmartAccount,
} from "@metamask/delegation-toolkit";
const publicClient = createPublicClient({
chain,
transport: http()
});
const privateKey = generatePrivateKey();
const account = privateKeyToAccount(privateKey);
export const delegatorSmartAccount = await toMetaMaskSmartAccount({
client: publicClient,
implementation: Implementation.Hybrid,
deployParams: [account.address, [], [], []],
deploySalt: "0x",
signatory: { account },
});
Create an open root delegation
An open root delegation is a root delegation that doesn't specify a delegate.
This means that any account can redeem the delegation.
You must create open root delegations carefully, to ensure that they are not misused.
Create an open root delegation by setting the delegate property to the special address
0x0000000000000000000000000000000000000a11
(available via the constant ANY_BENEFICIARY
).
- example.ts
- config.ts
import { createOpenDelegation } from "@metamask/delegation-toolkit";
import { delegatorSmartAccount } from "./config.ts";
const openRootDelegation = createOpenDelegation({
from: delegatorSmartAccount.address,
caveats: [] // Empty caveats array - we recommend adding appropriate restrictions.
});
import { createPublicClient, http } from "viem";
import { lineaSepolia as chain } from "viem/chains";
import {
Implementation,
toMetaMaskSmartAccount,
} from "@metamask/delegation-toolkit";
const publicClient = createPublicClient({
chain,
transport: http()
});
const privateKey = generatePrivateKey();
const account = privateKeyToAccount(privateKey);
export const delegatorSmartAccount = await toMetaMaskSmartAccount({
client: publicClient,
implementation: Implementation.Hybrid,
deployParams: [account.address, [], [], []],
deploySalt: "0x",
signatory: { account },
});
Create a redelegation
A recipient of a delegation (the delegate), can redelegate that authority to a third party, potentially applying additional restrictions. Create a redelegation as follows:
- example.ts
- config.ts
import {
createDelegation,
getDelegationHashOffchain
} from "@metamask/delegation-toolkit";
import { delegatorSmartAccount } from "./config.ts";
// The address is used as the root delegator. While creating the leaf delegation,
// the root delegate address will be the delegator address.
const delegate = "0x2FcB88EC2359fA635566E66415D31dD381CF5585";
const delegation = createDelegation({
to: delegate,
from: delegatorSmartAccount.address,
caveats: [] // Empty caveats array - we recommend adding appropriate restrictions.
});
// The authority is the (typed data) hash of the delegation from which the authority is derived.
const parentDelegationHash = getDelegationHashOffchain(delegation);
// The address is used as the delegate address while creating the redelegation.
const leafDelegate = "0xb4821Ab7d5942Bd2533387592068a12608B4a52C"
const leafDelegation = createDelegation({
to: leafDelegate,
from: delegate,
// You can also choose to pass the parent delegation object, and let function
// handle the rest.
parentDelegation: parentDelegationHash,
caveats: [] // Empty caveats array - we recommend adding appropriate restrictions.
});
import { createPublicClient, http } from "viem";
import { lineaSepolia as chain } from "viem/chains";
import {
Implementation,
toMetaMaskSmartAccount,
} from "@metamask/delegation-toolkit";
const publicClient = createPublicClient({
chain,
transport: http()
});
const privateKey = generatePrivateKey();
const account = privateKeyToAccount(privateKey);
export const delegatorSmartAccount = await toMetaMaskSmartAccount({
client: publicClient,
implementation: Implementation.Hybrid,
deployParams: [account.address, [], [], []],
deploySalt: "0x",
signatory: { account },
});
Create an open redelegation
An open redelegation is a redelegation that doesn't specify a delegate. This means that any account can redeem the redelegation. As with open root delegations, you must create open redelegations carefully, to ensure that they are not misused. Create an open redelegation as follows:
- example.ts
- config.ts
import {
createDelegation,
createOpenDelegation,
getDelegationHashOffchain
} from "@metamask/delegation-toolkit";
import { delegatorSmartAccount } from "./config.ts";
// The address is used as the root delegator. While creating the leaf delegation,
// the root delegate address will be the delegator address.
const delegate = "0x2FcB88EC2359fA635566E66415D31dD381CF5585";
const delegation = createDelegation({
to: delegate,
from: delegatorSmartAccount.address,
caveats: [] // Empty caveats array - we recommend adding appropriate restrictions.
});
// The authority is the (typed data) hash of the delegation from which the authority is derived.
const parentDelegationHash = getDelegationHashOffchain(delegation);
const leafDelegation = createOpenDelegation({
from: delegate,
// You can also choose to pass the parent delegation object, and let the function
// handle the rest.
parentDelegation: parentDelegationHash,
caveats: [] // Empty caveats array - we recommend adding appropriate restrictions.
});
import { createPublicClient, http } from "viem";
import { lineaSepolia as chain } from "viem/chains";
import {
Implementation,
toMetaMaskSmartAccount,
} from "@metamask/delegation-toolkit";
const publicClient = createPublicClient({
chain,
transport: http()
});
const privateKey = generatePrivateKey();
const account = privateKeyToAccount(privateKey);
export const delegatorSmartAccount = await toMetaMaskSmartAccount({
client: publicClient,
implementation: Implementation.Hybrid,
deployParams: [account.address, [], [], []],
deploySalt: "0x",
signatory: { account },
});
Sign a delegation
A delegation must be signed by the delegator to be valid for redemption. The MetaMaskSmartAccount
supports signing the delegation using EIP-712 Typed Data via the signDelegation
method.
Sign a delegation as follows:
- example.ts
- config.ts
import { createDelegation } from "@metamask/delegation-toolkit";
import { delegatorSmartAccount } from "./config.ts";
// The address to which the delegation is granted. It can be an EOA address, or
// smart account address.
const delegate = "0x2FcB88EC2359fA635566E66415D31dD381CF5585";
const delegation = createDelegation({
to: delegate,
from: delegatorSmartAccount.address,
caveats: [] // Empty caveats array - we recommend adding appropriate restrictions.
});
const signature = await delegatorSmartAccount.signDelegation({ delegation });
const signedDelegation = {
...delegation,
signature
};
import { createPublicClient, http } from "viem";
import { lineaSepolia as chain } from "viem/chains";
import {
Implementation,
toMetaMaskSmartAccount,
} from "@metamask/delegation-toolkit";
const publicClient = createPublicClient({
chain,
transport: http()
});
const privateKey = generatePrivateKey();
const account = privateKeyToAccount(privateKey);
export const delegatorSmartAccount = await toMetaMaskSmartAccount({
client: publicClient,
implementation: Implementation.Hybrid,
deployParams: [account.address, [], [], []],
deploySalt: "0x",
signatory: { account },
});