Skip to main content

Restrict a delegation

Caveat enforcers are used to apply specific rules and conditions to a delegation, ensuring that delegated executions are only performed under predefined circumstances.

The CaveatBuilder interface offers an intuitive way to define the caveats property of a delegation. Use the CaveatBuilder to easily ensure that your delegations grant only the necessary authority.

Access the CaveatBuilder

To use the CaveatBuilder, first access it from the DelegatorClient object using the following code:

const caveatBuilder = delegatorClient.createCaveatBuilder();

Add caveats to the builder

Once you have the CaveatBuilder instance, add caveats to it using the addCaveat method, specifying the caveat type and its parameters.

addCaveat allows you to chain multiple caveats together. For example:

const caveats = caveatBuilder
// allowedTargets accepts an array of addresses.
.addCaveat("allowedTargets", ["0xc11F3a8E5C7D16b75c9E2F60d26f5321C6Af5E92"])
// allowedMethods accepts an array of methods.
.addCaveat("allowedMethods", [
"approve(address,uint256)",
"transfer(address,uint256)"
])
// limitedCalls accepts a number.
.addCaveat("limitedCalls", 1)
.build();
note

This example uses Viem's toFunctionSelector utility to convert the function signatures to 4-byte hex strings.

This example adds three caveats, allowing the caller to only use the delegation:

  • To interact with the address 0xc11F3a8E5C7D16b75c9E2F60d26f5321C6Af5E92.
  • With call data to invoke either approve(address,unit256) or transfer(address,uint256).
  • One time.
Important

Be aware that delegations without caveats are entirely permissive. It is crucial to add appropriate caveats to restrict the delegated authority sufficiently. Failing to do so could result in unintended access or actions.

Caveat types

The CaveatBuilder supports various caveat types, each serving a specific purpose. These caveat types correspond to the out-of-the-box caveat enforcers that the MetaMask Delegation Toolkit provides.

allowedCalldata

Limits the calldata that is executed.

Parameters

  1. Start index as a number
  2. Calldata as a hex string

Example

caveatBuilder.addCaveat("allowedCalldata",
0,
encodeAbiParameters([
{ type: "string" },
{ type: "uint256" }
], [
"Hello Gator",
12345n
])
);
note

This example uses Viem's encodeAbiParameters utility to encode the parameters as ABI-encoded hex strings.

allowedMethods

Limits what methods the delegate can call.

Parameters

  1. An array of methods as 4-byte hex strings, ABI function signatures, or ABIFunction objects

Example

caveatBuilder.addCaveat("allowedMethods", [
"0xa9059cbb",
"transfer(address,uint256)",
{
name: 'transfer',
type: 'function',
inputs: [
{ name: 'recipient', type: 'address' },
{ name: 'amount', type: 'uint256' }
],
outputs: [],
stateMutability: 'nonpayable',
}
]);
note

This example adds the transfer function to the allowed methods in three different ways - as the 4-byte function selector, the ABI function signature, and the ABIFunction object.

allowedTargets

Limits what addresses the delegate can call.

Parameters

  1. An array of addresses as hex strings

Example

caveatBuilder.addCaveat("allowedTargets", [
"0xc11F3a8E5C7D16b75c9E2F60d26f5321C6Af5E92",
"0xB2880E3862f1024cAC05E66095148C0a9251718b"
]);

argsEqualityCheck

Ensures that the args provided when redeeming the delegation are equal to the terms specified on the caveat.

Parameters

  1. The expected args as a hex string

Example

caveatBuilder.addCaveat("argsEqualityCheck",
"0xf2bef872456302645b7c0bb59dcd96ffe6d4a844f311ebf95e7cf439c9393de2"
);

blockNumber

Specifies a range of blocks through which the delegation will be valid.

Parameters

  1. After threshold block number as a bigint
  2. Before threshold block number as a bigint

You can specify 0n to indicate that there is no limitation on a threshold.

Example

caveatBuilder.addCaveat("blocknumber",
19426587n,
0n
);

deployed

Ensures a contract is deployed, and if not, deploys the contract.

Parameters

  1. A contract address as a hex string
  2. A factory address as a hex string
  3. Bytecode as a hex string

