# Perform executions on a MetaMask user's behalf

> Use Advanced Permissions (ERC-7715) to perform executions on a MetaMask user's behalf.

# Perform executions on a MetaMask user's behalf

[Advanced Permissions (ERC-7715)](../../concepts/advanced-permissions.md) are fine-grained permissions that your dapp can request from a MetaMask user to execute transactions on their
behalf. For example, a user can grant your dapp permission to spend 10 USDC per day to buy ETH over the course
of a month. Once the permission is granted, your dapp can use the allocated 10 USDC each day to
purchase ETH directly from the MetaMask user's account.

In this guide, you'll request an ERC-20 periodic transfer permission from a MetaMask user to transfer 1 USDC every day on their behalf.

## Prerequisites

- [Install and set up the Smart Accounts Kit.](../../get-started/install.md)
- [Install MetaMask Flask 13.5.0 or later.](/snaps/get-started/install-flask)

## Steps

### 1. Set up a Wallet Client

Set up a Wallet Client using Viem's [`createWalletClient`](https://viem.sh/docs/clients/wallet) function. This client will
help you interact with MetaMask Flask.

Then, extend the Wallet Client functionality using `erc7715ProviderActions`.
These actions enable you to request <GlossaryTerm term="Advanced Permissions" /> from the user.

```typescript

const walletClient = createWalletClient({
  transport: custom(window.ethereum),
}).extend(erc7715ProviderActions())
```

### 2. Set up a Public Client

Set up a Public Client using Viem's [`createPublicClient`](https://viem.sh/docs/clients/public) function.
This client will help you query the account state and interact with the blockchain network.

```typescript

const publicClient = createPublicClient({
  chain,
  transport: http(),
})
```

### 3. Set up a session account

Set up a session account, which can be either a smart account or an
<GlossaryTerm term="Externally owned account (EOA)">EOA</GlossaryTerm>,
to request <GlossaryTerm term="Advanced Permissions" />. The requested permissions are granted to the session account, which
is responsible for executing transactions on behalf of the user.

<Tabs>
<TabItem value="Smart account">

```typescript

const privateKey = '0x...'
const account = privateKeyToAccount(privateKey)

const sessionAccount = await toMetaMaskSmartAccount({
  client: publicClient,
  implementation: Implementation.Hybrid,
  deployParams: [account.address, [], [], []],
  deploySalt: '0x',
  signer: { account },
})
```

</TabItem>
<TabItem value="EOA">

```typescript

const sessionAccount = privateKeyToAccount('0x...')
```

</TabItem>
</Tabs>

### 4. Check the EOA account code

With MetaMask Flask 13.9.0 or later, Advanced Permissions support automatically upgrading a user's
account to a [MetaMask smart account](../../concepts/smart-accounts.md). On earlier versions, upgrade
the user to a smart account before requesting Advanced Permissions.

