# Undelegate 7702 account

> Remove EIP-7702 delegation and restore an account to a plain EOA

> For the complete documentation index, see [llms.txt](/docs/llms.txt).

Undelegation removes the smart contract delegation from an [EIP-7702](/docs/wallets/transactions/using-eip-7702) account by delegating to the zero address (`0x0000...0000`). This restores the account to a plain EOA.

## How it works

When you undelegate an account, the Wallet API submits an EIP-7702 authorization that delegates the account to `address(0)`. Because the delegation target is the zero address, the EVM clears the account's code delegation and the account reverts to a standard externally-owned account.

Gas for the undelegation is sponsored through a BSO (Bundler Sponsorship) policy, so the account does not need to hold native tokens.

## Prerequisites

* API key from your [dashboard](https://dashboard.alchemy.com/apps)
* Enterprise plan tier — sponsored undelegation is gated to enterprise customers
* A BSO (Bundler Sponsorship) policy ID
* An account that is currently delegated via EIP-7702

## Implementation

<Tabs>
  <Tab title="JavaScript" language="typescript">
    Use the [`undelegateAccount`](/docs/wallets/reference/wallet-apis/functions/undelegateAccount) action on the smart wallet client to remove EIP-7702 delegation and restore the account to a plain EOA. Gas is sponsored via a BSO policy.

<CodeBlocks>

```ts title="undelegateAccount.ts"
import { client } from "./client.ts";

// Undelegate the account — uses the client's paymaster policy
const { id } = await client.undelegateAccount();

// Wait for confirmation
const status = await client.waitForCallsStatus({ id });

console.log(status); // status: 200 once mined
```

```ts title="client.ts"
import type { Hex } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { baseSepolia } from "viem/chains";
import { createSmartWalletClient, alchemyWalletTransport } from "@alchemy/wallet-apis";

export const client = createSmartWalletClient({
  transport: alchemyWalletTransport({
    apiKey: "YOUR_API_KEY",
  }),
  chain: baseSepolia,
  signer: privateKeyToAccount("0xYOUR_PRIVATE_KEY" as const),
  // BSO policy for gas sponsorship
  paymaster: { policyId: "YOUR_BSO_POLICY_ID" },
});
```

</CodeBlocks>

You can also pass the policy ID per-call or override the account and chain:

```ts
import { baseSepolia } from "viem/chains";

// Per-call policy ID
const { id } = await client.undelegateAccount({
  capabilities: {
    paymaster: { policyId: "YOUR_BSO_POLICY_ID" },
  },
});

// Override account and chain
const { id } = await client.undelegateAccount({
  account: "0xYourDelegatedAddress",
  chain: baseSepolia,
  capabilities: {
    paymaster: { policyId: "YOUR_BSO_POLICY_ID" },
  },
});
```

  </Tab>

  <Tab title="API" language="bash">
    <Info>
  The examples provided use [jq](https://jqlang.org/) to parse json and
  [foundry](https://getfoundry.sh/) to sign a raw payload.
</Info>

See the [`wallet_prepareCalls`](/docs/wallets/api-reference/smart-wallets/wallet-api-endpoints/wallet-prepare-calls),
[`wallet_sendPreparedCalls`](/docs/wallets/api-reference/smart-wallets/wallet-api-endpoints/wallet-send-prepared-calls),
and [`wallet_getCallsStatus`](/docs/wallets/api-reference/smart-wallets/wallet-api-endpoints/wallet-get-calls-status)
API reference for full descriptions of the parameters used in the following example.

<Steps>

<Step title="Declare your secrets">

```bash twoslash
ALCHEMY_API_KEY=your-alchemy-api-key
CHAIN_ID=0x14a34 # Base Sepolia (or your desired chain ID as hex)
SIGNER_ADDRESS=your-signer-address
SIGNER_PRIVATE_KEY=your-signer-private-key
BSO_POLICY_ID=your-bso-policy-id
```

</Step>

<Step title="Prepare the undelegation">

Call `wallet_prepareCalls` with the `eip7702Auth` capability set to delegate to the zero address. Do **not** include a `calls` field — undelegation requests must omit it entirely.

```bash twoslash
PREPARE_RESPONSE=$(curl --request POST \
  --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \
  --header 'accept: application/json' \
  --data '
{
  "id": 1,
  "jsonrpc": "2.0",
  "method": "wallet_prepareCalls",
  "params": [{
    "from": "'$SIGNER_ADDRESS'",
    "chainId": "'$CHAIN_ID'",
    "capabilities": {
      "paymasterService": {
        "policyId": "'$BSO_POLICY_ID'"
      },
      "eip7702Auth": {
        "delegation": "0x0000000000000000000000000000000000000000"
      }
    }
  }]
}')
```

The response type is `authorization`:

```json
{
  "type": "authorization",
  "chainId": "0x14a34",
  "data": {
    "address": "0x0000000000000000000000000000000000000000",
    "nonce": "0x5"
  }
}
```

</Step>

<Step title="Sign the authorization">

Sign the EIP-7702 authorization payload returned by the prepare step. The `signatureRequest.rawPayload` contains the hash to sign.

```bash twoslash
AUTH_PAYLOAD=$(echo $PREPARE_RESPONSE | jq -r '.result.signatureRequest.rawPayload')
AUTH_SIGNATURE=$(cast wallet sign --no-hash --private-key "$SIGNER_PRIVATE_KEY" "$AUTH_PAYLOAD")
```

<Warning>
  Using `cast wallet sign --no-hash` for the authorization payload is acceptable for testing but not recommended for production. See [Using EIP-7702](/docs/wallets/transactions/using-eip-7702) for production signing recommendations.
</Warning>

</Step>

<Step title="Send the signed undelegation">

Send the signed authorization via `wallet_sendPreparedCalls`. Include the BSO policy in the capabilities.

```bash twoslash
SEND_RESPONSE=$(curl --request POST \
  --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \
  --header 'accept: application/json' \
  --data '
{
  "id": 1,
  "jsonrpc": "2.0",
  "method": "wallet_sendPreparedCalls",
  "params": [{
    "type": "authorization",
    "chainId": "'$CHAIN_ID'",
    "signature": {
      "type": "secp256k1",
      "data": "'$AUTH_SIGNATURE'"
    },
    "data": {
      "address": "0x0000000000000000000000000000000000000000",
      "nonce": "'$(echo $PREPARE_RESPONSE | jq -r '.result.data.nonce')'"
    },
    "capabilities": {
      "paymasterService": {
        "policyId": "'$BSO_POLICY_ID'"
      }
    }
  }]
}')

CALL_ID=$(echo $SEND_RESPONSE | jq -r '.result.id')
```

</Step>

<Step title="Poll for status">

Use `wallet_getCallsStatus` to check when the undelegation is confirmed. A pending state (1xx status codes) is expected for some time before the transition to confirmed. Poll while the status is pending. The [API documentation](/docs/wallets/api-reference/smart-wallets/wallet-api-endpoints/wallet-get-calls-status#response.body.status) provides details on each status code.

```bash twoslash
curl --request POST \
  --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \
  --header 'accept: application/json' \
  --data '
{
  "id": 1,
  "jsonrpc": "2.0",
  "method": "wallet_getCallsStatus",
  "params": [
    "'$CALL_ID'"
  ]
}'
```

| Status code | Meaning |
|---|---|
| `100` | Pending — submitted, waiting to be mined |
| `200` | Confirmed — account is undelegated |
| `400` | Chain rules failure — transaction reverted onchain |

When confirmed, the response includes the transaction hash:

```json
{
  "id": "CALL_ID",
  "chainId": "0x14a34",
  "atomic": true,
  "status": 200,
  "receipts": [{ }],
  "details": {
    "type": "delegation",
    "txHash": "0xABCDEF..."
  }
}
```

</Step>

</Steps>

<Accordion title="Preparing the EIP-7702 authorization yourself with Foundry">
  If you need full control over the authorization step, you can sign the EIP-7702 authorization yourself using Foundry's `cast` and submit it directly via `wallet_sendPreparedCalls` — skipping the `wallet_prepareCalls` step.

  ```bash
  # 1. Get the current nonce for the account
  NONCE_DEC=$(cast nonce $SIGNER_ADDRESS --rpc-url https://base-sepolia.g.alchemy.com/v2/$ALCHEMY_API_KEY)
NONCE_HEX=$(printf "0x%x" $NONCE_DEC)

  # 2. Sign the EIP-7702 authorization to delegate to address(0)
  #    cast wallet sign-auth returns RLP-encoded [chainId, address, nonce, yParity, r, s]
  SIGNED_AUTH=$(cast wallet sign-auth \
    --private-key $SIGNER_PRIVATE_KEY \
    --nonce $NONCE_DEC \
    --chain 84532 \
    0x0000000000000000000000000000000000000000)

  # 3. Extract yParity, r, s from the RLP-decoded authorization
  DECODED=$(cast from-rlp $SIGNED_AUTH)
  YPARITY=$(echo $DECODED | jq -r '.[3]')
  R=$(echo $DECODED | jq -r '.[4]')
  S=$(echo $DECODED | jq -r '.[5]')

  # 4. Submit via wallet_sendPreparedCalls
  curl --request POST \
    --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \
    --header 'Content-Type: application/json' \
    --data '{
      "id": 1,
      "jsonrpc": "2.0",
      "method": "wallet_sendPreparedCalls",
      "params": [{
        "type": "authorization",
        "chainId": "0x14a34",
        "data": {
          "address": "0x0000000000000000000000000000000000000000",
          "nonce": "'$NONCE_HEX'"
        },
        "signature": {
          "type": "secp256k1",
          "data": {
            "r": "'$R'",
            "s": "'$S'",
            "yParity": "'$YPARITY'"
          }
        },
        "capabilities": {
          "paymasterService": {
            "policyId": "'$BSO_POLICY_ID'"
          }
        }
      }]
    }'
  ```

  <Warning>
    Ensure the nonce you use matches the account's current nonce onchain. An incorrect nonce will cause the authorization to be rejected.
  </Warning>
</Accordion>

  </Tab>
</Tabs>

## Advanced

<Accordion title="Preparing the EIP-7702 authorization yourself">
  If you need full control over the authorization step — for example, to inspect the authorization data before signing or to sign in a separate service — you can prepare and sign the EIP-7702 authorization yourself, then submit it via `wallet_sendPreparedCalls`.

  ```ts
  import { serializeSignature } from "viem";
  import { privateKeyToAccount } from "viem/accounts";
  import { baseSepolia } from "viem/chains";
  import { createSmartWalletClient, alchemyWalletTransport } from "@alchemy/wallet-apis";

  const account = privateKeyToAccount("0xYOUR_PRIVATE_KEY");

  // Smart wallet client for preparing and submitting calls
  const smartWalletClient = createSmartWalletClient({
    transport: alchemyWalletTransport({ apiKey: "YOUR_API_KEY" }),
    chain: baseSepolia,
    signer: account,
  });

  // Prepare the undelegation to get the authorization data
  const prepared = await smartWalletClient.prepareCalls({
    account: account.address,
    chainId: baseSepolia.id,
    capabilities: {
      paymaster: { policyId: "YOUR_BSO_POLICY_ID" },
      eip7702Auth: {
        delegation: "0x0000000000000000000000000000000000000000",
      },
    },
  });

  if (prepared.type !== "authorization") {
    throw new Error("Expected prepareCalls result type to be 'authorization'");
  }

  // Sign the EIP-7702 authorization using the values from the prepare step
  const { r, s, v, yParity } = await account.signAuthorization({
    contractAddress: prepared.data.address,
    chainId: prepared.chainId,
    nonce: prepared.data.nonce,
  });

  // Submit via sendPreparedCalls
  const result = await smartWalletClient.sendPreparedCalls({
    type: "authorization",
    chainId: baseSepolia.id,
    data: prepared.data,
    signature: {
      type: "secp256k1",
      data: serializeSignature({ r, s, yParity: yParity ?? Number(v - 27n) }),
    },
    capabilities: {
      paymaster: {
        policyId: "YOUR_BSO_POLICY_ID",
      },
    },
  });

  console.log(result);
  ```
</Accordion>

<Accordion title="Checking delegation status">
  After undelegation is confirmed, you can verify the account is no longer delegated by checking its code. A plain EOA has no code:

  ```bash
  cast code $ACCOUNT_ADDRESS --rpc-url https://base-sepolia.g.alchemy.com/v2/$ALCHEMY_API_KEY
  # Returns 0x for a plain EOA
  ```

  If the account is still delegated, the response will contain the EIP-7702 delegation designator prefix (`0xef0100`).
</Accordion>

## Next steps

Build more:

* [How EIP-7702 works](/docs/wallets/transactions/using-eip-7702)
* [Sponsor gas](/docs/wallets/transactions/sponsor-gas)