Withdrawal using VEM

It is possible to initiate withdrawals on the Ethereum network using the Staking API by retrieving the Validator Exit Message (VEM). Request examples are provided using cURL.

  1. Generate an Elliptic Curve Diffie-Hellman (ECDH) key pair. Retain the private key securely for future steps, as it is needed to decrypt the VEM.

    We use keys on theNIST P-256 curve (aka secp256r11 aka prime256v1) for the ECDH algorithm inside the ECIES encryption scheme. Keep your private key secret and discard it when it’s not needed anymore. You will also need it when making the initial request.

    Those public keys have to be a base64-encoded string of a PEM block with the x509 SPKI (aka PKIX) formatted public key on the P-256 curve.

    Example of such a key:

    1. "ecdhPublicKey":"LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFNWgwcmRaSEZaZWNSbWIxR2RqQis3T0dUMnovegozMG1TRnZhWTdQaTltek5HT3J6SjZVbS9GbUMwb2lXMVFBT1FhOVFldGFpZm1EMU0zc09iU0JwV3N3PT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg=="
    

    Example of its base64-decoded version:

    -----BEGIN PUBLIC KEY-----  
    MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE5h0rdZHFZecRmb1GdjB+7OGT2z/z  
    30mSFvaY7Pi9mzNGOrzJ6Um/FmC0oiW1QAOQa9QetaifmD1M3sObSBpWsw==  
    -----END PUBLIC KEY-----
    

    Your ECDH public key will be used to produce a ECDH shared secret inside the ECIES scheme by P2P server. The server’s ECDH public key is encoded in the first N bytes (65 in case of the P-256 curve) as it is required in SEC 1 v2.

  2. Prepare id that is an arbitrary UUID. Generate it in one of the following ways:

  3. Prepare the JSON vem_request structure embedding the validators' pubkeys for withdrawal and ecdh_client_pubkey.

    Example vem_request structure:

    {
      "action": "vem_request",
      "pubkeys": [
        "0x867a9f95287decd74e076b8fefca3022fcb2c3c37de246e950ade0cc3609c74d4badda62657a64c2c4009f1b6625c647",
        "0x93e298f6ac5fb8f262a23450d1638aec1c45f77fd1ac86beb19b696b3b84fef3694e9b2166b337783911a3d7df13fbf1"
      ],
      "ecdh_client_pubkey": "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFNWgwcmRaSEZaZWNSbWIxR2RqQis3T0dUMnovegozMG1TRnZhWTdQaTltek5HT3J6SjZVbS9GbUMwb2lXMVFBT1FhOVFldGFpZm1EMU0zc09iU0JwV3N3PT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg=="
    }
    
    • action — action type to be requested, here specified as a VEM request.
    • pubkeys — array of public keys identifying the validators you are withdrawing from.
    • ecdh_client_pubkey — your ECDH public key for secure communication.
  4. Submit the VEM request by sending a POST request to /api/v1/eth/staking/direct/vem/create. Use https://api-test-holesky.p2p.org for testing or https://api.p2p.org for production.

    Example request:

    curl --request POST \
         --url https://api.p2p.org/api/v1/eth/staking/direct/vem/create \
         --header 'accept: application/json' \
         --header 'authorization: Bearer <token>' \
         --header 'content-type: application/json' \
         --data '
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "type": "",
      "vemRequest": "{\"action\": \"vem_request\",\"pubkeys\": [\"0x867a9f95287decd74e076b8fefca3022fcb2c3c37de246e950ade0cc3609c74d4badda62657a64c2c4009f1b6625c647\", \"0x93e298f6ac5fb8f262a23450d1638aec1c45f77fd1ac86beb19b696b3b84fef3694e9b2166b337783911a3d7df13fbf1\"],\"ecdh_client_pubkey\": \"LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFNWgwcmRaSEZaZWNSbWIxR2RqQis3T0dUMnovegozMG1TRnZhWTdQaTltek5HT3J6SjZVbS9GbUMwb2lXMVFBT1FhOVFldGFpZm1EMU0zc09iU0JwV3N3PT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==\"}",
      "vemRequestSignature": "0x.....",
      "vemRequestSignedBy": "0x.....",
      "vemRequestTxId": "",
      "vemRequestProof": ""
    }
    '
    
    • id — UUID to identify the VEM set-up request.
    • type — VEM request type used. Two types are supported: off_chain and on_chain.
    • vemRequest — JSON formatted string that represents the VEM request.
    • vemRequestSignature — signature of the vem_request by either withdrawalAddress or controllerAddress. Required if the type is off_chain.
    • vemRequestSignedBy — Ethereum address (withdrawalAddress or controllerAddress) that signed the request. Required if the type is off_chain.
    • vemRequestTxId — hash of the successful vem_request transaction. Required if the type is on_chain.
    • vemRequestProof — hash of the signed vemRequestTxId transaction. Required if the type is on_chain.

    Example response:

    {
      "result": true
    }
    

    To authorize the withdrawal process, the vem_request must be digitally signed using the Ethereum private key associated with your withdrawalAddress or controllerAddress.

    vem_request_signature is a 0x prefixed hex encoded signature of the Keccak256 hash of the vem_request field, done using the private key of either the withdrawalAddress or controllerAddress. This private key should be a standard Ethereum key on the secp256k1 curve and use the standard Ethereum extended ECDSA signature algorithm.

    The signature verifies that the requestor has the authority to perform the transaction and is an essential security measure to prevent unauthorized withdrawals.

  5. Check the request status and retrieve the signed VEM by sending a GET request to /api/v1/eth/staking/direct/vem/status/{id}.

    Example request:

    curl --request GET \
         --url https://api.p2p.org/api/v1/eth/staking/direct/vem/status/3fa85f64-5717-4562-b3fc-2c963f66afa6 \
         --header 'accept: application/json' \
         --header 'authorization: Bearer <token>' \
    
    • id — UUID that was specified in the VEM set-up request.

    Example response:

    {
      "error": null,
      "result": {
        "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "status": "processing",
        "type": "",
        "vemRequest": "{\"action\": \"vem_request\",\"pubkeys\": [\"0x867a9f95287decd74e076b8fefca3022fcb2c3c37de246e950ade0cc3609c74d4badda62657a64c2c4009f1b6625c647\", \"0x93e298f6ac5fb8f262a23450d1638aec1c45f77fd1ac86beb19b696b3b84fef3694e9b2166b337783911a3d7df13fbf1\"],\"ecdh_client_pubkey\": \"LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFNWgwcmRaSEZaZWNSbWIxR2RqQis3T0dUMnovegozMG1TRnZhWTdQaTltek5HT3J6SjZVbS9GbUMwb2lXMVFBT1FhOVFldGFpZm1EMU0zc09iU0JwV3N3PT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==\"}",
        "vemRequestSignature": "0x.....",
        "vemRequestSignedBy": "0x.....",
        "vemRequestTxId": "",
        "vemRequestProof": "",
        "error": "",
        "vemResult": "",
        "forkVersion": ""
      }
    }
    
    • id — UUID that was specified in the VEM set-up request.
    • status — current status of the VEM request:
      • processing — requst is in progress.
      • success — answer is ready.
      • error — there is an issue with the data, unable to create VEM.
      • fault — error occurred on the server side.
    • type — VEM request type used. Two types are supported: off_chain and on_chain.
    • vemRequest — base64-encoded VEM request.
    • vemRequestSignature — digital signature of the request.
    • vemRequestSignedBy — Ethereum address that signed the request, providing a linkage to the withdrawal or controller account.
    • vemRequestTxId — hash of the successful vem_request transaction.
    • vemRequestProof — hash of the signed vemRequestTxId transaction.
    • error — error message (if exists).
    • vemResult — encrypted VEM results.
    • forkVersion — version of the Ethereum fork.

Upon receipt of the vemResult, use your private ECDH key to decrypt the message using the ECIES scheme and retrieve the signed VEM for your staked Ethereum.

What's Next?