If the user has not yet been upgraded, you can handle the upgrade [programmatically](/metamask-connect/evm/guides/send-transactions/batch-transactions) or ask the
user to [switch to a smart account manually](https://support.metamask.io/configure/accounts/switch-to-or-revert-from-a-smart-account/#how-to-switch-to-a-metamask-smart-account).

:::info Why is a Smart Account upgrade is required?
MetaMask's Advanced Permissions (ERC-7715) implementation requires the user to be upgraded to a MetaMask
Smart Account because, under the hood, you're requesting a signature for an [ERC-7710 delegation](../../concepts/delegation/overview.md).
ERC-7710 delegation is one of the core features supported only by MetaMask Smart Accounts.
:::

```typescript

const addresses = await walletClient.requestAddresses()
const address = addresses[0]

// Get the EOA account code
const code = await publicClient.getCode({
  address,
})

if (code) {
  // The address to which EOA has delegated. According to EIP-7702, 0xef0100 || address
  // represents the delegation.
  //
  // You need to remove the first 8 characters (0xef0100) to get the delegator address.
  const delegatorAddress = `0x${code.substring(8)}`

  const statelessDelegatorAddress = getSmartAccountsEnvironment(chain.id).implementations
    .EIP7702StatelessDeleGatorImpl

  // If account is not upgraded to MetaMask smart account, you can
  // either upgrade programmatically or ask the user to switch to a smart account manually.
  const isAccountUpgraded =
    delegatorAddress.toLowerCase() === statelessDelegatorAddress.toLowerCase()
}
```

### 5. Request Advanced Permissions

Request Advanced Permissions from the user with the Wallet Client's `requestExecutionPermissions` action.
In this example, you'll request an
[ERC-20 periodic permission](use-permissions/erc20-token.md#erc-20-periodic-permission).

See the [`requestExecutionPermissions`](../../reference/advanced-permissions/wallet-client.md#requestexecutionpermissions) API reference for more information.

```typescript

// Since current time is in seconds, we need to convert milliseconds to seconds.
const currentTime = Math.floor(Date.now() / 1000)
// 1 week from now.
const expiry = currentTime + 604800

// USDC address on Ethereum Sepolia.
const tokenAddress = '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238'

const grantedPermissions = await walletClient.requestExecutionPermissions([
  {
    chainId: chain.id,
    expiry,
    // The requested permissions will granted to the
    // session account.
    to: sessionAccount.address,
    permission: {
      type: 'erc20-token-periodic',
      data: {
        tokenAddress,
        // 10 USDC in WEI format. Since USDC has 6 decimals, 10 * 10^6
        periodAmount: parseUnits('10', 6),
        // 1 day in seconds
        periodDuration: 86400,
        justification: 'Permission to transfer 10 USDC every day',
      },
      isAdjustmentAllowed: true,
    },
  },
])
```

### 6. Set up a Viem client

Set up a Viem client depending on your session account type.

For a smart account, set up a Bundler Client using Viem's [`createBundlerClient`](https://viem.sh/account-abstraction/clients/bundler) function.
This lets you use the <GlossaryTerm term="Bundler">bundler</GlossaryTerm> service
to estimate gas for user operations and submit transactions to the network.

For an EOA, set up a Wallet Client using Viem's [`createWalletClient`](https://viem.sh/docs/clients/wallet) function.
This lets you send transactions directly to the network.

The toolkit provides public actions for both of the clients which can be used to redeem Advanced Permissions, and execute transactions on a user's behalf.

<Tabs>
<TabItem value="Smart account">

```typescript

const bundlerClient = createBundlerClient({
  client: publicClient,
  transport: http('https://your-bundler-rpc.com'),
  // Allows you to use the same Bundler Client as paymaster.
  paymaster: true,
}).extend(erc7710BundlerActions())
```

</TabItem>
<TabItem value="EOA">

```typescript

const sessionAccountWalletClient = createWalletClient({
  account: sessionAccount,
  chain,
  transport: http(),
}).extend(erc7710WalletActions())
```

</TabItem>
</Tabs>

### 7. Redeem Advanced Permissions

The session account can now redeem the permissions. 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, use the client action based on your session account type.
A smart account uses the Bundler Client's `sendUserOperationWithDelegation` action,
and an EOA uses the Wallet Client's `sendTransactionWithDelegation` action.

See the [`sendUserOperationWithDelegation`](../../reference/erc7710/bundler-client.md#senduseroperationwithdelegation) and [`sendTransactionWithDelegation`](../../reference/erc7710/wallet-client.md#sendtransactionwithdelegation) API reference for more information.

<Tabs>
<TabItem value="Smart account">

```typescript

// These properties must be extracted from the permission response.
const permissionContext = grantedPermissions[0].context
const delegationManager = grantedPermissions[0].delegationManager

// USDC address on Ethereum Sepolia.
const tokenAddress = '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238'

// Calls without permissionContext and delegationManager will be executed
// as a normal user operation.
const userOperationHash = await bundlerClient.sendUserOperationWithDelegation({
  publicClient,
  account: sessionAccount,
  calls: [
    {
      to: tokenAddress,
      data: calldata,
      permissionContext,
      delegationManager,
    },
  ],
  // Appropriate values must be used for fee-per-gas.
  maxFeePerGas: 1n,
  maxPriorityFeePerGas: 1n,
})
```

</TabItem>
<TabItem value="EOA">

```typescript

// These properties must be extracted from the permission response.
const permissionContext = grantedPermissions[0].context
const delegationManager = grantedPermissions[0].delegationManager

// USDC address on Ethereum Sepolia.
const tokenAddress = '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238'

const transactionHash = await sessionAccountWalletClient.sendTransactionWithDelegation({
  to: tokenAddress,
  data: calldata,
  permissionContext,
  delegationManager,
})
```

</TabItem>

<TabItem value="config.ts">

```typescript

export const calldata = encodeFunctionData({
  abi: erc20Abi,
  args: [sessionAccount.address, parseUnits('1', 6)],
  functionName: 'transfer',
})
```

</TabItem>
</Tabs>

## Next steps

- See how to [get the supported execution permissions](get-supported-permissions.md).
- See how to configure different [ERC-20 token permissions](use-permissions/erc20-token.md) and
  [native token permissions](use-permissions/native-token.md).
