> ## Documentation Index
> Fetch the complete documentation index at: https://docs.p2p.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Withdrawal

> Withdraw staked ETH with the P2P.org Staking API.

To initiate a withdrawal on the Ethereum network using the Staking API, follow these steps:

1. Prepare the withdrawal transaction.
2. Sign and broadcast the transaction.

Request examples are provided using [cURL](https://curl.se/).

Before initiating the withdrawal flow, please note that only the address set as the <Tooltip tip="On Ethereum, we introduced the controller address entity to provide greater flexibility and maintain high-level security during the withdrawal flow. When the user requests the validator withdrawal, only authorized addresses can initiate this request by calling our withdrawal smart contract. The authorized addresses are the withdrawal address and the controller address. Since many users prefer not to use the withdrawal address to interact with smart contracts, a second authorized address can be specified as controller address in the new staking request.">controller address</Tooltip> or withdrawal address can initiate a validator exit via our smart contract. If the transaction sender doesn't match the eligible address, the validator exit will not be initiated.

<Warning>
  **Please note**

  On May 7, 2025, the major Ethereum network upgrade was carried out that brought many benefits for clients who use ETH or SSV validators. In particular, for clients with `0x02` withdrawal credentials, **partial withdrawals** are available.

  For more details, see [Pectra Upgrade overview](/docs/pectra-overview).
</Warning>

<Steps>
  <Step title="Prepare withdrawal transaction">
    Create a serialized transaction to initiate the withdrawal process utilizing the functionalities of P2P smart contract and send it as a POST request to [/api/v1/eth/staking/direct/tx/withdrawal](/reference/eth-staking-withdrawal).

    Example request:

    ```bash theme={null}
    curl --request POST \
         --url https://api.p2p.org/api/v1/eth/staking/direct/tx/withdrawal \
         --header 'accept: application/json' \
         --header 'authorization: Bearer <token>' \
         --header 'content-type: application/json' \
         --data '
    {
      "pubkeys": [
        "0xffC08FcD7cFeF5c70fB2b0e1f2A8EaA690AaE2bDFfa5dBEc4dEef31DcC0B19eB1f9Cebe3E2fe9eefBD9a1BDF6FD89b39"
      ]
    }
    '
    ```

    * `pubkeys` — list of validators public keys.

    Example response:

    ```json theme={null}
    {
       "result": {
          "serializeTx": "0x02f902d705808301674e8508530af16e830186a094681a1b3441c6bfb12f91651efd9f02c83c0702938901bc16d674ec800000b902a44f498c730000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030aa5f27070a21d79455c4a9b73c0aa4a8b1a65a1fb530d7fd8e6cd23aa16660679ac43ee4861098f6d9166aed3a4d8abb0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002001000000000000000000000028c84612d37de9209018ad96167f12169b653e9a000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000060978c565cd915f4e885b4201093d1501697610eb9ee99b9b60b70434dc330e98d5b42927725304ded48483a8b8f39506d09bcb22ee18d4f6b50257946ac5ee360385308d95c0e2bc963902d42e985c29ee489aa3c989ac1561c952a6424f107a800000000000000000000000000000000000000000000000000000000000000014cb452f6e3f10ba2175c86a0284f53fcb61404b458393391abc3d5622e3e55cdc0",
          "to": "0x39D02C253dA1d9F85ddbEB3B6Dc30bc1EcBbFA17",
          "gasLimit": "",
          "data": "",
          "value": "",
          "chainId": "",
          "type": "",
          "maxFeePerGas": "",
          "maxPriorityFeePerGas" : ""
       }
    }
    ```

    * `serializeTx` — serialized unsigned transaction.
    * `to` — recipient address for this transaction.
    * `gasLimit` — maximum gas limit for this block.
    * `data` — transaction data.
    * `value` — amount this transaction is sending in Wei.
    * `chainId` — chain ID this transaction is authorized on, as specified by [EIP-155](https://eips.ethereum.org/EIPS/eip-155).
    * `type` — [EIP-2718](https://eips.ethereum.org/EIPS/eip-2718) type of this transaction envelope.
    * `maxFeePerGas` — maximum price per unit of gas this transaction will pay for the combined [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) block's base fee and this transaction's priority fee in Wei.
    * `maxPriorityFeePerGas` — price per unit of gas in Wei, which is added to the [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) block's base fee. This added fee is used to incentivize miners to prioritize this transaction.

    ### P2P smart contracts

    [Mainnet](https://etherscan.io/address/0x4E1224f513048e18e7a1883985B45dc0Fe1D917e) `0x4E1224f513048e18e7a1883985B45dc0Fe1D917e`

    [Hoodi](https://hoodi.etherscan.io/address/0x158f2bbEf21CF9F92Cf4A294999BA422948c8242) `0x158f2bbEf21CF9F92Cf4A294999BA422948c8242`

    [Source](https://github.com/p2p-org/eth-staking-fee-distributor-contracts/blob/v2-audited/contracts/p2pMessageSender/P2pMessageSender.sol)

    [Audit](https://github.com/mixbytes/audits_public/tree/master/P2P.org/ETH2%20Depositor%20%26%20ETH%20Staking%20Fee%20Distributor%20\(v.2\))
  </Step>

  <Step title="Sign and broadcast transaction">
    Use the following example of the code to sign and broadcast the withdrawal transaction.

    ```javascript theme={null}
    require("dotenv").config();
    const ethers = require("ethers");

    async function signAndBroadcast() {
      console.log("Started");

      // Enter the serialized transaction
      const rawTransaction = process.env.RAW_TRANSACTION;

      // Enter the private key of the address used to transfer the stake amount
      const privateKey = process.env.PRIVATE_KEY;

      // Enter the selected RPC URL
      const rpcURL = process.env.RPC_URL;

      // Initialize the provider using the RPC URL
      const provider = new ethers.providers.JsonRpcProvider(rpcURL);

      // Initialize a new Wallet instance
      const wallet = new ethers.Wallet(privateKey, provider);

      // Parse the raw transaction
      const tx = ethers.utils.parseTransaction(rawTransaction);

      tx.nonce = await provider.getTransactionCount(wallet.address);

      // Enter the max fee per gas and prirorty fee
      tx.maxFeePerGas = ethers.utils.parseUnits(
        process.env.MAX_FEE_PER_GAS_IN_GWEI,
        "gwei"
      );
      tx.maxPriorityFeePerGas = ethers.utils.parseUnits(
        process.env.MAX_PRIORITY_FEE_IN_GWEI,
        "gwei"
      );

      // Sign the transaction
      const signedTransaction = await wallet.signTransaction(tx);

      // Send the signed transaction
      const transactionResponse = await provider.sendTransaction(signedTransaction);

      return transactionResponse;
    }

    signAndBroadcast()
      .then((transactionResponse) => {
        console.log(
          "Transaction broadcasted, transaction hash:",
          transactionResponse.hash
        );
      })
      .catch((error) => {
        console.error("Error:", error);
      })
      .finally(() => {
        console.log("Finished");
      });
    ```
  </Step>
</Steps>


## Related topics

- [Withdrawal using VEM](/docs/withdrawal-vem.md)
- [Getting Started](/docs/staking-ethereum.md)
- [Staking API reference](/reference/eth-nodes-request-create.md)
