From 8c39f76be83570b5b9d6add95523cb402f9d9ca2 Mon Sep 17 00:00:00 2001 From: Alexandra Tran Date: Fri, 22 Aug 2025 15:45:44 -0700 Subject: [PATCH 1/4] Refactor and update delegation content --- delegation-toolkit/concepts/delegation.md | 140 ------- .../{ => delegation}/caveat-enforcers.md | 20 +- .../concepts/delegation/index.md | 173 +++++++++ delegation-toolkit/concepts/environment.md | 4 +- delegation-toolkit/concepts/smart-accounts.md | 2 +- .../store-retrieve-delegations.md | 2 +- .../get-started/erc7715-quickstart.md | 2 +- delegation-toolkit/get-started/install.md | 4 +- .../smart-account-quickstart/eip7702.md | 4 +- .../smart-account-quickstart/index.md | 4 +- delegation-toolkit/guides/configure.md | 4 +- .../guides/create-delegation/index.md | 349 ----------------- .../guides/delegation/_category_.json | 4 + .../create-custom-caveat-enforcer.md | 2 +- .../delegation/execute-on-users-behalf.md | 232 ++++++++++++ .../restrict-delegation.md | 4 +- .../guides/redeem-delegation.md | 352 ------------------ .../smart-accounts/create-smart-account.md | 4 +- delegation-toolkit/index.md | 4 +- .../reference/api/delegation.md | 7 +- delegation-toolkit/reference/caveats.md | 2 +- vercel.json | 10 +- 22 files changed, 450 insertions(+), 879 deletions(-) delete mode 100644 delegation-toolkit/concepts/delegation.md rename delegation-toolkit/concepts/{ => delegation}/caveat-enforcers.md (89%) create mode 100644 delegation-toolkit/concepts/delegation/index.md delete mode 100644 delegation-toolkit/guides/create-delegation/index.md create mode 100644 delegation-toolkit/guides/delegation/_category_.json rename delegation-toolkit/guides/{create-delegation => delegation}/create-custom-caveat-enforcer.md (99%) create mode 100644 delegation-toolkit/guides/delegation/execute-on-users-behalf.md rename delegation-toolkit/guides/{create-delegation => delegation}/restrict-delegation.md (97%) delete mode 100644 delegation-toolkit/guides/redeem-delegation.md diff --git a/delegation-toolkit/concepts/delegation.md b/delegation-toolkit/concepts/delegation.md deleted file mode 100644 index 003a68ccd17..00000000000 --- a/delegation-toolkit/concepts/delegation.md +++ /dev/null @@ -1,140 +0,0 @@ ---- -description: Learn about delegation, the delegation lifecycle, and the Delegation Framework. -sidebar_position: 2 ---- - -# Delegation - -*Delegation* is the ability for a [MetaMask smart account](smart-accounts.md) to grant permission to another smart account -or externally owned account (EOA) to perform specific executions on its behalf, under defined rules and restrictions. -The account that grants the permission is called the *delegator account*, while the account that receives the permission -is called the *delegate account*. - -The MetaMask Delegation Toolkit includes the following delegation features: - -- **Caveats** - Users can use [caveat enforcers](caveat-enforcers.md) to apply rules and restrictions to delegations. - For example: Alice delegates the ability to spend her USDC to Bob, limiting the amount to 100 USDC. - -- **Chain of delegations** - Users can redelegate permissions that have been delegated to them, creating a chain of delegations across trusted parties. - -Delegations are created using the `Delegation` type, which is specified as follows: - -```typescript -export type Delegation = { - delegate: Hex; // The address to which the delegation is being granted. - delegator: Hex; // The address that is granting the delegation. - authority: Hex; // Hash of the parent delegation, or the constant ROOT_AUTHORITY. - caveats: Caveat[]; // Caveats that restrict the authority being granted. - salt: Hex; // Used to avoid hash collisions between identical delegations. - signature: Hex; // Signature from the delegator account. -}; -``` - -## Delegation lifecycle - -The delegation lifecycle is as follows: - -1. **Delegation creation** - A delegation is initialized, and the delegator account signs it. - -2. **Caveat enforcement** - The caveats applied to the delegation specify conditions under which - the delegation can be redeemed. - -3. **Delegation storage** - The delegation can be stored, enabling retrieval for future redemption. - - :::note - [Storing and retrieving delegations](../experimental/store-retrieve-delegations.md) using the toolkit's - `DelegationStorageClient` is an experimental feature. - ::: - -4. **Delegation redemption** - The delegate (the account being granted the permission) redeems the - delegation through an [ERC-4337 user operation](smart-accounts.md#account-abstraction-erc-4337), - which verifies that the delegated authority is valid in order to perform the execution. - -See [how to create a delegation](../guides/create-delegation/index.md) to get started with the -delegation lifecycle. - -## Delegation Framework - -The MetaMask Delegation Toolkit includes the Delegation Framework, which is a -[set of comprehensively audited smart contracts](https://github.com/MetaMask/delegation-framework) that -collectively handle delegator account creation, the delegation lifecycle, -and caveat enforcement. -It consists of the following components: - -- **Delegator Core** - Delegator Core contains the logic for the ERC-4337 compliant delegator accounts. - It defines the interface needed for the Delegation Manager to invoke executions on behalf of the accounts. - -- **Delegator account implementations** - There are [multiple delegator account implementations](smart-accounts.md#smart-account-implementation-types), - with the main difference being the signature scheme used to manage the underlying account. - -- **Delegation Manager** - The Delegation Manager validates delegations and triggers executions - on behalf of the delegator, ensuring tasks are executed accurately and securely. - - When a delegation is redeemed, the Delegation Manager performs the following steps. - It processes a single step for all redemptions before proceeding to the next one: - - 1. Validates the input data by ensuring the lengths of `permissionContexts`, `modes`, and - `executionCallDatas` match, or throws `BatchDataLengthMismatch`. - 2. Decodes and validates the delegation, checking that the caller (`msg.sender`) is the delegate - and that there are no empty signatures, or throws `InvalidDelegate`. - 3. Verifies delegation signatures, ensuring validity using `ECDSA` (for EOAs) or - `isValidSignature` (for contracts), or throws `InvalidSignature`. - 4. Validates the delegation chain's authority and ensures delegations are not disabled. - 5. Executes the `beforeHook` for each `caveat` in the delegation, passing relevant data (`terms`, - `arguments`, `mode`, `execution` `calldata`, and `delegationHash`) to the caveat enforcer. - 6. Calls `executeFromExecutor` to perform the delegation's execution, either by the delegator or - the caller for self-authorized executions. - 7. Executes the `afterHook` for each `caveat`, similar to the `beforeHook`, passing required data - to enforce post-execution conditions. - 8. Emits `RedeemedDelegation` events for each delegation that was successfully redeemed. - -- **Caveat enforcers** - [Caveat enforcers](caveat-enforcers.md) manage rules and restrictions for delegations, - providing fine-tuned control over delegated executions. - -## Delegation redemption flow - -This diagram illustrates how a delegation is created and subsequently redeemed on the Delegation Manager. -The Delegation Manager is responsible for validating the signature of the delegation and the caveat enforcers. -If everything is correct, it allows a delegate to execute an action on behalf of the delegator. - -Learn more about the caveat enforcer hooks in the [Caveat enforcers](caveat-enforcers.md) section. - -```mermaid -%%{ - init: { - 'sequence': { - 'actorMargin': 25, - 'width': 200 - } - } -}%% - -sequenceDiagram - participant Delegator - participant Delegate - participant Manager as Delegation Manager - participant Enforcer as Caveat enforcer - - Delegator->>Delegator: Create delegation with caveat enforcers - Delegator->>Delegator: Sign delegation - Delegator->>Delegate: Send signed delegation - Note right of Delegate: Hold delegation until redemption - - Delegate->>Manager: redeemDelegations() with delegation & execution details - Manager->>Delegator: isValidSignature() - Delegator-->>Manager: Confirm valid (or not) - - Manager->>Enforcer: beforeAllHook() - Note right of Manager: Expect no error - Manager->>Enforcer: beforeHook() - Note right of Manager: Expect no error - - Manager->>Delegator: executeFromExecutor() with execution details - Delegator->>Delegator: Perform execution - Note right of Manager: Expect no error - - Manager->>Enforcer: afterHook() - Note right of Manager: Expect no error - Manager->>Enforcer: afterAllHook() - Note right of Manager: Expect no error -``` diff --git a/delegation-toolkit/concepts/caveat-enforcers.md b/delegation-toolkit/concepts/delegation/caveat-enforcers.md similarity index 89% rename from delegation-toolkit/concepts/caveat-enforcers.md rename to delegation-toolkit/concepts/delegation/caveat-enforcers.md index f8dc236e715..dcefa1dc49f 100644 --- a/delegation-toolkit/concepts/caveat-enforcers.md +++ b/delegation-toolkit/concepts/delegation/caveat-enforcers.md @@ -1,12 +1,12 @@ --- description: Learn about caveat enforcers and how they restrict delegations. -sidebar_position: 4 +sidebar_position: 2 --- # Caveat enforcers The MetaMask Delegation Toolkit provides *caveat enforcers*, which are smart contracts that implement rules and restrictions (*caveats*) on delegations. -They serve as the underlying mechanism that enables conditional execution within the [Delegation Framework](delegation.md#delegation-framework). +They serve as the underlying mechanism that enables conditional execution within the [Delegation Framework](index.md#delegation-framework). A caveat enforcer acts as a gate that validates whether a delegation can be used for a particular execution. When a delegate attempts to execute an action on behalf of a delegator, each caveat enforcer specified in the delegation evaluates whether the execution meets its defined criteria. @@ -119,7 +119,7 @@ This "all-or-nothing" approach ensures that delegations only execute exactly as ## Caveat builder -While caveat enforcers operate at the smart contract level, most developers interact with them through the [`CaveatBuilder`](../guides/create-delegation/restrict-delegation.md) interface in the MetaMask Delegation Toolkit. +While caveat enforcers operate at the smart contract level, most developers interact with them through the [`CaveatBuilder`](../../guides/delegation/restrict-delegation.md) interface in the MetaMask Delegation Toolkit. The `CaveatBuilder` provides a developer-friendly TypeScript API that: @@ -127,7 +127,7 @@ The `CaveatBuilder` provides a developer-friendly TypeScript API that: - Provides type-checking and validation for caveat parameters. - Handles the creation of the `caveats` array needed when creating a delegation. -Each [caveat type](../reference/caveats.md) in the `CaveatBuilder` +Each [caveat type](../../reference/caveats.md) in the `CaveatBuilder` corresponds to a specific caveat enforcer contract. For example, when you use: ```typescript @@ -135,7 +135,7 @@ caveatBuilder.addCaveat("allowedTargets", ["0xc11F3a8E5C7D16b75c9E2F60d26f5321C6 ``` The builder is creating a caveat that references the -[`AllowedTargetsEnforcer`](../reference/caveats.md#allowedtargets) contract address and +[`AllowedTargetsEnforcer`](../../reference/caveats.md#allowedtargets) contract address and properly encodes the provided addresses as terms for that enforcer. ## Caveat enforcer best practices @@ -145,14 +145,14 @@ When designing delegations with caveats, consider these best practices: - **Combine caveat enforcers appropriately** - Use multiple caveat enforcers to create comprehensive restrictions. - **Consider caveat enforcer order** - When using caveat enforcers that modify external contract states, the order matters. - For example, using [`NativeTokenPaymentEnforcer`](../reference/caveats.md#nativetokenpayment) before - [`NativeBalanceChangeEnforcer`](../reference/caveats.md#nativebalancechange) might cause validation failures. + For example, using [`NativeTokenPaymentEnforcer`](../../reference/caveats.md#nativetokenpayment) before + [`NativeBalanceChangeEnforcer`](../../reference/caveats.md#nativebalancechange) might cause validation failures. - **Be careful with unbounded delegations** - Always include appropriate caveat enforcers to limit what a delegate can do. ## Available caveat enforcers -The Delegation Toolkit provides [many out-of-the-box caveat enforcers](../reference/caveats.md) +The Delegation Toolkit provides [many out-of-the-box caveat enforcers](../../reference/caveats.md) for common restriction patterns, including: - Limiting target addresses and methods. @@ -160,11 +160,11 @@ for common restriction patterns, including: - Restricting token transfers and approvals. - Limiting execution frequency. -For more complex scenarios, you can also [create custom caveat enforcers](../guides/create-delegation/create-custom-caveat-enforcer.md) by implementing the `ICaveatEnforcer` interface. +For more complex scenarios, you can also [create custom caveat enforcers](../../guides/delegation/create-custom-caveat-enforcer.md) by implementing the `ICaveatEnforcer` interface. ## Attenuating authority with redelegations -When [creating chains of delegations](../guides/create-delegation/index.md#create-a-redelegation), it's important to understand how authority flows and can be restricted. +When creating chains of delegations via [redelegations](index.md#delegation-types), it's important to understand how authority flows and can be restricted. Caveats applied to a chain of delegations are *accumulative*—they stack on top of each other: diff --git a/delegation-toolkit/concepts/delegation/index.md b/delegation-toolkit/concepts/delegation/index.md new file mode 100644 index 00000000000..9871e1fb407 --- /dev/null +++ b/delegation-toolkit/concepts/delegation/index.md @@ -0,0 +1,173 @@ +--- +description: Learn about delegation, the delegation lifecycle, and the Delegation Framework. +sidebar_position: 2 +toc_max_heading_level: 2 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +# Delegation + +*Delegation* is the ability for a [MetaMask smart account](../smart-accounts.md) to grant permission to another smart account +or externally owned account (EOA) to perform specific executions on its behalf, under defined rules and restrictions. +The account that grants the permission is called the *delegator account*, while the account that receives the permission +is called the *delegate account*. + +The MetaMask Delegation Toolkit follows the [ERC-7710](https://eips.ethereum.org/EIPS/eip-7710) standard for smart contract delegation. +In addition, users can use [caveat enforcers](caveat-enforcers.md) to apply rules and restrictions to delegations. +For example: Alice delegates the ability to spend her USDC to Bob, limiting the amount to 100 USDC. + +## Delegation lifecycle + +The delegation lifecycle is as follows: + +1. **Delegation creation** - A delegation is initialized, and the delegator account signs it. + +2. **Caveat enforcement** - The caveats applied to the delegation specify conditions under which + the delegation can be redeemed. + +3. **Delegation storage** - The delegation can be stored, enabling retrieval for future redemption. + +4. **Delegation redemption** - The delegate (the account being granted the permission) redeems the delegation via the Delegation Manager, + which verifies that the delegated authority is valid in order to perform the execution. + +See [how to perform executions on a user's behalf](../../guides/delegation/execute-on-users-behalf.md) to get started with the delegation lifecycle. + +## Delegation types + +There are multiple types of delegations you can create: + +- A **root delegation** is a delegation that doesn't derive its authority from another delegation. + It is when a delegator delegates its own authority away, as opposed to a redelegation. + Use [`createDelegation`](../../reference/api/delegation.md#createdelegation) to create a root delegation. + +- An **open root delegation** is a root delegation that doesn't specify a delegate. + This means that any account can redeem the delegation. + You must create open root delegations carefully, to ensure that they are not misused. + Use [`createOpenDelegation`](../../reference/api/delegation.md#createopendelegation) to create a root delegation. + +- A delegate can **redelegate** permissions that have been granted to them, creating a chain of delegations across trusted parties. + Use [`createDelegation`](../../reference/api/delegation.md#createdelegation) to create a redelegation. + +- An **open redelegation** is a redelegation that doesn't specify a delegate. + This means that any account can redeem the redelegation. + As with open root delegations, you must create open redelegations carefully, to ensure that they are not misused. + Use [`createOpenDelegation`](../../reference/api/delegation.md#createopendelegation) to create an open redelegation. + +## Delegation Framework + +The MetaMask Delegation Toolkit includes the Delegation Framework, which is a +[set of comprehensively audited smart contracts](https://github.com/MetaMask/delegation-framework) that +collectively handle delegator account creation, the delegation lifecycle, +and caveat enforcement. +It consists of the following components: + +- **Delegator Core** - Delegator Core contains the logic for the ERC-4337 compliant delegator accounts. + It defines the interface needed for the Delegation Manager to invoke executions on behalf of the accounts. + +- **Delegator account implementations** - Delegator accounts are smart accounts, and there are [multiple smart account implementations](../smart-accounts.md#smart-account-implementation-types), + with differing signature schemes used to manage the underlying account. + +- **Delegation Manager** - The Delegation Manager validates delegations and triggers executions + on behalf of the delegator, ensuring tasks are executed accurately and securely. + + When you redeem a delegation using [`redeemDelegations`](../../reference/api/delegation.md#redeemdelegations), the Delegation Manager performs the following steps. + It processes a single step for all redemptions before proceeding to the next one: + + 1. Validates the input data by ensuring the lengths of `delegations`, `modes`, and + `executions` match. + 2. Decodes and validates the delegation, checking that the caller is the delegate + and that there are no empty signatures. + 3. Verifies delegation signatures, ensuring validity using ECDSA (for EOAs) or + `isValidSignature` (for contracts). + 4. Validates the delegation chain's authority and ensures delegations are not disabled. + 5. Executes the `beforeHook` for each [caveat](caveat-enforcers.md) in the delegation, passing relevant data (`terms`, + `arguments`, `mode`, `execution` `calldata`, and `delegationHash`) to the caveat enforcer. + 6. Calls `executeFromExecutor` to perform the delegation's execution, either by the delegator or + the caller for self-authorized executions. + 7. Executes the `afterHook` for each caveat, similar to the `beforeHook`, passing required data + to enforce post-execution conditions. + 8. Emits `RedeemedDelegation` events for each delegation that was successfully redeemed. + +- **Caveat enforcers** - [Caveat enforcers](caveat-enforcers.md) manage rules and restrictions for delegations, + providing fine-tuned control over delegated executions. + +## Delegation flow + +This diagram illustrates how a delegation is created and subsequently redeemed on the Delegation Manager. +The Delegation Manager is responsible for validating the signature of the delegation and the caveat enforcers. +If everything is correct, it allows a delegate to execute an action on behalf of the delegator. + +Learn more about the caveat enforcer hooks in the [Caveat enforcers](caveat-enforcers.md) section. + +```mermaid +%%{ + init: { + 'sequence': { + 'actorMargin': 30, + 'width': 250 + } + } +}%% + +sequenceDiagram + participant Delegator + participant Delegate + participant Manager as Delegation Manager + participant Enforcer as Caveat enforcer + + Delegator->>Delegator: Create delegation with caveat enforcers + Delegator->>Delegator: Sign delegation + Delegator->>Delegate: Send signed delegation + Note right of Delegate: Hold delegation until redemption + + Delegate->>Manager: redeemDelegations() with delegation & execution details + Manager->>Delegator: isValidSignature() + Delegator-->>Manager: Confirm valid (or not) + + Manager->>Enforcer: beforeAllHook() + Note right of Manager: Expect no error + Manager->>Enforcer: beforeHook() + Note right of Manager: Expect no error + + Manager->>Delegator: executeFromExecutor() with execution details + Delegator->>Delegator: Perform execution + Note right of Manager: Expect no error + + Manager->>Enforcer: afterHook() + Note right of Manager: Expect no error + Manager->>Enforcer: afterAllHook() + Note right of Manager: Expect no error +``` + +## Execution modes + +When redeeming a delegation using [`redeemDelegations`](../../reference/api/delegation.md#redeemdelegations), you must pass an execution mode for each delegation chain you pass to the method. +The Delegation Toolkit supports several execution modes based on [ERC-7579](https://erc7579.com/): `SINGLE_DEFAULT_MODE`, `SINGLE_TRY_MODE`, `BATCH_DEFAULT_MODE`, and `BATCH_TRY_MODE`. + +### `SINGLE` execution modes + +In `SINGLE` execution modes, only a single delegation chain and a single execution can be provided. This mode processes delegations sequentially: + +1. For each delegation in the chain, all caveats' `before` hooks are called. +2. The single redeemed action is executed. +3. For each delegation in the chain, all caveats' `after` hooks are called. + +### `BATCH` execution modes + +In `BATCH` execution modes, multiple delegation chains and multiple executions can be provided. This mode executes delegations in an interleaved way: + +1. For each chain in the batch, and each delegation in the chain, all caveats' `before` hooks are called. +2. Each redeemed action is executed. +3. For each chain in the batch, and each delegation in the chain, all caveats' `after` hooks are called. + +`BATCH` mode allows for powerful use cases, but the Delegation Framework currently does not include any `BATCH` compatible caveat enforcers. + +### `DEFAULT` modes + +In `DEFAULT` modes, if a revert occurs during redemption, the entire user operation reverts at that point. + +### `TRY` modes + +In `TRY` modes, if a revert occurs during redemption, execution of the user operation continues. diff --git a/delegation-toolkit/concepts/environment.md b/delegation-toolkit/concepts/environment.md index 986c7a16a4a..fe4c046db78 100644 --- a/delegation-toolkit/concepts/environment.md +++ b/delegation-toolkit/concepts/environment.md @@ -8,7 +8,7 @@ import TabItem from "@theme/TabItem"; # Delegator environment -The `DeleGatorEnvironment` object is a component of the MetaMask Delegation Toolkit that defines the contract addresses necessary for interacting with the [Delegation Framework](delegation.md#delegation-framework) on a specific network. +The `DeleGatorEnvironment` object is a component of the MetaMask Delegation Toolkit that defines the contract addresses necessary for interacting with the [Delegation Framework](delegation/index.md#delegation-framework) on a specific network. The delegator environment serves several key purposes: @@ -71,7 +71,7 @@ See the changelog of the toolkit version you are using (in the left sidebar) for Alternatively, you can use the [`getDelegatorEnvironment`](../reference/api/delegation.md#getdelegatorenvironment) function to resolve the environment. This function is especially useful if your delegator is not a smart account when -[creating a redelegation](../guides/create-delegation/index.md#create-a-redelegation). +creating a [redelegation](delegation/index.md#delegation-types). ```typescript import { diff --git a/delegation-toolkit/concepts/smart-accounts.md b/delegation-toolkit/concepts/smart-accounts.md index 181af10d8e3..8426e6754ac 100644 --- a/delegation-toolkit/concepts/smart-accounts.md +++ b/delegation-toolkit/concepts/smart-accounts.md @@ -92,4 +92,4 @@ The MetaMask Smart Accounts flow is as follows: Delegator accounts are a type of MetaMask smart account that allows users to grant permission to other smart accounts or EOAs to perform specific executions on their behalf, under defined rules and restrictions. -Learn more about [delegation](delegation.md). +Learn more about [delegation](delegation/index.md). diff --git a/delegation-toolkit/experimental/store-retrieve-delegations.md b/delegation-toolkit/experimental/store-retrieve-delegations.md index ff22fe8a5be..56f56315d9e 100644 --- a/delegation-toolkit/experimental/store-retrieve-delegations.md +++ b/delegation-toolkit/experimental/store-retrieve-delegations.md @@ -15,7 +15,7 @@ This is an experimental feature and may change in future releases. ::: You can use methods provided by the `DelegationStorageClient` of the MetaMask Delegation Toolkit to store and retrieve -[delegations](../concepts/delegation.md). +[delegations](../concepts/delegation/index.md). ## Prerequisites diff --git a/delegation-toolkit/get-started/erc7715-quickstart.md b/delegation-toolkit/get-started/erc7715-quickstart.md index fc608e0e57b..8907abdeac2 100644 --- a/delegation-toolkit/get-started/erc7715-quickstart.md +++ b/delegation-toolkit/get-started/erc7715-quickstart.md @@ -130,7 +130,7 @@ const bundlerClient = createBundlerClient({ ### 6. Redeem ERC-7715 permissions -The session account can now [redeem the delegation](../guides/redeem-delegation.md). The redeem transaction is sent to the `DelegationManager` contract, which validates the delegation and executes actions on the user's behalf. +The session account can now [redeem the delegation](../experimental/erc-7710-redeem-delegations.md). The redeem transaction is sent to the `DelegationManager` contract, which validates the delegation and executes actions on the user's behalf. To redeem the permissions, you can use the `sendUserOperationWithDelegation` bundler client action. diff --git a/delegation-toolkit/get-started/install.md b/delegation-toolkit/get-started/install.md index 0b3dceeea31..7709ad6b0c3 100644 --- a/delegation-toolkit/get-started/install.md +++ b/delegation-toolkit/get-started/install.md @@ -17,7 +17,7 @@ This page provides instructions to install and set up the MetaMask Delegation To - Install [Yarn](https://yarnpkg.com/), [npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm), or another package manager. - If you plan to use any smart contracts (for example, to - [create a custom caveat enforcer](../guides/create-delegation/create-custom-caveat-enforcer.md)), + [create a custom caveat enforcer](../guides/delegation/create-custom-caveat-enforcer.md)), install [Foundry](https://book.getfoundry.sh/getting-started/installation). ## Steps @@ -33,7 +33,7 @@ npm install @metamask/delegation-toolkit ### 2. (Optional) Install the contracts If you plan to extend the Delegation Framework smart contracts (for example, to -[create a custom caveat enforcer](../guides/create-delegation/create-custom-caveat-enforcer.md)), install +[create a custom caveat enforcer](../guides/delegation/create-custom-caveat-enforcer.md)), install the contract package using Foundry's command-line tool, Forge: ```bash diff --git a/delegation-toolkit/get-started/smart-account-quickstart/eip7702.md b/delegation-toolkit/get-started/smart-account-quickstart/eip7702.md index 493c17982d0..1b92f38837b 100644 --- a/delegation-toolkit/get-started/smart-account-quickstart/eip7702.md +++ b/delegation-toolkit/get-started/smart-account-quickstart/eip7702.md @@ -8,7 +8,7 @@ sidebar_label: EIP-7702 quickstart This quickstart demonstrates how to upgrade your externally owned account (EOA) to support MetaMask Smart Accounts functionality using an [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702) transaction. This enables your EOA to leverage the benefits of account -abstraction, such as batch transactions, gas sponsorship, and [ERC-7710 delegation capabilities](../../concepts/delegation.md). +abstraction, such as batch transactions, gas sponsorship, and [delegation capabilities](../../concepts/delegation/index.md). ## Prerequisites @@ -156,6 +156,6 @@ const userOperationHash = await bundlerClient.sendUserOperation({ ## Next steps -- To grant specific permissions to other accounts from your smart account, [create a delegation](../../how-to/create-delegation/index.md). +- To grant specific permissions to other accounts from your smart account, [create a delegation](../../guides/delegation/execute-on-users-behalf.md). - To quickly bootstrap a MetaMask Smart Accounts project, [use the CLI](../use-the-cli.md). - You can also [use MetaMask SDK to upgrade a MetaMask account to a smart account](/sdk/tutorials/upgrade-eoa-to-smart-account). diff --git a/delegation-toolkit/get-started/smart-account-quickstart/index.md b/delegation-toolkit/get-started/smart-account-quickstart/index.md index b8d29e6e081..91cb43434fe 100644 --- a/delegation-toolkit/get-started/smart-account-quickstart/index.md +++ b/delegation-toolkit/get-started/smart-account-quickstart/index.md @@ -94,8 +94,8 @@ const userOperationHash = await bundlerClient.sendUserOperation({ ## Next steps -- To grant specific permissions to other accounts from your smart account, [create a delegation](../../guides/create-delegation/index.md). +- To grant specific permissions to other accounts from your smart account, [create a delegation](../../guides/delegation/execute-on-users-behalf.md). - This quickstart example uses a Hybrid smart account. You can also [configure other smart account types](../../guides/smart-accounts/create-smart-account.md). -- To upgrade an EOA to a smart account, see the [EIP-7702 quickstart](eip7702-quickstart.md). +- To upgrade an EOA to a smart account, see the [EIP-7702 quickstart](eip7702.md). - To quickly bootstrap a MetaMask Smart Accounts project, [use the CLI](../use-the-cli.md). diff --git a/delegation-toolkit/guides/configure.md b/delegation-toolkit/guides/configure.md index c518247b3cd..b12e70d6851 100644 --- a/delegation-toolkit/guides/configure.md +++ b/delegation-toolkit/guides/configure.md @@ -17,9 +17,7 @@ The MetaMask Delegation Toolkit provides custom middleware for [Pimlico's](https ## Prerequisites -- [Install and set up the Delegation Toolkit](../get-started/install.md). -- Optionally, complete the [smart account quickstart](../get-started/quickstart.md) or [delegation quickstart](../get-started/delegation-quickstart.md) to - familiarize yourself with the toolkit's capabilities. +[Install and set up the Delegation Toolkit.](../get-started/install.md) ## Viem's Account Abstraction API diff --git a/delegation-toolkit/guides/create-delegation/index.md b/delegation-toolkit/guides/create-delegation/index.md deleted file mode 100644 index df9c3da9607..00000000000 --- a/delegation-toolkit/guides/create-delegation/index.md +++ /dev/null @@ -1,349 +0,0 @@ ---- -description: Learn how to create different types of delegations, and how to sign a delegation. -sidebar_position: 6 ---- - -import Tabs from "@theme/Tabs"; -import TabItem from "@theme/TabItem"; - -# Create a delegation - -The MetaMask Delegation Toolkit enables you to create [delegations](../../concepts/delegation.md) -from a delegator account to a delegate account. - -:::note -Delegations are compatible with [ERC-7710](https://eip.tools/eip/7710) and [ERC-7715](https://ethereum-magicians.org/t/erc-7715-grant-permissions-from-wallets/20100), to support a standardized minimal interface. -[Requesting ERC-7715 permissions](../../experimental/erc-7715-request-permissions.md) and [redeeming ERC-7710 delegations](../../experimental/erc-7710-redeem-delegations.md) -are experimental features. -::: - -:::warning -The examples on this page demonstrate delegations without any restrictions. -Unrestricted delegations grant complete control over the account to the delegate, which can pose significant security risks. -It is crucial to add caveats to limit the delegated authority. -Learn how to [restrict a delegation](./restrict-delegation.md) using caveat enforcers. -::: - -## Prerequisites - -- [Install and set up the Delegation Toolkit.](../../get-started/install.md) -- [Configure the Delegation Toolkit.](../configure.md) -- [Create a MetaMask smart account.](../smart-accounts/create-smart-account.md) - -## Create a root delegation - -A *root delegation* is a delegation that doesn't derive its authority from another delegation. -It is when a delegator delegates its own authority away, as opposed to a [redelegation](#create-a-redelegation). -Create a root delegation as follows: - - - - -```typescript -import { createDelegation } from "@metamask/delegation-toolkit"; -import { delegatorSmartAccount } from "./config.ts"; - -// The address to which the delegation is granted. It can be an EOA address, or -// smart account address. -const delegate = "0x2FcB88EC2359fA635566E66415D31dD381CF5585"; - -const delegation = createDelegation({ - to: delegate, - from: delegatorSmartAccount.address, - caveats: [] // Empty caveats array - we recommend adding appropriate restrictions. -}); -``` - - - - - -```typescript -import { createPublicClient, http } from "viem"; -import { sepolia as chain } from "viem/chains"; -import { - Implementation, - toMetaMaskSmartAccount, -} from "@metamask/delegation-toolkit"; - -const publicClient = createPublicClient({ - chain, - transport: http() -}); - -const privateKey = generatePrivateKey(); -const account = privateKeyToAccount(privateKey); - -export const delegatorSmartAccount = await toMetaMaskSmartAccount({ - client: publicClient, - implementation: Implementation.Hybrid, - deployParams: [account.address, [], [], []], - deploySalt: "0x", - signatory: { account }, -}); -``` - - - - -## Create an open root delegation - -An *open root delegation* is a root delegation that doesn't specify a delegate. -This means that any account can redeem the delegation. -You must create open root delegations carefully, to ensure that they are not misused. -Create an open root delegation by setting the delegate property to the special address -`0x0000000000000000000000000000000000000a11` (available via the constant `ANY_BENEFICIARY`). - - - - -```typescript -import { createOpenDelegation } from "@metamask/delegation-toolkit"; -import { delegatorSmartAccount } from "./config.ts"; - -const openRootDelegation = createOpenDelegation({ - from: delegatorSmartAccount.address, - caveats: [] // Empty caveats array - we recommend adding appropriate restrictions. -}); -``` - - - - - -```typescript -import { createPublicClient, http } from "viem"; -import { sepolia as chain } from "viem/chains"; -import { - Implementation, - toMetaMaskSmartAccount, -} from "@metamask/delegation-toolkit"; - -const publicClient = createPublicClient({ - chain, - transport: http() -}); - -const privateKey = generatePrivateKey(); -const account = privateKeyToAccount(privateKey); - -export const delegatorSmartAccount = await toMetaMaskSmartAccount({ - client: publicClient, - implementation: Implementation.Hybrid, - deployParams: [account.address, [], [], []], - deploySalt: "0x", - signatory: { account }, -}); -``` - - - - -## Create a redelegation - -A recipient of a delegation (the delegate), can *redelegate* that authority to a third party, potentially applying additional [restrictions](restrict-delegation.md). -Create a redelegation as follows: - - - - -```typescript -import { - createDelegation, - getDelegationHashOffchain - } from "@metamask/delegation-toolkit"; -import { delegatorSmartAccount } from "./config.ts"; - -// The address is used as the root delegator. While creating the redelegation, -// the root delegate address will be the delegator address. -const delegate = "0x2FcB88EC2359fA635566E66415D31dD381CF5585"; - -const delegation = createDelegation({ - to: delegate, - from: delegatorSmartAccount.address, - caveats: [] // Empty caveats array - we recommend adding appropriate restrictions. -}); - -// The authority is the (typed data) hash of the delegation from which the authority is derived. -const parentDelegationHash = getDelegationHashOffchain(delegation); - -// The address is used as the delegate address while creating the redelegation. -const leafDelegate = "0xb4821Ab7d5942Bd2533387592068a12608B4a52C" - -const leafDelegation = createDelegation({ - to: leafDelegate, - from: delegate, - // You can also choose to pass the parent delegation object, and let function - // handle the rest. - parentDelegation: parentDelegationHash, - caveats: [] // Empty caveats array - we recommend adding appropriate restrictions. -}); -``` - - - - - -```typescript -import { createPublicClient, http } from "viem"; -import { sepolia as chain } from "viem/chains"; -import { - Implementation, - toMetaMaskSmartAccount, -} from "@metamask/delegation-toolkit"; - -const publicClient = createPublicClient({ - chain, - transport: http() -}); - -const privateKey = generatePrivateKey(); -const account = privateKeyToAccount(privateKey); - -export const delegatorSmartAccount = await toMetaMaskSmartAccount({ - client: publicClient, - implementation: Implementation.Hybrid, - deployParams: [account.address, [], [], []], - deploySalt: "0x", - signatory: { account }, -}); -``` - - - - -## Create an open redelegation - -An *open redelegation* is a [redelegation](#create-a-redelegation) that doesn't specify a delegate. -This means that any account can redeem the redelegation. -As with [open root delegations](#create-an-open-root-delegation), you must create open redelegations carefully, -to ensure that they are not misused. -Create an open redelegation as follows: - - - - -```typescript -import { - createDelegation, - createOpenDelegation, - getDelegationHashOffchain - } from "@metamask/delegation-toolkit"; -import { delegatorSmartAccount } from "./config.ts"; - -// The address is used as the root delegator. While creating the redelegation, -// the root delegate address will be the delegator address. -const delegate = "0x2FcB88EC2359fA635566E66415D31dD381CF5585"; - -const delegation = createDelegation({ - to: delegate, - from: delegatorSmartAccount.address, - caveats: [] // Empty caveats array - we recommend adding appropriate restrictions. -}); - -// The authority is the (typed data) hash of the delegation from which the authority is derived. -const parentDelegationHash = getDelegationHashOffchain(delegation); - -const leafDelegation = createOpenDelegation({ - from: delegate, - // You can also choose to pass the parent delegation object, and let the function - // handle the rest. - parentDelegation: parentDelegationHash, - caveats: [] // Empty caveats array - we recommend adding appropriate restrictions. -}); -``` - - - - - -```typescript -import { createPublicClient, http } from "viem"; -import { sepolia as chain } from "viem/chains"; -import { - Implementation, - toMetaMaskSmartAccount, -} from "@metamask/delegation-toolkit"; - -const publicClient = createPublicClient({ - chain, - transport: http() -}); - -const privateKey = generatePrivateKey(); -const account = privateKeyToAccount(privateKey); - -export const delegatorSmartAccount = await toMetaMaskSmartAccount({ - client: publicClient, - implementation: Implementation.Hybrid, - deployParams: [account.address, [], [], []], - deploySalt: "0x", - signatory: { account }, -}); -``` - - - - -## Sign a delegation - -A delegation must be signed by the delegator to be valid for redemption. The `MetaMaskSmartAccount` supports signing the delegation using [EIP-712 Typed Data](https://eips.ethereum.org/EIPS/eip-712) via the `signDelegation` method. -Sign a delegation as follows: - - - - -```typescript -import { createDelegation } from "@metamask/delegation-toolkit"; -import { delegatorSmartAccount } from "./config.ts"; - -// The address to which the delegation is granted. It can be an EOA address, or -// smart account address. -const delegate = "0x2FcB88EC2359fA635566E66415D31dD381CF5585"; - -const delegation = createDelegation({ - to: delegate, - from: delegatorSmartAccount.address, - caveats: [] // Empty caveats array - we recommend adding appropriate restrictions. -}); - -const signature = await delegatorSmartAccount.signDelegation({ delegation }); - -const signedDelegation = { - ...delegation, - signature -}; -``` - - - - - -```typescript -import { createPublicClient, http } from "viem"; -import { sepolia as chain } from "viem/chains"; -import { - Implementation, - toMetaMaskSmartAccount, -} from "@metamask/delegation-toolkit"; - -const publicClient = createPublicClient({ - chain, - transport: http() -}); - -const privateKey = generatePrivateKey(); -const account = privateKeyToAccount(privateKey); - -export const delegatorSmartAccount = await toMetaMaskSmartAccount({ - client: publicClient, - implementation: Implementation.Hybrid, - deployParams: [account.address, [], [], []], - deploySalt: "0x", - signatory: { account }, -}); - -``` - - - \ No newline at end of file diff --git a/delegation-toolkit/guides/delegation/_category_.json b/delegation-toolkit/guides/delegation/_category_.json new file mode 100644 index 00000000000..b304d543be5 --- /dev/null +++ b/delegation-toolkit/guides/delegation/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Delegation", + "position": 3 +} \ No newline at end of file diff --git a/delegation-toolkit/guides/create-delegation/create-custom-caveat-enforcer.md b/delegation-toolkit/guides/delegation/create-custom-caveat-enforcer.md similarity index 99% rename from delegation-toolkit/guides/create-delegation/create-custom-caveat-enforcer.md rename to delegation-toolkit/guides/delegation/create-custom-caveat-enforcer.md index 4dcefc0711b..46346d337a7 100644 --- a/delegation-toolkit/guides/create-delegation/create-custom-caveat-enforcer.md +++ b/delegation-toolkit/guides/delegation/create-custom-caveat-enforcer.md @@ -1,6 +1,6 @@ --- description: Learn how to create, deploy, and apply a custom caveat enforcer -sidebar_position: 2 +sidebar_position: 3 --- import Tabs from "@theme/Tabs"; diff --git a/delegation-toolkit/guides/delegation/execute-on-users-behalf.md b/delegation-toolkit/guides/delegation/execute-on-users-behalf.md new file mode 100644 index 00000000000..8aa84e4375b --- /dev/null +++ b/delegation-toolkit/guides/delegation/execute-on-users-behalf.md @@ -0,0 +1,232 @@ +--- +description: Use delegations to perform executions on a user's behalf. +sidebar_position: 1 +sidebar_label: Execute on a user's behalf +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +# Perform executions on a user's behalf + +[Delegation](../../concepts/delegation/index.md) is the ability for a [MetaMask smart account](../../concepts/smart-accounts.md) to grant permission to another account to perform executions on its behalf. + +This guide demonstrates how to create a *delegator account* (the account that grants the permission) and *delegate account* (the account that receives the permission), and complete the delegation lifecycle (create, sign, and redeem a delegation). + +This guide will refer to the delegator account as "Alice," who grants permission to "Bob," the delegate account, to perform executions on her behalf. + +## Prerequisites + +[Install and set up the Delegation Toolkit.](../../get-started/install.md) + +## Steps + +### 1. Set up a Public Client + +Set up a [Viem Public Client](https://viem.sh/docs/clients/public) using Viem's `createPublicClient` function. This client will let the delegator account query the signer's account state and interact with smart contracts. + +```typescript +import { createPublicClient, http } from "viem" +import { sepolia as chain } from "viem/chains" + +const publicClient = createPublicClient({ + chain, + transport: http(), +}) +``` + +### 2. Set up a Bundler Client + +Set up a [Viem Bundler Client](https://viem.sh/account-abstraction/clients/bundler) using Viem's `createBundlerClient` function. This lets you use the bundler service to estimate gas for user operations and submit transactions to the network. + +```typescript +import { createBundlerClient } from "viem/account-abstraction" + +const bundlerClient = createBundlerClient({ + client: publicClient, + transport: http("https://your-bundler-rpc.com"), +}) +``` + +### 3. Create a delegator account + +Create an account to represent Alice, the delegator who will create a delegation. +The delegator must be a MetaMask smart account; use the toolkit's [`toMetaMaskSmartAccount`](../../reference/api/smart-account.md#tometamasksmartaccount) method to create the delegator account. + +This example configures a Hybrid smart account, +which is a flexible smart account implementation that supports both an externally owned account (EOA) owner and any number of P256 (passkey) signers: + +```typescript +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 }, +}) +``` + +:::note +See [how to configure other smart account types](../smart-accounts/create-smart-account.md). +::: + +### 4. Create a delegate account + +Create an account to represent Bob, the delegate who will receive the delegation. The delegate can be a smart account or an externally owned account (EOA): + + + + +```typescript +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, // Hybrid smart account + deployParams: [delegateAccount.address, [], [], []], + deploySalt: "0x", + signatory: { account: delegateAccount }, +}) +``` + + + + +```typescript +import { privateKeyToAccount } from "viem/accounts"; +import { sepolia as chain } from "viem/chains"; +import { createWalletClient, http } from "viem"; + +const delegateAccount = privateKeyToAccount("0x..."); + +export const delegateWalletClient = createWalletClient({ + account: delegateAccount, + chain, + transport: http(), +}) +``` + + + + +### 5. Create a delegation + +Create a [root delegation](../../concepts/delegation/index.md#delegation-types) from Alice to Bob. +A root delegation is a delegation that doesn't derive its authority from another delegation. +Alice is delegating her own authority away, as opposed to *redelegating* permissions she received from a previous delegation. + +Use the toolkit's [`createDelegation`](../../reference/api/delegation.md#createdelegation) method to create a root delegation. +This example passes an empty `caveats` array, which means Bob can perform any action on Alice's behalf. We recommend [restricting the delegation](restrict-delegation.md) by adding caveat enforcers. +For example, Alice can delegate the ability to spend her USDC to Bob, limiting the amount to 100 USDC. + +:::warning Important + +Before creating a delegation, ensure that the delegator account (in this example, Alice's account) has been deployed. If the account is not deployed, redeeming the delegation will fail. + +::: + +```typescript +import { createDelegation } from "@metamask/delegation-toolkit" + +const delegation = createDelegation({ + to: delegateSmartAccount.address, // This example uses a delegate smart account + from: delegatorSmartAccount.address, + caveats: [], // Empty caveats array - we recommend adding appropriate restrictions. +}) +``` + +### 6. Sign the delegation + +Sign the delegation with Alice's account, using the [`signDelegation`](../../reference/api/smart-account.md#signdelegation) method from `MetaMaskSmartAccount`. Alternatively, you can use the toolkit's [`signDelegation`](../../reference/api/delegation.md#signdelegation) utility method. Bob will later use the signed delegation to perform actions on Alice's behalf. + +```typescript +const signature = await delegatorSmartAccount.signDelegation({ + delegation, +}) + +const signedDelegation = { + ...delegation, + signature, +} +``` + +### 7. Redeem the delegation + +Bob can now redeem the delegation. The redeem transaction is sent to the `DelegationManager` contract, which validates the delegation and executes actions on Alice's behalf. +To prepare the calldata for the redeem transaction, use the [`redeemDelegations`](../../reference/api/delegation.md#redeemdelegations) method from `DelegationManager`. + +Bob can redeem the delegation by submitting a user operation if his account is a smart account, or a regular transaction if his account is an EOA: + + + + +```typescript +import { createExecution } from "@metamask/delegation-toolkit" +import { DelegationManager } from "@metamask/delegation-toolkit/contracts" +import { SINGLE_DEFAULT_MODE } from "@metamask/delegation-toolkit/utils" +import { zeroAddress } from "viem" + +const delegations = [signedDelegation] + +const executions = createExecution({ target: zeroAddress }) + +const redeemDelegationCalldata = DelegationManager.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, +}) +``` + + + + +```typescript +import { createExecution, getDeleGatorEnvironment } from "@metamask/delegation-toolkit" +import { DelegationManager } from "@metamask/delegation-toolkit/contracts" +import { SINGLE_DEFAULT_MODE } from "@metamask/delegation-toolkit/utils" +import { zeroAddress } from "viem" + +const delegations = [signedDelegation] + +const executions = createExecution({ target: zeroAddress }) + +const redeemDelegationCalldata = DelegationManager.encode.redeemDelegations({ + delegations: [delegations], + modes: [SINGLE_DEFAULT_MODE], + executions: [executions] +}); + +const transactionHash = await delegateWalletClient.sendTransaction({ + to: getDeleGatorEnvironment(chain.id).DelegationManager, + data: redeemDelegationCalldata, + chain, +}) +``` + + + + +:::note +`SINGLE_DEFAULT_MODE` is the default execution mode. +Learn about the different [execution modes](../../concepts/delegation/index.md#execution-modes). +::: diff --git a/delegation-toolkit/guides/create-delegation/restrict-delegation.md b/delegation-toolkit/guides/delegation/restrict-delegation.md similarity index 97% rename from delegation-toolkit/guides/create-delegation/restrict-delegation.md rename to delegation-toolkit/guides/delegation/restrict-delegation.md index ba82bb6d119..fe6c5838b01 100644 --- a/delegation-toolkit/guides/create-delegation/restrict-delegation.md +++ b/delegation-toolkit/guides/delegation/restrict-delegation.md @@ -1,6 +1,6 @@ --- description: Learn how to restrict a delegation using caveat enforcers, and the available caveat types. -sidebar_position: 1 +sidebar_position: 2 toc_max_heading_level: 3 --- @@ -9,7 +9,7 @@ import TabItem from "@theme/TabItem"; # Restrict a delegation -Use [caveat enforcers](../../concepts/caveat-enforcers.md) to apply specific rules and restrictions +Use [caveat enforcers](../../concepts/delegation/caveat-enforcers.md) to apply specific rules and restrictions to a delegation, ensuring that delegated executions are only performed under predefined circumstances. A delegation has a `caveats` property, which is an array of `Caveat` objects. diff --git a/delegation-toolkit/guides/redeem-delegation.md b/delegation-toolkit/guides/redeem-delegation.md deleted file mode 100644 index 58f6a81e1f0..00000000000 --- a/delegation-toolkit/guides/redeem-delegation.md +++ /dev/null @@ -1,352 +0,0 @@ ---- -description: Learn how to redeem a delegation with a smart contract account (SCA) or an externally owned account (EOA). -sidebar_position: 7 -toc_max_heading_level: 3 ---- - -import Tabs from "@theme/Tabs"; -import TabItem from "@theme/TabItem"; - -# Redeem a delegation - -A delegate can redeem a delegation by submitting either a user operation or a regular transaction, -depending on whether the delegate is a MetaMask smart account or an externally owned account (EOA). - -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`](../reference/api/delegation.md#redeemdelegation) utility function. -The function supports batch redemption, allowing multiple delegations to be processed within a single transaction. - -## Prerequisites - -- [Install and set up the Delegation Toolkit.](../get-started/install.md) -- [Configure the Delegation Toolkit.](configure.md) -- [Create a delegator smart account.](smart-accounts/create-smart-account.md) -- [Create a delegation.](create-delegation/index.md) - -## Redeem a delegation - -Redeem a delegation with a [MetaMask smart account](#redeem-with-a-metamask-smart-account) or an [externally owned account (EOA)](#redeem-with-an-eoa). - -### Redeem with a MetaMask smart account - -The following example demonstrates how to submit a user operation to redeem a delegation. -It assumes you have a delegation signed by the delegator, and that the delegate is a MetaMask smart account. - - - - -```typescript -import { ExecutionStruct } from "@metamask/delegation-toolkit"; -import { DelegationManager } from "@metamask/delegation-toolkit/contracts"; -import { SINGLE_DEFAULT_MODE } from "@metamask/delegation-toolkit/utils"; -import { bundlerClient, pimlicoClient } from "./client.ts"; -import { delegateSmartAccount } from "./account.ts"; - -const delegations: Delegation[] = [ signedDelegation ]; - -// SINGLE_DEFAULT_MODE is the default execution mode. -const mode: ExecutionMode = SINGLE_DEFAULT_MODE; - -// For SINGLE execution modes, the executions array must be length 1. -const executions: ExecutionStruct[] = [{ - target: zeroAddress, - value: 0n, - callData: "0x" -}]; - -const redeemDelegationCalldata = DelegationManager.encode.redeemDelegations({ - delegations: [ delegations ], - modes: [ mode ], - executions: [ executions ] -}); - -const { fast: fee } = await pimlicoClient.getUserOperationGasPrice(); - -const userOperationHash = await bundlerClient.sendUserOperation({ - account: delegateSmartAccount, - calls: [ - { - to: delegateSmartAccount.address, - data: redeemDelegationCalldata - } - ], - ...fee -}); -``` - - - - - -```typescript -import { - Implementation, - toMetaMaskSmartAccount, -} from "@metamask/delegation-toolkit"; -import { privateKeyToAccount } from "viem/accounts"; -import { publicClient } from "./client.ts" - -const delegateAccount = privateKeyToAccount("0x..."); - -export const delegateSmartAccount = await toMetaMaskSmartAccount({ - client: publicClient, - implementation: Implementation.Hybrid, - deployParams: [delegateAccount.address, [], [], []], - deploySalt: "0x", - signatory: { account: delegateAccount }, -}); -``` - - - - - -```typescript -import { createPublicClient, http } from "viem"; -import { sepolia as chain } from "viem/chains"; -import { createBundlerClient } from "viem/account-abstraction"; -import { createPimlicoClient } from "permissionless/clients/pimlico"; - -// You can get the API Key from the Pimlico dashboard. -const pimlicoUrl = "https://api.pimlico.io/v2/11155111/rpc"; - -export const publicClient = createPublicClient({ - chain, - transport: http(), -}); - -export const bundlerClient = createBundlerClient({ - client: publicClient, - transport: http(pimlicoUrl), -}); - -export const pimlicoClient = createPimlicoClient({ - transport: http(pimlicoUrl), -}); -``` - - - - -### Redeem with an EOA - -The following example demonstrates how to submit a transaction to redeem a delegation. It assumes you have a delegation signed by the delegator, and that the delegate is an EOA. - - - - -```typescript -import { - ExecutionStruct, - ExecutionMode - Delegation, - getDeleGatorEnvironment, -} from "@metamask/delegation-toolkit"; -import { DelegationManager } from "@metamask/delegation-toolkit/contracts"; -import { SINGLE_DEFAULT_MODE } from "@metamask/delegation-toolkit/utils"; -import { sepolia as chain } from "viem/chains"; -import { delegateWalletClient } from "./account.ts"; - -const delegations: Delegation[] = [ signedDelegation ]; - -// SINGLE_DEFAULT_MODE is the default execution mode. -const mode: ExecutionMode = SINGLE_DEFAULT_MODE; - -// For SINGLE execution modes, the executions array must be length 1. -// Modify the executions to fit your use case. -const executions: ExecutionStruct[] = [{ - target: zeroAddress, - value: 0n, - callData: "0x" -}]; - -const redeemDelegationCalldata = DelegationManager.encode.redeemDelegations({ - delegations: [ delegations ], - modes: [ mode ], - executions: [ executions ] -}); - -const transactionHash = await walletClient.sendTransaction({ - to: getDeleGatorEnvironment(chain.id).DelegationManager, - data: redeemDelegationCalldata, - chain, -}); -``` - - - - - -```typescript -import { privateKeyToAccount } from "viem/accounts"; -import { sepolia as chain } from "viem/chains"; -import { createWalletClient, http } from "viem"; - -const delegateAccount = privateKeyToAccount("0x..."); - -export const delegateWalletClient = createWalletClient({ - account: delegateAccount, - chain, - transport: http(), -}) -``` - - - - -## Redeem multiple delegations - -You can redeem multiple delegations in a single user operation, each delegation independent of the others. -Each element in the `delegationsArray` must have a corresponding element in the `executionsArray` and `modes`. - -The following example assumes you already have multiple signed delegations and that the delegate is a MetaMask smart account. -The preparation of the calldata is the same when [using an EOA as the delegate](#redeem-with-an-eoa); -the primary difference is that an EOA submits a regular transaction instead of a user operation. - - - - -```typescript -import { - ExecutionStruct, - Delegation, - ExecutionMode, -} from "@metamask/delegation-toolkit"; -import { DelegationManager } from "@metamask/delegation-toolkit/contracts"; -import { SINGLE_DEFAULT_MODE } from "@metamask/delegation-toolkit/utils"; -import { bundlerClient, pimlicoClient } from "./client.ts"; -import { delegateSmartAccount } from "./account.ts"; - -const delegationsArray: Delegation[][] = [ - [ signedDelegation1 ] - [ signedDelegation2 ] - [ signedDelegation3 ] -]; - -const modes: ExecutionMode = [ - SINGLE_DEFAULT_MODE, - SINGLE_DEFAULT_MODE, - SINGLE_DEFAULT_MODE -]; - -const execution: ExecutionStruct[] = [{ - target: zeroAddress, - value: 0n, - callData: "0x" -}]; - -// Modify the executions to fit your use case. For simplicity, we've -// included a basic example. The execution array can contain -// multiple different executions. -const executionsArray: ExecutionStruct:[][] = [ - execution, - execution, - execution -]; - -const redeemDelegationCalldata = DelegationManager.encode.redeemDelegations({ - delegations: [ delegations ], - modes: [ mode ], - executions: [ executions ] -}); - -const { fast: fee } = await pimlicoClient.getUserOperationGasPrice(); - -const userOperationHash = await bundlerClient.sendUserOperation({ - account: delegateSmartAccount, - calls: [ - { - to: delegateSmartAccount.address, - data: redeemDelegationCalldata - } - ], - ...fee -}); -``` - - - - - -```typescript -import { - Implementation, - toMetaMaskSmartAccount, -} from "@metamask/delegation-toolkit"; -import { privateKeyToAccount } from "viem/accounts"; -import { publicClient } from "./client.ts"; - -const delegateAccount = privateKeyToAccount("0x..."); - -export const delegateSmartAccount = await toMetaMaskSmartAccount({ - client: publicClient, - implementation: Implementation.Hybrid, - deployParams: [delegateAccount.address, [], [], []], - deploySalt: "0x", - signatory: { account: delegateAccount }, -}); -``` - - - - - -```typescript -import { createPublicClient, http } from "viem"; -import { sepolia as chain } from "viem/chains"; -import { createBundlerClient } from "viem/account-abstraction"; -import { createPimlicoClient } from "permissionless/clients/pimlico"; - -// You can get the API Key from the Pimlico dashboard. -const pimlicoUrl = "https://api.pimlico.io/v2/11155111/rpc"; - -export const publicClient = createPublicClient({ - chain, - transport: http(), -}); - -export const bundlerClient = createBundlerClient({ - client: publicClient, - transport: http(pimlicoUrl), -}); - -export const pimlicoClient = createPimlicoClient({ - transport: http(pimlicoUrl), -}); -``` - - - - -## Execution modes - -The Delegation Toolkit supports several execution modes based on [ERC-7579](https://erc7579.com/). -See the [ERC implementation](https://github.com/erc7579/erc7579-implementation/blob/main/src/lib/ModeLib.sol) for more details about the execution modes. - -The supported execution modes are `SINGLE_DEFAULT_MODE`, `SINGLE_TRY_MODE`, `BATCH_DEFAULT_MODE`, and `BATCH_TRY_MODE`. - -### `SINGLE` execution modes - -In `SINGLE` execution modes, only a single delegation chain and a single execution can be provided. This mode processes delegations sequentially: - -1. For each delegation in the chain, all caveats' `before` hooks are called. -2. The single redeemed action is executed. -3. For each delegation in the chain, all caveats' `after` hooks are called. - -### `BATCH` execution modes - -In `BATCH` execution modes, multiple delegation chains and multiple executions can be provided. This mode executes delegations in an interleaved way: - -1. For each chain in the batch, and each delegation in the chain, all caveats' `before` hooks are called. -2. Each redeemed action is executed. -3. For each chain in the batch, and each delegation in the chain, all caveats' `after` hooks are called. - -`BATCH` mode allows for powerful use cases, but the Delegation Framework currently does not include any `BATCH` compatible caveat enforcers. - -### `DEFAULT` modes - -In `DEFAULT` modes, if a revert occurs during redemption, the entire user operation reverts at that point. - -### `TRY` modes - -In `TRY` modes, if a revert occurs during redemption, execution of the user operation continues. diff --git a/delegation-toolkit/guides/smart-accounts/create-smart-account.md b/delegation-toolkit/guides/smart-accounts/create-smart-account.md index a2b565a64d0..05451922141 100644 --- a/delegation-toolkit/guides/smart-accounts/create-smart-account.md +++ b/delegation-toolkit/guides/smart-accounts/create-smart-account.md @@ -289,7 +289,7 @@ A [Stateless 7702 smart account](../../concepts/smart-accounts.md#stateless-7702 functionality as defined by [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702). :::note -This implementation does not handle the upgrade process; see the [EIP-7702 quickstart](../../get-started/eip7702-quickstart.md) to learn how to upgrade. +This implementation does not handle the upgrade process; see the [EIP-7702 quickstart](../../get-started/smart-account-quickstart/eip7702.md) to learn how to upgrade. ::: You can create a Stateless 7702 smart account with the following types of signatories. @@ -415,5 +415,5 @@ With a MetaMask smart account, you can perform the following functions: - In conjunction with [Viem Account Abstraction clients](../configure.md), deploy the smart account and [send user operations](send-user-operation.md). -- [Create delegations](../create-delegation/index.md) that can be used to grant specific rights and permissions to other accounts. +- [Create delegations](../delegation/execute-on-users-behalf.md) that can be used to grant specific rights and permissions to other accounts. Smart accounts that create delegations are called *delegator accounts*. diff --git a/delegation-toolkit/index.md b/delegation-toolkit/index.md index 943740f8b11..fbd2bc0c874 100644 --- a/delegation-toolkit/index.md +++ b/delegation-toolkit/index.md @@ -12,8 +12,8 @@ import CardList from "@site/src/components/CardList" The MetaMask Delegation Toolkit is a [Viem](https://viem.sh/)-based collection of tools for embedding [MetaMask Smart Accounts](concepts/smart-accounts.md) into dapps. Smart accounts support programmable account behavior and advanced features like delegated permissions, multi-signature approvals, and gas abstraction. -[Delegations](concepts/delegation.md) are a core feature of MetaMask Smart Accounts, enabling secure, rule-based permission sharing. -These delegations are powered by the toolkit's Delegation Framework, which defines how +[Delegation](concepts/delegation/index.md) is a core feature of MetaMask Smart Accounts, enabling secure, rule-based permission sharing. +Delegation is powered by the toolkit's Delegation Framework, which defines how permissions are created, shared, and enforced. ## Why use the toolkit? diff --git a/delegation-toolkit/reference/api/delegation.md b/delegation-toolkit/reference/api/delegation.md index 1cff1034af8..705a97afb8c 100644 --- a/delegation-toolkit/reference/api/delegation.md +++ b/delegation-toolkit/reference/api/delegation.md @@ -10,7 +10,7 @@ import TabItem from "@theme/TabItem"; # Delegation API reference -The following API methods are related to creating and managing [delegations](../../concepts/delegation.md). +The following API methods are related to creating and managing [delegations](../../concepts/delegation/index.md). ## `createCaveatBuilder` @@ -358,16 +358,17 @@ export const environment: DeleGatorEnvironment = { -## `redeemDelegation` +## `redeemDelegations` Encodes calldata for redeeming delegations. +This method supports batch redemption, allowing multiple delegations to be processed within a single transaction. ### Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `delegations` | `Delegation[][]` | Yes | A nested collection representing chains of delegations. Each inner collection contains a chain of delegations to be redeemed. | -| `modes` | `ExecutionMode[]` | Yes | A collection specifying the execution mode for each corresponding delegation chain. | +| `modes` | `ExecutionMode[]` | Yes | A collection specifying the [execution mode](../../concepts/delegation/index.md#execution-modes) for each corresponding delegation chain. Supported execution modes are `SINGLE_DEFAULT_MODE`, `SINGLE_TRY_MODE`, `BATCH_DEFAULT_MODE`, and `BATCH_TRY_MODE`. | | `executions` | `ExecutionStruct[][]` | Yes | A nested collection where each inner collection contains a list of `ExecutionStruct` objects associated with a specific delegation chain. | ### Example diff --git a/delegation-toolkit/reference/caveats.md b/delegation-toolkit/reference/caveats.md index c1eef812e94..6e2c63d8805 100644 --- a/delegation-toolkit/reference/caveats.md +++ b/delegation-toolkit/reference/caveats.md @@ -6,7 +6,7 @@ toc_max_heading_level: 2 # Caveats -When [restricting a delegation](../guides/create-delegation/restrict-delegation.md), you can specify the following caveat types in the `CaveatBuilder`. +When [restricting a delegation](../guides/delegation/restrict-delegation.md), you can specify the following caveat types in the `CaveatBuilder`. ## `allowedCalldata` diff --git a/vercel.json b/vercel.json index 62e57d0b0a5..8e3fc2f7a8b 100644 --- a/vercel.json +++ b/vercel.json @@ -732,15 +732,15 @@ }, { "source": "/delegation-toolkit/development/how-to/create-delegation/", - "destination": "/delegation-toolkit/development/guides/create-delegation/" + "destination": "/delegation-toolkit/development/guides/delegation/execute-on-users-behalf/" }, { "source": "/delegation-toolkit/development/how-to/create-delegation/:path*/", - "destination": "/delegation-toolkit/development/guides/create-delegation/:path*/" + "destination": "/delegation-toolkit/development/guides/delegation/:path*/" }, { "source": "/delegation-toolkit/development/how-to/redeem-delegation/", - "destination": "/delegation-toolkit/development/guides/redeem-delegation/" + "destination": "/delegation-toolkit/development/guides/delegation/execute-on-users-behalf/" }, { "source": "/delegation-toolkit/development/get-started/quickstart/", @@ -754,6 +754,10 @@ "source": "/delegation-toolkit/development/get-started/cli-quickstart/", "destination": "/delegation-toolkit/development/get-started/use-the-cli/" }, + { + "source": "/delegation-toolkit/development/concepts/caveat-enforcers/", + "destination": "/delegation-toolkit/development/concepts/delegation/caveat-enforcers/" + }, { "source": "/developer-tools/faucet/sepolia/", "destination": "/developer-tools/faucet/" From cdb9eb8e6f78488e20d88f89acffd3e7f8e95fc4 Mon Sep 17 00:00:00 2001 From: Alexandra Tran Date: Mon, 25 Aug 2025 13:46:11 -0700 Subject: [PATCH 2/4] address some feedback --- .../concepts/delegation/index.md | 40 ++++++++++++------- .../delegation/execute-on-users-behalf.md | 15 +++---- 2 files changed, 34 insertions(+), 21 deletions(-) diff --git a/delegation-toolkit/concepts/delegation/index.md b/delegation-toolkit/concepts/delegation/index.md index 9871e1fb407..af8f8345a8f 100644 --- a/delegation-toolkit/concepts/delegation/index.md +++ b/delegation-toolkit/concepts/delegation/index.md @@ -10,7 +10,7 @@ import TabItem from "@theme/TabItem"; # Delegation *Delegation* is the ability for a [MetaMask smart account](../smart-accounts.md) to grant permission to another smart account -or externally owned account (EOA) to perform specific executions on its behalf, under defined rules and restrictions. +or externally owned account (EOA) to perform specific executions on its behalf. The account that grants the permission is called the *delegator account*, while the account that receives the permission is called the *delegate account*. @@ -22,36 +22,46 @@ For example: Alice delegates the ability to spend her USDC to Bob, limiting the The delegation lifecycle is as follows: -1. **Delegation creation** - A delegation is initialized, and the delegator account signs it. +1. **Create a delegation** - The delegator account creates and signs a delegation. -2. **Caveat enforcement** - The caveats applied to the delegation specify conditions under which +2. **Apply caveats** - The caveats applied to the delegation specify conditions under which the delegation can be redeemed. -3. **Delegation storage** - The delegation can be stored, enabling retrieval for future redemption. +3. **Store the delegation** - A dapp can store the delegation, enabling retrieval for future redemption. -4. **Delegation redemption** - The delegate (the account being granted the permission) redeems the delegation via the Delegation Manager, +4. **Redeem the delegation** - The delegate (the account being granted the permission) redeems the delegation via the Delegation Manager, which verifies that the delegated authority is valid in order to perform the execution. See [how to perform executions on a user's behalf](../../guides/delegation/execute-on-users-behalf.md) to get started with the delegation lifecycle. ## Delegation types -There are multiple types of delegations you can create: +You can create the following delegation types: + +- **Root delegation** - A root delegation is a delegation that doesn't derive its authority from another delegation. + It is when a delegator delegates its own authority away. + For example, Alice delegates the ability to spend her USDC to Bob, limiting the amount to 100 USDC. -- A **root delegation** is a delegation that doesn't derive its authority from another delegation. - It is when a delegator delegates its own authority away, as opposed to a redelegation. Use [`createDelegation`](../../reference/api/delegation.md#createdelegation) to create a root delegation. -- An **open root delegation** is a root delegation that doesn't specify a delegate. +- **Open root delegation** - An open root delegation is a root delegation that doesn't specify a delegate. This means that any account can redeem the delegation. + For example, Alice delegates the ability to spend 100 of her USDC to anyone. + You must create open root delegations carefully, to ensure that they are not misused. Use [`createOpenDelegation`](../../reference/api/delegation.md#createopendelegation) to create a root delegation. -- A delegate can **redelegate** permissions that have been granted to them, creating a chain of delegations across trusted parties. +- **Redelegation** - A delegate can redelegate permissions that have been granted to them, creating a chain of delegations across trusted parties. + For example, Alice delegates the ability to spend 100 of her USDC to Bob. + Bob redelegates the ability to spend 50 of Alice's 100 USDC to Carol. + Use [`createDelegation`](../../reference/api/delegation.md#createdelegation) to create a redelegation. -- An **open redelegation** is a redelegation that doesn't specify a delegate. +- **Open redelegation** - An open redelegation is a redelegation that doesn't specify a delegate. This means that any account can redeem the redelegation. + For example, Alice delegates the ability to spend 100 of her USDC to Bob. + Bob redelegates the ability to spend 50 of Alice's 100 USDC to anyone. + As with open root delegations, you must create open redelegations carefully, to ensure that they are not misused. Use [`createOpenDelegation`](../../reference/api/delegation.md#createopendelegation) to create an open redelegation. @@ -95,7 +105,7 @@ It consists of the following components: ## Delegation flow -This diagram illustrates how a delegation is created and subsequently redeemed on the Delegation Manager. +This diagram shows how a delegation is created and redeemed with the Delegation Manager. The Delegation Manager is responsible for validating the signature of the delegation and the caveat enforcers. If everything is correct, it allows a delegate to execute an action on behalf of the delegator. @@ -148,7 +158,8 @@ The Delegation Toolkit supports several execution modes based on [ERC-7579](http ### `SINGLE` execution modes -In `SINGLE` execution modes, only a single delegation chain and a single execution can be provided. This mode processes delegations sequentially: +In `SINGLE` modes, you can provide only one delegation chain and one execution. +Processing is sequential: 1. For each delegation in the chain, all caveats' `before` hooks are called. 2. The single redeemed action is executed. @@ -156,7 +167,8 @@ In `SINGLE` execution modes, only a single delegation chain and a single executi ### `BATCH` execution modes -In `BATCH` execution modes, multiple delegation chains and multiple executions can be provided. This mode executes delegations in an interleaved way: +In `BATCH` modes, you can provide multiple delegation chains and multiple executions. +Processing is interleaved: 1. For each chain in the batch, and each delegation in the chain, all caveats' `before` hooks are called. 2. Each redeemed action is executed. diff --git a/delegation-toolkit/guides/delegation/execute-on-users-behalf.md b/delegation-toolkit/guides/delegation/execute-on-users-behalf.md index 8aa84e4375b..9703b2b7481 100644 --- a/delegation-toolkit/guides/delegation/execute-on-users-behalf.md +++ b/delegation-toolkit/guides/delegation/execute-on-users-behalf.md @@ -11,9 +11,8 @@ import TabItem from "@theme/TabItem"; [Delegation](../../concepts/delegation/index.md) is the ability for a [MetaMask smart account](../../concepts/smart-accounts.md) to grant permission to another account to perform executions on its behalf. -This guide demonstrates how to create a *delegator account* (the account that grants the permission) and *delegate account* (the account that receives the permission), and complete the delegation lifecycle (create, sign, and redeem a delegation). - -This guide will refer to the delegator account as "Alice," who grants permission to "Bob," the delegate account, to perform executions on her behalf. +In this guide, you'll create a delegator account (Alice) and a delegate account (Bob), and grant Bob permission to perform executions on Alice's behalf. +You'll complete the delegation lifecycle (create, sign, and redeem a delegation). ## Prerequisites @@ -21,9 +20,10 @@ This guide will refer to the delegator account as "Alice," who grants permission ## Steps -### 1. Set up a Public Client +### 1. Create a Public Client -Set up a [Viem Public Client](https://viem.sh/docs/clients/public) using Viem's `createPublicClient` function. This client will let the delegator account query the signer's account state and interact with smart contracts. +Create a [Viem Public Client](https://viem.sh/docs/clients/public) using Viem's `createPublicClient` function. +Your dapp can use the Public Client to query the signer's account state and interact with smart contracts. ```typescript import { createPublicClient, http } from "viem" @@ -35,9 +35,10 @@ const publicClient = createPublicClient({ }) ``` -### 2. Set up a Bundler Client +### 2. Create a Bundler Client -Set up a [Viem Bundler Client](https://viem.sh/account-abstraction/clients/bundler) using Viem's `createBundlerClient` function. This lets you use the bundler service to estimate gas for user operations and submit transactions to the network. +Create a [Viem Bundler Client](https://viem.sh/account-abstraction/clients/bundler) using Viem's `createBundlerClient` function. +Your dapp can use the bundler service to estimate gas for user operations and submit transactions to the network. ```typescript import { createBundlerClient } from "viem/account-abstraction" From c3a362d870d30eca42639712c45ec479b04c9fd5 Mon Sep 17 00:00:00 2001 From: Alexandra Tran Date: Mon, 25 Aug 2025 22:12:58 -0700 Subject: [PATCH 3/4] minor edits --- .../guides/delegation/execute-on-users-behalf.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/delegation-toolkit/guides/delegation/execute-on-users-behalf.md b/delegation-toolkit/guides/delegation/execute-on-users-behalf.md index 9703b2b7481..2e7187f72c9 100644 --- a/delegation-toolkit/guides/delegation/execute-on-users-behalf.md +++ b/delegation-toolkit/guides/delegation/execute-on-users-behalf.md @@ -23,7 +23,7 @@ You'll complete the delegation lifecycle (create, sign, and redeem a delegation) ### 1. Create a Public Client Create a [Viem Public Client](https://viem.sh/docs/clients/public) using Viem's `createPublicClient` function. -Your dapp can use the Public Client to query the signer's account state and interact with smart contracts. +You will configure Alice's account (the delegator) and the Bundler Client with the Public Client, which you can use to query the signer's account state and interact with smart contracts. ```typescript import { createPublicClient, http } from "viem" @@ -38,7 +38,7 @@ const publicClient = createPublicClient({ ### 2. Create a Bundler Client Create a [Viem Bundler Client](https://viem.sh/account-abstraction/clients/bundler) using Viem's `createBundlerClient` function. -Your dapp can use the bundler service to estimate gas for user operations and submit transactions to the network. +You can use the bundler service to estimate gas for user operations and submit transactions to the network. ```typescript import { createBundlerClient } from "viem/account-abstraction" From b1f7c99033fb06033cbb77a4e40adf008542448e Mon Sep 17 00:00:00 2001 From: Alexandra Tran Date: Wed, 27 Aug 2025 14:26:22 -0700 Subject: [PATCH 4/4] edits to address reviewer comments --- .../concepts/delegation/index.md | 55 +++++++++---------- .../smart-account-quickstart/eip7702.md | 2 +- .../smart-account-quickstart/index.md | 2 +- ...md => execute-on-smart-accounts-behalf.md} | 20 +++---- .../smart-accounts/create-smart-account.md | 4 +- vercel.json | 4 +- 6 files changed, 39 insertions(+), 48 deletions(-) rename delegation-toolkit/guides/delegation/{execute-on-users-behalf.md => execute-on-smart-accounts-behalf.md} (90%) diff --git a/delegation-toolkit/concepts/delegation/index.md b/delegation-toolkit/concepts/delegation/index.md index af8f8345a8f..9f4ee8e4fdb 100644 --- a/delegation-toolkit/concepts/delegation/index.md +++ b/delegation-toolkit/concepts/delegation/index.md @@ -22,24 +22,22 @@ For example: Alice delegates the ability to spend her USDC to Bob, limiting the The delegation lifecycle is as follows: -1. **Create a delegation** - The delegator account creates and signs a delegation. - -2. **Apply caveats** - The caveats applied to the delegation specify conditions under which - the delegation can be redeemed. +1. **Create a delegation** - The delegator account creates a delegation, applying *caveats* which specify conditions under which the delegation can be redeemed. + The delegator signs the delegation. 3. **Store the delegation** - A dapp can store the delegation, enabling retrieval for future redemption. 4. **Redeem the delegation** - The delegate (the account being granted the permission) redeems the delegation via the Delegation Manager, which verifies that the delegated authority is valid in order to perform the execution. -See [how to perform executions on a user's behalf](../../guides/delegation/execute-on-users-behalf.md) to get started with the delegation lifecycle. +See [how to perform executions on a smart account's behalf](../../guides/delegation/execute-on-smart-accounts-behalf.md) to get started with the delegation lifecycle. ## Delegation types You can create the following delegation types: -- **Root delegation** - A root delegation is a delegation that doesn't derive its authority from another delegation. - It is when a delegator delegates its own authority away. +- **Root delegation** - A root delegation is when a delegator delegates their own authority away, as opposed to *redelegating* permissions they received from a previous delegation. + In a chain of delegations, the first delegation is the root delegation. For example, Alice delegates the ability to spend her USDC to Bob, limiting the amount to 100 USDC. Use [`createDelegation`](../../reference/api/delegation.md#createdelegation) to create a root delegation. @@ -49,7 +47,7 @@ You can create the following delegation types: For example, Alice delegates the ability to spend 100 of her USDC to anyone. You must create open root delegations carefully, to ensure that they are not misused. - Use [`createOpenDelegation`](../../reference/api/delegation.md#createopendelegation) to create a root delegation. + Use [`createOpenDelegation`](../../reference/api/delegation.md#createopendelegation) to create an open root delegation. - **Redelegation** - A delegate can redelegate permissions that have been granted to them, creating a chain of delegations across trusted parties. For example, Alice delegates the ability to spend 100 of her USDC to Bob. @@ -87,18 +85,18 @@ It consists of the following components: 1. Validates the input data by ensuring the lengths of `delegations`, `modes`, and `executions` match. - 2. Decodes and validates the delegation, checking that the caller is the delegate + 2. Decodes and validates the delegation, checking that the caller is the delegate and that there are no empty signatures. - 3. Verifies delegation signatures, ensuring validity using ECDSA (for EOAs) or + 3. Verifies delegation signatures, ensuring validity using ECDSA (for EOAs) or `isValidSignature` (for contracts). - 4. Validates the delegation chain's authority and ensures delegations are not disabled. - 5. Executes the `beforeHook` for each [caveat](caveat-enforcers.md) in the delegation, passing relevant data (`terms`, + 4. Validates the delegation chain's authority and ensures delegations are not disabled. + 5. Executes the `beforeHook` for each [caveat](caveat-enforcers.md) in the delegation, passing relevant data (`terms`, `arguments`, `mode`, `execution` `calldata`, and `delegationHash`) to the caveat enforcer. - 6. Calls `executeFromExecutor` to perform the delegation's execution, either by the delegator or + 6. Calls `executeFromExecutor` to perform the delegation's execution, either by the delegator or the caller for self-authorized executions. - 7. Executes the `afterHook` for each caveat, similar to the `beforeHook`, passing required data + 7. Executes the `afterHook` for each caveat, similar to the `beforeHook`, passing required data to enforce post-execution conditions. - 8. Emits `RedeemedDelegation` events for each delegation that was successfully redeemed. + 8. Emits `RedeemedDelegation` events for each delegation that was successfully redeemed. - **Caveat enforcers** - [Caveat enforcers](caveat-enforcers.md) manage rules and restrictions for delegations, providing fine-tuned control over delegated executions. @@ -154,32 +152,29 @@ sequenceDiagram ## Execution modes When redeeming a delegation using [`redeemDelegations`](../../reference/api/delegation.md#redeemdelegations), you must pass an execution mode for each delegation chain you pass to the method. -The Delegation Toolkit supports several execution modes based on [ERC-7579](https://erc7579.com/): `SINGLE_DEFAULT_MODE`, `SINGLE_TRY_MODE`, `BATCH_DEFAULT_MODE`, and `BATCH_TRY_MODE`. +The Delegation Toolkit supports the following execution modes, based on [ERC-7579](https://erc7579.com/): + +| Execution mode | Number of delegation chains passed to `redeemDelegations` | Processing method | Does user operation continue execution if redemption reverts? | +|--|--|--|--| +| `SINGLE_DEFAULT_MODE` | One | Sequential | No | +| `SINGLE_TRY_MODE` | One | Sequential | Yes | +| `BATCH_DEFAULT_MODE` | Multiple | Interleaved | No | +| `BATCH_TRY_MODE` | Multiple | Interleaved | Yes | -### `SINGLE` execution modes +### Sequential processing -In `SINGLE` modes, you can provide only one delegation chain and one execution. -Processing is sequential: +In `SINGLE` modes, processing is sequential: 1. For each delegation in the chain, all caveats' `before` hooks are called. 2. The single redeemed action is executed. 3. For each delegation in the chain, all caveats' `after` hooks are called. -### `BATCH` execution modes +### Interleaved processing -In `BATCH` modes, you can provide multiple delegation chains and multiple executions. -Processing is interleaved: +In `BATCH` modes, processing is interleaved: 1. For each chain in the batch, and each delegation in the chain, all caveats' `before` hooks are called. 2. Each redeemed action is executed. 3. For each chain in the batch, and each delegation in the chain, all caveats' `after` hooks are called. `BATCH` mode allows for powerful use cases, but the Delegation Framework currently does not include any `BATCH` compatible caveat enforcers. - -### `DEFAULT` modes - -In `DEFAULT` modes, if a revert occurs during redemption, the entire user operation reverts at that point. - -### `TRY` modes - -In `TRY` modes, if a revert occurs during redemption, execution of the user operation continues. diff --git a/delegation-toolkit/get-started/smart-account-quickstart/eip7702.md b/delegation-toolkit/get-started/smart-account-quickstart/eip7702.md index 1b92f38837b..41a90b2f518 100644 --- a/delegation-toolkit/get-started/smart-account-quickstart/eip7702.md +++ b/delegation-toolkit/get-started/smart-account-quickstart/eip7702.md @@ -156,6 +156,6 @@ const userOperationHash = await bundlerClient.sendUserOperation({ ## Next steps -- To grant specific permissions to other accounts from your smart account, [create a delegation](../../guides/delegation/execute-on-users-behalf.md). +- To grant specific permissions to other accounts from your smart account, [create a delegation](../../guides/delegation/execute-on-smart-accounts-behalf.md). - To quickly bootstrap a MetaMask Smart Accounts project, [use the CLI](../use-the-cli.md). - You can also [use MetaMask SDK to upgrade a MetaMask account to a smart account](/sdk/tutorials/upgrade-eoa-to-smart-account). diff --git a/delegation-toolkit/get-started/smart-account-quickstart/index.md b/delegation-toolkit/get-started/smart-account-quickstart/index.md index 91cb43434fe..931a8347364 100644 --- a/delegation-toolkit/get-started/smart-account-quickstart/index.md +++ b/delegation-toolkit/get-started/smart-account-quickstart/index.md @@ -94,7 +94,7 @@ const userOperationHash = await bundlerClient.sendUserOperation({ ## Next steps -- To grant specific permissions to other accounts from your smart account, [create a delegation](../../guides/delegation/execute-on-users-behalf.md). +- To grant specific permissions to other accounts from your smart account, [create a delegation](../../guides/delegation/execute-on-smart-accounts-behalf.md). - This quickstart example uses a Hybrid smart account. You can also [configure other smart account types](../../guides/smart-accounts/create-smart-account.md). - To upgrade an EOA to a smart account, see the [EIP-7702 quickstart](eip7702.md). diff --git a/delegation-toolkit/guides/delegation/execute-on-users-behalf.md b/delegation-toolkit/guides/delegation/execute-on-smart-accounts-behalf.md similarity index 90% rename from delegation-toolkit/guides/delegation/execute-on-users-behalf.md rename to delegation-toolkit/guides/delegation/execute-on-smart-accounts-behalf.md index 2e7187f72c9..39666994b62 100644 --- a/delegation-toolkit/guides/delegation/execute-on-users-behalf.md +++ b/delegation-toolkit/guides/delegation/execute-on-smart-accounts-behalf.md @@ -1,13 +1,13 @@ --- -description: Use delegations to perform executions on a user's behalf. +description: Use delegations to perform executions on a smart account's behalf. sidebar_position: 1 -sidebar_label: Execute on a user's behalf +sidebar_label: Execute on a smart account's behalf --- import Tabs from "@theme/Tabs"; import TabItem from "@theme/TabItem"; -# Perform executions on a user's behalf +# Perform executions on a smart account's behalf [Delegation](../../concepts/delegation/index.md) is the ability for a [MetaMask smart account](../../concepts/smart-accounts.md) to grant permission to another account to perform executions on its behalf. @@ -54,8 +54,8 @@ const bundlerClient = createBundlerClient({ Create an account to represent Alice, the delegator who will create a delegation. The delegator must be a MetaMask smart account; use the toolkit's [`toMetaMaskSmartAccount`](../../reference/api/smart-account.md#tometamasksmartaccount) method to create the delegator account. -This example configures a Hybrid smart account, -which is a flexible smart account implementation that supports both an externally owned account (EOA) owner and any number of P256 (passkey) signers: +A Hybrid smart account is a flexible smart account implementation that supports both an externally owned account (EOA) owner and any number of P256 (passkey) signers. +This examples configures a [Hybrid smart account with an Account signatory](../smart-accounts/create-smart-account.md#create-a-hybrid-smart-account-with-an-account-signatory): ```typescript import { Implementation, toMetaMaskSmartAccount } from "@metamask/delegation-toolkit" @@ -121,8 +121,7 @@ export const delegateWalletClient = createWalletClient({ ### 5. Create a delegation Create a [root delegation](../../concepts/delegation/index.md#delegation-types) from Alice to Bob. -A root delegation is a delegation that doesn't derive its authority from another delegation. -Alice is delegating her own authority away, as opposed to *redelegating* permissions she received from a previous delegation. +With a root delegation, Alice is delegating her own authority away, as opposed to *redelegating* permissions she received from a previous delegation. Use the toolkit's [`createDelegation`](../../reference/api/delegation.md#createdelegation) method to create a root delegation. This example passes an empty `caveats` array, which means Bob can perform any action on Alice's behalf. We recommend [restricting the delegation](restrict-delegation.md) by adding caveat enforcers. @@ -162,7 +161,9 @@ const signedDelegation = { ### 7. Redeem the delegation Bob can now redeem the delegation. The redeem transaction is sent to the `DelegationManager` contract, which validates the delegation and executes actions on Alice's behalf. + To prepare the calldata for the redeem transaction, use the [`redeemDelegations`](../../reference/api/delegation.md#redeemdelegations) method from `DelegationManager`. +Since Bob is redeeming a single delegation chain, use the [`SINGLE_DEFAULT_MODE`](../../concepts/delegation/index.md#execution-modes) execution mode. Bob can redeem the delegation by submitting a user operation if his account is a smart account, or a regular transaction if his account is an EOA: @@ -226,8 +227,3 @@ const transactionHash = await delegateWalletClient.sendTransaction({ - -:::note -`SINGLE_DEFAULT_MODE` is the default execution mode. -Learn about the different [execution modes](../../concepts/delegation/index.md#execution-modes). -::: diff --git a/delegation-toolkit/guides/smart-accounts/create-smart-account.md b/delegation-toolkit/guides/smart-accounts/create-smart-account.md index 05451922141..ec9b3fc5c88 100644 --- a/delegation-toolkit/guides/smart-accounts/create-smart-account.md +++ b/delegation-toolkit/guides/smart-accounts/create-smart-account.md @@ -23,7 +23,7 @@ You can create a Hybrid smart account with the following types of signatories. ### Create a Hybrid smart account with an Account signatory -Use [`toMetaMaskSmartAccount`](../../reference/api/smart-account.md#tometamasksmartaccount) and Viem's [`privateKeyToAccount`](https://viem.sh/docs/accounts/local/privateKeyToAccount) to create a Hybrid smart account with a signatory from a private key: +Use [`toMetaMaskSmartAccount`](../../reference/api/smart-account.md#tometamasksmartaccount), and Viem's [`privateKeyToAccount` and `generatePrivateKey`](https://viem.sh/docs/accounts/local/privateKeyToAccount), to create a Hybrid smart account with a signatory from a randomly generated private key: @@ -415,5 +415,5 @@ With a MetaMask smart account, you can perform the following functions: - In conjunction with [Viem Account Abstraction clients](../configure.md), deploy the smart account and [send user operations](send-user-operation.md). -- [Create delegations](../delegation/execute-on-users-behalf.md) that can be used to grant specific rights and permissions to other accounts. +- [Create delegations](../delegation/execute-on-smart-accounts-behalf.md) that can be used to grant specific rights and permissions to other accounts. Smart accounts that create delegations are called *delegator accounts*. diff --git a/vercel.json b/vercel.json index 8e3fc2f7a8b..04e124f10ab 100644 --- a/vercel.json +++ b/vercel.json @@ -732,7 +732,7 @@ }, { "source": "/delegation-toolkit/development/how-to/create-delegation/", - "destination": "/delegation-toolkit/development/guides/delegation/execute-on-users-behalf/" + "destination": "/delegation-toolkit/development/guides/delegation/execute-on-smart-accounts-behalf/" }, { "source": "/delegation-toolkit/development/how-to/create-delegation/:path*/", @@ -740,7 +740,7 @@ }, { "source": "/delegation-toolkit/development/how-to/redeem-delegation/", - "destination": "/delegation-toolkit/development/guides/delegation/execute-on-users-behalf/" + "destination": "/delegation-toolkit/development/guides/delegation/execute-on-smart-accounts-behalf/" }, { "source": "/delegation-toolkit/development/get-started/quickstart/",