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();
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)
ortransfer(address,uint256)
. - One time.
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
- Start index as a number
- Calldata as a hex string
Example
caveatBuilder.addCaveat("allowedCalldata",
0,
encodeAbiParameters([
{ type: "string" },
{ type: "uint256" }
], [
"Hello Gator",
12345n
])
);
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
- 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',
}
]);
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
- 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
- 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
- After threshold block number as a
bigint
- 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
- A contract address as a hex string
- A factory address as a hex string
- 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
- An ERC-20 contract address as a hex string
- 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
- An ERC-20 contract address as a hex string
- 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
- 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
- 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
- The recipient's address as a hex string
- 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
- The recipient's address as a hex string
- 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
- 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
- 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.
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
- 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
- After threshold timestamp as a number
- 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
- A value as a
bigint
Example
caveatBuilder.addCaveat("valueLte",
1_000_000_000_000_000_000n // 1 ETH in wei
);