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..9f4ee8e4fdb --- /dev/null +++ b/delegation-toolkit/concepts/delegation/index.md @@ -0,0 +1,180 @@ +--- +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. +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. **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 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 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. + +- **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 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. + 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. + +- **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. + +## 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 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. + +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 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 | + +### Sequential processing + +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. + +### Interleaved processing + +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. 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 ab153d1fa57..a57bf44e3d3 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..41a90b2f518 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-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 2d65660cf80..3cab237b061 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-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-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-smart-accounts-behalf.md b/delegation-toolkit/guides/delegation/execute-on-smart-accounts-behalf.md new file mode 100644 index 00000000000..39666994b62 --- /dev/null +++ b/delegation-toolkit/guides/delegation/execute-on-smart-accounts-behalf.md @@ -0,0 +1,229 @@ +--- +description: Use delegations to perform executions on a smart account's behalf. +sidebar_position: 1 +sidebar_label: Execute on a smart account's behalf +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +# 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. + +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 + +[Install and set up the Delegation Toolkit.](../../get-started/install.md) + +## Steps + +### 1. Create a Public Client + +Create a [Viem Public Client](https://viem.sh/docs/clients/public) using Viem's `createPublicClient` function. +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" +import { sepolia as chain } from "viem/chains" + +const publicClient = createPublicClient({ + chain, + transport: http(), +}) +``` + +### 2. Create a Bundler Client + +Create a [Viem Bundler Client](https://viem.sh/account-abstraction/clients/bundler) using Viem's `createBundlerClient` function. +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" + +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. + +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" +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. +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. +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`. +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: + + + + +```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, +}) +``` + + + 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 3989a08155f..db5573d4880 100644 --- a/delegation-toolkit/guides/smart-accounts/create-smart-account.md +++ b/delegation-toolkit/guides/smart-accounts/create-smart-account.md @@ -24,7 +24,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: @@ -290,7 +290,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. @@ -416,5 +416,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-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/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..04e124f10ab 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-smart-accounts-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-smart-accounts-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/"