Manage delegations
Delegation is the ability for an account owner (the delegator) to grant permission to another smart contract account (SCA) or externally owned account (EOA) to perform specific actions on the delegator's behalf, under defined rules and conditions. This page provides instructions to complete specific tasks within the delegation lifecycle.
Prerequisites
- Install and set up the MetaMask Delegation Toolkit.
- Configure the toolkit.
Create a delegation
A delegation is an instance of DelegationStruct
, where delegator
is the account granting
permission to the delegate
account.
Caveats are constraints placed on the granted permission.
export type DelegationStruct = {
delegate: Hex;
delegator: Hex;
authority: Hex;
caveats: CaveatStruct[];
salt: bigint;
signature: Hex;
};
The following is an example of creating a delegation using the provided helper function:
import {
createDelegation,
} from "@codefi/delegator-core-viem";
...
const delegation = createDelegation(
delegatorClient.account.address,
'0x2FcB88EC2359fA635566E66415D31dD381CF5585',
);
This code sample assumes the existence of a
delegatorClient
.
Restrict a delegation
In the previous example, no caveats enforcers were provided, which would grant the delegate complete control over the delegator's account. While this level of access might be acceptable in certain scenarios, such as delegating to another account you own or to a trusted family member, it is essentially equivalent to handing over your private keys.
We strongly recommend applying caveats enforcers to limit delegated permissions.
The MetaMask Delegation Toolkit provides some out-of-the-box caveat enforcers that cover common use cases. For more granular or custom control, you can also create a custom caveat enforcer.
You can apply multiple caveats to a single delegation by adding the address of each caveat enforcer
to the caveats
array in the delegation struct.
We recommend using the CaveatBuilder
interface to easily apply multiple caveat enforcers to a delegation.
Learn more in Restrict a delegation.
Stacking caveats in this way enables highly customizable access control. For example, using a combination of out-of-the-box caveat enforcers can result in a delegation with the caveats that the delegate can:
- Only send up to 100 USDC.
- Only send it to Alice.
- Only send funds twice.
Open delegations
In some cases, it might be optimal to create more open-ended delegations by not specifying a single delegate.
Open delegations can be redeemed by any account that meets the requirements set by the caveat enforcers.
To create an open delegation, set the delegate
to the special value address(0xa11)
.
Sign a delegation
The delegator must sign a delegation in order for it to be valid. The following is an example of signing a delegation:
const signedDelegation = await delegatorClient.signDelegation(delegation);
This code sample assumes the existence of a
delegatorClient
.
The user interaction this triggers depends on the configured signer.
Store a delegation
Delegations often need to be stored for extended periods of time, so it's crucial to store them securely until they are redeemed.
The MetaMask Delegation Toolkit offers a DelegationStorageClient
to provide storage for signed delegations.
Import the DelegationStorageClient
from the @codefi/delegator-core-viem
package.
When using this service, delegations are stored in MetaMask's secure storage to ensure they are available when needed.
See more information about storing and retrieving delegations using DelegationStorageClient
.
Redeem a delegation
Redeeming a delegation occurs when the delegate initiates an on-chain action within the bounds of the caveats. For example, Bob (the delegate) might transfer an NFT from Alice's account (the delegator) to Carol's account.
If there are no caveats limiting it, delegations can be redeemed multiple times.
The delegation must be signed by the delegator, and is redeemed by sending a user operation that executes the delegated action.
The following is an example of redeeming a delegation:
const action = {
to: account.address,
data: "0x",
value: 0n,
};
const signedUserOp = await client.createAndSignInvokeUserOp(
[delegation],
action,
userOpOptions,
nonce,
);
const { result: userOpHash } = await bundler.sendUserOp(
signedUserOp,
client.account.environment.EntryPoint,
);
This code sample assumes the existence of a valid delegation
to redeem.
Redelegate a delegation
Redelegating is the act of assigning an existing delegation to a new account. When redelegating:
- The account redelegating retains the same permissions as before they redelegated, and any caveats applied to their delegation are unchanged.
- All caveats applied to the delegation are maintained and enforced on the account that receives the redelegation.
The following is an example of redelegating a delegation:
import {
createDelegation,
getDelegationHashOffchain,
} from "@codefi/delegator-core-viem";
const delegate = ACCOUNT_RECEIVING_REDELEGATION;
const delegator = ACCOUNT_WITH_EXISTING_DELEGATION;
const authority = getDelegationHashOffchain(delegation)
createDelegation(delegate, delegator, authority);