Example

caveatBuilder.addCaveat("deployed",
"0xc11F3a8E5C7D16b75c9E2F60d26f5321C6Af5E92",
"0x4fa079Afaa26A601FF458bC826FB498621f5E2e1",
"0x..." // The deploy bytecode for the contract at 0xc11F3a8E5C7D16b75c9E2F60d26f5321C6Af5E92
);

erc20TransferAmount

Limits the transfer of ERC-20 tokens.

Parameters

  1. An ERC-20 contract address as a hex string
  2. Amount as a bigint

Example

caveatBuilder.addCaveat("erc20TransferAmount",
"0xc11F3a8E5C7D16b75c9E2F60d26f5321C6Af5E92",
1_000_000n
);

erc20BalanceGte

Ensures the delegator's ERC-20 balance has increased by at least a specified amount after the execution has been performed, regardless of what the execution is.

Parameters

  1. An ERC-20 contract address as a hex string
  2. Amount as a bigint

Example

caveatBuilder.addCaveat("erc20BalanceGte",
"0xc11F3a8E5C7D16b75c9E2F60d26f5321C6Af5E92",
1_000_000n
);

id

Specifies an ID for multiple delegations. Once one of them is redeemed, the other delegations with the same ID are revoked.

Parameters

  1. An ID as a number

Example

caveatBuilder.addCaveat("id",
123456
);

limitedCalls

Limits the number of times the delegate can perform executions on the delegator's behalf.

Parameters

  1. A count as a number

Example

caveatBuilder.addCaveat("limitedCalls",
1
);

nativeBalanceGte

Ensures that the recipient's native token balance has increased by at least the specified amount.

Parameters

  1. The recipient's address as a hex string
  2. The amount by which the balance must have increased as a bigint

Example

caveatBuilder.addCaveat("nativeBalanceGte",
"0x3fF528De37cd95b67845C1c55303e7685c72F319",
1_000_000n
);

nativeTokenPayment

Enforces payment in native token (for example, ETH) for the right to use the delegation. A permissions context allowing payment must be provided as the args when redeeming the delegation.

Parameters

  1. The recipient's address as a hex string
  2. The amount that must be paid as a bigint

Example

caveatBuilder.addCaveat("nativeTokenPayment",
"0x3fF528De37cd95b67845C1c55303e7685c72F319",
1_000_000n
);

nativeTokenTransferAmount

Enforces an allowance of native currency (for example, ETH).

Parameters

  1. The allowance as a bigint

Example

caveatBuilder.addCaveat("nativeTokenTransferAmount",
1_000_000n
);

nonce

Adds a nonce to a delegation, and revokes previous delegations by incrementing the current nonce by calling incrementNonce(address _delegationManager).

Parameters

  1. A nonce as a hex string

Example

caveatBuilder.addCaveat("nonce",
"0x1"
);

redeemer

Limits the addresses that can redeem the delegation. This caveat enforcer is designed for restricting smart contracts or EOAs lacking delegation support, and can be placed anywhere in the delegation chain to restrict the redeemer.

note

Delegator accounts with delegation functionalities can bypass these restrictions by delegating to other addresses. For example, Alice makes Bob the redeemer. This condition is enforced, but if Bob is a delegator he can create a separate delegation to Carol that allows her to redeem Alice's delegation through Bob.

Parameters

  1. An array of addresses as hex strings

Example

caveatBuilder.addCaveat("redeemer",
[
"0xb4aE654Aca577781Ca1c5DE8FbE60c2F423f37da",
"0x6be97c23596ECed7170fdFb28e8dA1Ca5cdc54C5"
]
);

timestamp

Specifies a range of timestamps through which the delegation will be valid.

Parameters

  1. After threshold timestamp as a number
  2. Before threshold timestamp as a number

You can specify 0 to indicate that there is no limitation on a threshold.

Example

caveatBuilder.addCaveat("timestamp",
499165200,
1445412480
);

valueLte

Limits the value of native tokens that the delegate can spend.

Parameters

  1. A value as a bigint

Example

caveatBuilder.addCaveat("valueLte",
1_000_000_000_000_000_000n // 1 ETH in wei
);