| description | Get started quickly with the MetaMask Delegation Toolkit. |
|---|---|
| sidebar_position | 2 |
| sidebar_label | Quickstart |
This page demonstrates how to get started quickly with the MetaMask Delegation Toolkit, by creating a delegator account and completing the delegation lifecycle (creating, signing, and redeeming a delegation).
Install and set up the Delegation Toolkit.
Set up a Viem Public Client using Viem's createPublicClient function.
This client will let the delegator account 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(),
});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.
import { createBundlerClient } from "viem/account-abstraction";
const bundlerClient = createBundlerClient({
client: publicClient,
transport: http("https://your-bundler-rpc.com"),
});Create a delegator smart account to set up a delegation.
This example configures a Hybrid delegator account:
import {
Implementation,
toMetaMaskSmartAccount,
} from "@metamask/delegation-toolkit";
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 },
});Create a delegate account to receive the delegation. The delegate can be either a smart account or an externally owned account (EOA).
This example uses a smart account:
import {
Implementation,
toMetaMaskSmartAccount,
} from "@metamask/delegation-toolkit";
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 },
});Create a root delegation from the delegator account to the delegate account.
This example passes an empty caveats array, which means the delegate can perform any action on the delegator's behalf.
We recommend restricting the delegation by adding caveat enforcers.
import { createDelegation } from "@metamask/delegation-toolkit";
const delegation = createDelegation({
to: delegateSmartAccount.address,
from: delegatorSmartAccount.address,
caveats: [] // Empty caveats array - we recommend adding appropriate restrictions.
});Sign the delegation 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,
};The delegate account can now redeem the delegation.
The redeem transaction is sent to the DelegationManager contract, which validates the delegation and
executes actions on the delegator's behalf.
To prepare the calldata for the redeem transaction, use the redeemDelegation utility function from the Delegation Toolkit.
import {
createExecution,
DelegationFramework,
SINGLE_DEFAULT_MODE,
} from "@metamask/delegation-toolkit";
import { zeroAddress } from "viem";
const delegations = [ signedDelegation ];
const executions = [{
target: zeroAddress,
value: 0n,
callData: "0x"
}];
const redeemDelegationCalldata = DelegationFramework.encode.redeemDelegations({
delegations: [ delegations ],
modes: [ SINGLE_DEFAULT_MODE ],
executions: [ executions ]
});
const userOperationHash = await bundlerClient.sendUserOperation({
account: delegateSmartAccount,
calls: [
{
to: delegateSmartAccount.address,
data: redeemDelegationCalldata
}
],
maxFeePerGas: 1n,
maxPriorityFeePerGas: 1n,
});