Skip to main content

ERC-7710: Redeem delegations

Experimental

This experimental feature requires MetaMask Delegation Toolkit version 0.8.0 or later and may change in future releases.

ERC-7710 introduces a standard way for smart contract accounts (SCAs) to delegate capabilities to other SCAs or externally owned accounts (EOAs).

The MetaMask Delegation Toolkit provides two experimental functions, erc7710BundlerActions() and erc7710WalletActions(), that let a caller redeem delegations granted by MetaMask's permissions system.

Extract relevant data

Refer to ERC-7715: Request permissions for information on how to request user permissions. Once the permission has been granted, extract the relevant data from the response. For example:

// Response received from the ERC-7715 wallet_grantPermissions request.
const permissionsResponse = [{
chainId: "0xe715",
account: "0xD6f56C2B10b1e02D841E4a97c60Afe914E884DBd",
expiry: 1234567890,
permission: {
type: "native-token-stream",
data: {
amountPerSecond: "0x1",
maxAmount: "0x2",
initialAmount: undefined,
startTime: 2,
},
},
context: "0x1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d"
signer: {
type: "account",
data: {
account: "0x07bfc7230D5BD2544059816D88A895BB000Abe00"
}
},
signerMeta: {
delegationManager: "0xDC7e12b41E5e61BfCc7F56AAFB7B93288F61e841"
},
accountMetadata: [{
factory: "0x65E726b404149fE37F4b291c81Dc6eddd44763A7",
factoryData: "0x1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b"
}]
}];

const permissionsContext = permissionsResponse[0].context;
const delegationManager = permissionsResponse[0].signerMeta.delegationManager;
// accountMeta is only present when the smart contract account is not deployed.
const accountMetadata = permissionsResponse[0].accountMeta;

This data encodes the authority that lets the delegate redeem the permission.

Security considerations for accountMeta

When a user grants a permission, they can provide accountMeta which is an array of factory and factoryData values. These calls must be executed before redeeming the permission (this is handled for you in sendUserOperationWithDelegation).

Because each accountMeta is an arbitrary call specified by the granter, it is important that these are executed carefully. We recommend taking the following precautions:

  • Only grant permissions to session accounts - When requesting permissions, use an account that is only used for that single purpose, and does not contain tokens. This way, any accountMeta executed can't perform any damaging actions.

  • Only execute accountMeta against trusted factory addresses - Ensure that only accountMeta targeting a known factory address is executed. The bundler action sendUserOperationWithDelegation only executes accountMeta that targets the SimpleFactory address for the current Delegation Framework. If you redeem delegations in any other way, it is your responsibility to validate trusted factory addresses.

Redeem the permission

Redeem a delegation using one of two methods. Choose the method based on your account type:

  • If redeeming with an SCA, call sendUserOperationWithDelegation.
  • If redeeming with an EOA, call sendTransactionWithDelegation.

Redeem with an SCA

To redeem a delegation with a smart contract account, create a MetaMaskSmartAccount and a Viem Bundler Client.

After setting up your Bundler Client, you can extend its functionality with erc7710BundlerActions actions to support ERC-7710. Once extended, use sendUserOperationWithDelegation to redeem the permission.

import { sessionAccount, bundlerClient, publicClient } from "./config.ts";

// These properties must be extracted from the permission response.
const permissionsContext = permissionsResponse[0].context;
const delegationManager = permissionsResponse[0].signerMeta.delegationManager;
const accountMetadata = permissionsResponse[0].accountMeta;

// Calls without permissionsContext and delegationManager will be executed
// as a normal user operation.
const userOperationHash = await bundlerClient.sendUserOperationWithDelegation({
publicClient,
account: sessionAccount,
calls: [
{
to: sessionAccount.address,
data: "0x",
value: 1n,
permissionsContext,
delegationManager,
},
],
// Appropriate values must be used for fee-per-gas.
maxFeePerGas: 1n,
maxPriorityFeePerGas: 1n
accountMetadata,
});
note

sendUserOperationWithDelegation is similar to the sendUserOperation function, but does not accept callData directly.

Redeem with an EOA

To redeem a delegation with an EOA, create a Viem Wallet Client.

After creating your Wallet Client, you can extend its functionality with erc7710WalletActions actions to support ERC-7710. Once extended, use sendTransactionWithDelegation to redeem the permission.

import { walletClient, publicClient } from "./config.ts";

// These properties must be extracted from the permission response.
const permissionsContext = permissionsResponse[0].context;
const delegationManager = permissionsResponse[0].signerMeta.delegationManager;
const accountMetadata = permissionsResponse[0].accountMeta;

if (accountMetadata?.length !== 0) {
// If the granted permission contains accountMetadata, this must be executed before attempting to
// redeem the delegation.

// This transaction will deploy the delegator account.
const hash = walletClient.sendTransaction({
to: accountMetadata.factory,
data: accountMetadata.factoryData,
});

// You should wait for transaction to be successfully executed.
// You can use the TransactionReceipt.status to verify the state.
await publicClient.waitForTransactionReceipt( { hash });
}

const hash = walletClient.sendTransactionWithDelegation({
chain,
to: "0x70997970c51812dc3a010c7d01b50e0d17dc79c8",
value: 1n,
permissionsContext,
delegationManager
});