Create a delegation
Delegation is the ability for a smart contract account (the delegator) to grant permission to another smart contract account, or externally owned account (the delegate) to perform specific executions on the delegator's behalf, under defined rules and conditions.
Prerequisites
- Install and set up the MetaMask Delegation Toolkit.
- Configure the toolkit.
- Create a delegator account.
The DelegationStruct
type
Delegations are created using the DelegationStruct
type, which is specified as follows:
export type DelegationStruct = {
delegate: Hex; // The address to which the delegation is being granted.
delegator: Hex; // The address that is granting the delegation.
authority: Hex; // Hash of the parent delegation, or the constant ROOT_AUTHORITY.
caveats: CaveatStruct[]; // Caveats that restrict the authority being granted.
salt: bigint; // Used to avoid hash collisions between identical delegations.
signature: Hex; // Signature from the delegator account.
};
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.
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 { createRootDelegation } from "@metamask-private/delegator-core-viem";
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 = createRootDelegation(
delegate,
delegatorSmartAccount.address,
[] // 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-private/delegator-core-viem";
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 delegation that doesn't specify a delegate.
This means that any account can redeem the delegation.
You must create open delegations carefully, to ensure that they are not misused.
Create an open delegation by setting the delegate property to the special address
0x0000000000000000000000000000000000000a11
(available via the constant ANY_BENEFICIARY
).
- example.ts
- config.ts
import { createOpenRootDelegation } from "@metamask-private/delegator-core-viem";
import { delegatorSmartAccount } from "./config.ts";
const openRootDelegation = createOpenRootDelegation(
delegatorSmartAccount.address,
[] // 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-private/delegator-core-viem";
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 {
createRootDelegation,
createDelegation,
getDelegationHashOffchain
} from "@metamask-private/delegator-core-viem";
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 = createRootDelegation(
delegate,
delegatorSmartAccount.address,
[] // 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 authority = getDelegationHashOffchain(delegation);
// The address is used as the delegate address while creating the redelegation.
const leafDelegate = "0xb4821Ab7d5942Bd2533387592068a12608B4a52C"
const leafDelegation = createDelegation(
leafDelegate,
delegate,
authority,
[] // 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-private/delegator-core-viem";
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 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 {
createRootDelegation,
createOpenDelegation,
getDelegationHashOffchain
} from "@metamask-private/delegator-core-viem";
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 = createRootDelegation(
delegate,
delegatorSmartAccount.address,
[] // 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 authority = getDelegationHashOffchain(delegation);
const leafDelegation = createOpenDelegation(
delegate,
authority,
[] // 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-private/delegator-core-viem";
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 { createRootDelegation } from "@metamask-private/delegator-core-viem";
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 = createRootDelegation(
delegate,
delegatorSmartAccount.address,
[] // 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-private/delegator-core-viem";
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 },
});