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,
});