Direct staking

P2P Direct Staking v1: Eth2 Depositor & Message Sender Flow

Non-Custodial On-Chain Deposit (via P2pEth2Depositor)

Diagram 1: Direct Deposit Flow

sequenceDiagram
    autonumber
    participant Client (Validator Owner)
    participant P2pEth2Depositor
    participant BeaconDepositContract
    participant P2P Infra (Observer)

    Client->>P2pEth2Depositor: deposit(pubkey[], withdrawalCreds, signatures, roots)\nwith 32 ETH * N
    activate P2pEth2Depositor
    loop For each validator
        P2pEth2Depositor->>BeaconDepositContract: deposit(pubkey, withdrawalCreds, signature, root)\nwith 32 ETH
    end
    P2pEth2Depositor-->>Client: emit DepositEvent(msg.sender, count)
    deactivate P2pEth2Depositor

    P2P Infra (Observer)-->>P2P Infra (Observer): Listens for DepositEvent

ETH is atomically forwarded to the official Beacon chain deposit contract — no ETH is ever held or pooled.

In P2P.org’s direct staking v1 flow, users stake ETH without ever giving custody of their funds to P2P. The key on-chain component is the P2pEth2Depositor smart contract, which atomically forwards the user’s ETH to Ethereum’s official deposit contract. The flow is as follows:

  1. User Initiates Deposit: The user (validator owner) generates or obtains the required deposit data (validator BLS public key, withdrawal credential, signature, etc.) and sends a transaction calling P2pEth2Depositor.deposit(...) with this data. The call includes the exact ETH required (32 ETH per validator) for one or multiple validator deposits. The contract enforces that the caller sends the correct amount of ETH – it requires msg.value to equal 32 ETH * number_of_validators, and allows batching up to 100 validators in one transaction. (This batching is purely for gas efficiency; each validator still gets a distinct 32 ETH deposit.)

  2. Atomic Deposit to Beacon Chain: Inside the deposit(...) function, P2pEth2Depositor immediately forwards the ETH to the official Eth2 DepositContract for each validator. It iterates over the provided list of validator pubkeys and calls depositContract.deposit{value: 32 ETH}(pubkey, withdrawal_credential, signature, deposit_data_root) for each one. This means the user’s ETH is transferred directly into the Beacon Chain deposit contract within the same transaction that the user sent, without any intermediate holding account. If any deposit in the batch fails (e.g., invalid data), the entire transaction reverts, ensuring no partial custody or leftover funds – it’s all-or-nothing and handled atomically on-chain.

  3. No P2P Custody or Pools: The design explicitly prevents P2P or the proxy contract from holding the ETH. The P2pEth2Depositor contract does not accept free-floating ETH transfers at all – its fallback/receive function immediately reverts any direct ETH sent to it. This guarantees that users can only send ETH through the deposit function (which forwards it onward). In other words, P2P never aggregates user ETH in its own wallet or contract; the stake goes straight from the user’s address to the official deposit contract (under the user’s withdrawal credentials) in one step.

  4. Event Emission: After successfully processing the deposits, P2pEth2Depositor emits a DepositEvent recording the sender’s address and the number of validators deposited. This on-chain event serves as a record that the deposit transaction for those validators was completed. (The event does not contain the validator pubkeys or other details, just a count, since P2P already knows which keys were included based on the transaction data.)

Validator Key Management & Withdrawal Credentials

A core principle of this direct staking flow is that the user retains control of withdrawal rights, while validator operation is handled by P2P. In practice, this means the user’s chosen withdrawal credential is used for the deposit, and P2P never controls the withdrawal of the staked ETH or rewards. P2P’s system is non-custodial by ensuring the roles of the two key pairs are separated:

  • Validator Keys (BLS Signing Keys): These are the keys that actually run the validator (sign attestations, propose blocks, and sign the deposit and exit messages). In the v1 flow, the user delegates validator key management to P2P’s infrastructure. In many cases, P2P (and/or a Distributed Validator Technology like SSV) generates or holds the BLS validator private keys to operate the validators on behalf of the user. The user does not need to run the validator node; P2P handles all “live” validator duties. Importantly, these validator keys are also required to initiate a voluntary exit (they must sign the exit message when the user wants to stop validating).

  • Withdrawal Credentials/Keys: The withdrawal credential defines where validator withdrawals (refund of 32 ETH and earned rewards) will be paid out on the execution layer. In direct staking v1, the client (user) maintains custody of the withdrawal credential. Typically, the withdrawal credential is set to an Ethereum address controlled by the user (a 0x01 credential, hashed from the user’s ETH address). This means when withdrawals are enabled, the unlocked ETH will go directly to the user’s own wallet. P2P does not hold the withdrawal private key and cannot unilaterally withdraw or move the staked funds – this is what makes the setup non-custodial. (In some setups, the withdrawal credential could point to a smart contract that trustlessly splits fees, but it would still be a pre-set address that P2P cannot change or divert; P2P v1 generally had the user’s address as the withdrawal target to ensure P2P never controls the funds.)

By structuring key ownership this way, the user’s capital is secure from P2P or any third-party misappropriation. P2P’s role is limited to validator operations, while the user’s role is to ultimately control the funds at withdrawal. This “user holds withdrawal key, operator holds validator key” model is explicitly highlighted by P2P: the client preserves custody of withdrawal credentials, delegating only the validator key management to P2P. The trade-off is that the user must trust P2P to manage the validator key honestly (e.g. not to double-sign or to properly sign an exit when asked), but P2P cannot steal or redirect the stake or rewards because it lacks the withdrawal key.

On-Chain Signaling & Validator Exit via P2pMessageSender

Diagram 2: Validator Exit via On-Chain Signal

sequenceDiagram
    autonumber
    participant Client (Validator Owner)
    participant P2pMessageSender
    participant P2P Backend
    participant Beacon Chain

    Client->>P2pMessageSender: send("exit validator X")
    activate P2pMessageSender
    P2pMessageSender-->>Client: emit Message(msg.sender, "exit validator X")
    deactivate P2pMessageSender

    P2P Backend->>P2P Backend: Listen for Message events
    P2P Backend->>P2P Backend: Verify msg.sender is authorized
    P2P Backend->>Beacon Chain: Broadcast VEM (Voluntary Exit Message)

Only messages from the real validator owner (msg.sender) are accepted. P2P backend handles validator exit using the BLS key.

Direct staking v1 also provides a mechanism for the user to signal certain actions (especially to request a validator exit/withdrawal) in a secure, trust-minimized way. This is handled by the P2pMessageSender contract, which works in tandem with off-chain services:

  • P2pMessageSender Contract: It’s a simple on-chain messaging contract that anyone can call, but whose events are monitored by P2P. The contract has a function send(string text) that emits an event (e.g. Message) containing the caller’s address and the provided text message. The text can encode a specific instruction or identifier (for example, a message indicating “please exit validator X”). The key point is that whatever the message contains, it is cryptographically tied to the sender’s Ethereum address via the event.

  • Off-Chain Listening and Verification: P2P’s backend service continuously listens for Message events from the P2pMessageSender contract. When a user wants to initiate a validator exit (withdraw), they will trigger an on-chain message via this contract (either directly or through an integrated UI/API call). The P2P service, upon detecting a new message event, checks the sender field of the event against its records of validator ownership. Only if the event’smsg.sender matches the address authorized for that validator (i.e. the original staker’s address or a designated controller address) will P2P honor the request. This ensures authenticity – a malicious party can’t impersonate the validator owner by simply calling the contract, because P2P will ignore messages from addresses that don’t match the expected owner. (The P2P docs note that only the preset controller or withdrawal address can initiate an exit via the smart contract; if the sender doesn’t match, the exit won’t proceed.)

  • VEM Broadcast (Validator Exit Message): Once a valid exit request is confirmed (i.e. a Message event from the real owner is received with an instruction to exit), P2P will perform the off-chain steps to exit the validator. Specifically, P2P will take the validator’s BLS key (which it manages) and craft a Voluntary Exit Message (VEM), which is a signed message to the Ethereum consensus layer indicating the validator should exit. P2P then broadcasts this VEM to the Beacon Chain network. Broadcasting the VEM initiates the validator’s voluntary exit from active duty. After the exit is processed on-chain and the exit epoch is reached, the validator’s balance (32 ETH plus any rewards) becomes withdrawable to the user’s withdrawal address. The entire exit process is thus triggered by the user’s on-chain signal and executed by P2P, with the smart contract event acting as the trigger point for a secure off-chain action.

In summary, the direct staking v1 architecture uses on-chain contracts to keep the process trust-minimized: P2pEth2Depositor lets users deposit to the beacon chain directly (no custodial intermediate), and P2pMessageSender provides a verified communication channel for exits or other signals. The user maintains ultimate control of funds via the withdrawal credential, while P2P handles the technical heavy lifting of validator operations and exit broadcasting. This separation of concerns provides a non-custodial staking service – P2P cannot move the user’s ETH, only facilitate its staking and unstaking per the user’s requests.


Smart contract deployments

P2pEth2Depositor (v1)

Mainnet 0x4ca21e4d3a86e7399698f88686f5596dbe74adeb

Hoodi 0x375e183efA05fb2bb66Cd0e7C3a05D9942046612

Github

Audit


P2pEth2Depositor (v2)

Mainnet 0x8e76a33f1aFf7EB15DE832810506814aF4789536

Hoodi 0x44Cd8e2c24d3E11F0838a8Ea942C2D0da5BeD6C0

Github

Audit