Unlimited (Smart Contract 3.1/Unified API)

P2P.org DVT Staking Architecture Overview

Components and Roles

  • P2pOrgUnlimitedEthDepositor: This on-chain contract serves as the single entry-point for client ETH deposits into P2P.org’s staking system. It escrows all incoming ETH from users and holds it until P2P’s service performs the actual beacon chain deposit or issues a refund. In other words, it temporarily holds user ETH and then forwards it only to either the official Ethereum Beacon Deposit Contract (to create validators using the user’s withdrawal credentials) or back to the user if the service times out. This design ensures the user retains custody over withdrawal credentials (the deposit uses a client-provided withdrawal address) while P2P coordinates the validator setup off-chain.

  • P2pSsvProxyFactory: This is the main on-chain orchestration contract for P2P.org’s Distributed Validator Technology (SSV) staking. It acts as the entry point for validator registration and is responsible for deploying the necessary helper contracts per client. When a user initiates staking, the factory contract deploys a new P2pSsvProxy instance (a minimal proxy contract) for that user’s validators and also coordinates creation of a FeeDistributor contract for reward splitting. The factory batches and proxies the complex operations (beacon deposits and SSV cluster registration) so that users do not need to handle SSV tokens themselves. P2P.org’s contracts manage all required SSV token funding for validator registration while the user’s ETH stake remains under the user’s withdrawal credentials.

  • FeeDistributor: (Deployed via the factory) A per-client contract that handles reward distribution according to the configured splits. For example, a client’s FeeDistributor might be set to deliver 90% of rewards to the client, 6% to a referrer, and the remaining ~4% to P2P.org. The P2pOrgUnlimitedEthDepositor uses a FeeDistributorFactory to deploy this contract at a deterministic address when the user deposits ETH. The FeeDistributor’s address becomes part of the deposit’s identity and is used to tie the user’s deposit to their reward distribution scheme.

  • P2pSsvProxy: (Deployed via the factory) A lightweight proxy contract that becomes the owner of the validator cluster on the SSV Network. This contract receives SSV tokens from the factory and calls the SSV Network on-chain to register the new validators. Each user (or each deposit batch) gets its own proxy contract, which P2P’s infrastructure will manage as the cluster controller. The proxy holds the validator’s public key and encrypted key shares, and it interfaces with the SSV network contract to add the validator with the chosen operators. Importantly, by using this proxy, P2P can supply the needed SSV tokens on-chain and invoke SSV registerValidator calls, abstracting those steps away from the end-user.

Staking Process Flow

Diagram 1: User Deposit & Proxy Setup Flow

sequenceDiagram
    autonumber
    participant User
    participant P2pSsvProxyFactory
    participant P2pOrgUnlimitedEthDepositor
    participant FeeDistributorFactory
    participant FeeDistributor
    participant P2pSsvProxy

    User->>P2pSsvProxyFactory: addEth({ethAmount, withdrawalCreds, splits...})
    activate P2pSsvProxyFactory
    P2pSsvProxyFactory->>P2pOrgUnlimitedEthDepositor: addEth(...)
    activate P2pOrgUnlimitedEthDepositor
    P2pOrgUnlimitedEthDepositor->>FeeDistributorFactory: createIfAbsent(config)
    FeeDistributorFactory->>FeeDistributor: deploy new FeeDistributor
    FeeDistributor-->>P2pOrgUnlimitedEthDepositor: FeeDistributor address
    P2pOrgUnlimitedEthDepositor-->>P2pSsvProxyFactory: depositId
    deactivate P2pOrgUnlimitedEthDepositor

    P2pSsvProxyFactory->>P2pSsvProxy: deploy Proxy for user
    P2pSsvProxyFactory-->>User: emit EthForSsvStakingDeposited (proxy + depositId)
    deactivate P2pSsvProxyFactory

ETH is transferred from User → P2pOrgUnlimitedEthDepositor and held in escrow.
FeeDistributor and P2pSsvProxy are created and linked to the deposit.


Diagram 2: Operator-Driven Activation (Beacon + SSV Registration)

sequenceDiagram
    autonumber
    participant Operator (P2P.org)
    participant P2pSsvProxyFactory
    participant P2pOrgUnlimitedEthDepositor
    participant DepositContract (Beacon Chain)
    participant P2pSsvProxy
    participant SSVToken
    participant SSVNetwork

    Operator->>P2pSsvProxyFactory: makeBeaconDepositsAndRegisterValidators(depositId, validatorData)
    activate P2pSsvProxyFactory

    P2pSsvProxyFactory->>P2pOrgUnlimitedEthDepositor: makeBeaconDeposit(depositId, validatorData)
    activate P2pOrgUnlimitedEthDepositor
    loop For each validator
        P2pOrgUnlimitedEthDepositor->>DepositContract: deposit(pubkey, withdrawalCreds, signature, root) with 32 ETH
    end
    P2pOrgUnlimitedEthDepositor-->>P2pSsvProxyFactory: Eth2DepositCompleted
    deactivate P2pOrgUnlimitedEthDepositor

    P2pSsvProxyFactory->>SSVToken: transfer(to: P2pSsvProxy, amount)
    P2pSsvProxyFactory->>P2pSsvProxy: registerValidators(pubkeys, shares, operatorIds)
    activate P2pSsvProxy
    P2pSsvProxy->>SSVNetwork: registerValidator(...)
    deactivate P2pSsvProxy

    P2pSsvProxyFactory-->>Operator: emit RegistrationCompleted
    deactivate P2pSsvProxyFactory

Validators are deposited to the Beacon chain using escrowed ETH.
SSV tokens are transferred and used to register the validator on the SSV Network via the proxy.


1. User Deposit and Proxy Deployment: The process begins with the user initiating a stake by calling P2pSsvProxyFactory.addEth. The user includes their 32 ETH * n (or more) and specifies:

  • _eth2WithdrawalCredentials: the user’s withdrawal credentials (usually an 0x01 prefixed ETH1 address, meaning the user’s own address for withdrawals).
  • _ethAmountPerValidatorInWei: typically set to exactly 32 ETH (in wei) for each new validator.
  • _clientConfig and _referrerConfig: addresses and fee percentages for the reward split (e.g. 90% to client, 6% to referrer, 4% to P2P).
  • _extraData: any extra metadata (e.g. MEV relay info).

When addEth is called, the factory internally forwards the call (and ETH) to P2pOrgUnlimitedEthDepositor.addEth. The UnlimitedEthDepositor contract then:

  • Verifies the deposit parameters (e.g. correct withdrawal credential format and 32 ETH per validator).
  • Computes a unique depositId for this request based on the withdrawal credentials, ETH per validator, and the derived FeeDistributor address.
  • If needed, deploys the FeeDistributor contract for the given client/referrer configuration (via the FeeDistributorFactory).
  • Records the deposit details on-chain (amount and a 1-day expiration timestamp) and emits the event P2pOrgUnlimitedEthDepositor__ClientEthAdded with the depositId and deposit info.

Meanwhile, the P2pSsvProxyFactory completes the addEth call by deploying a new P2pSsvProxy contract instance for the deposit (using a create2 clone, so its address is pre-computable) and emitting its own event P2pSsvProxyFactory__EthForSsvStakingDeposited. At this point, the user’s ETH is held securely in the UnlimitedEthDepositor contract (tagged by depositId) and their proxy+feeDistributor have been set up on-chain, but the 32 ETH validator deposits have not yet been sent to the official beacon chain contract.

Note: After this step, the user’s part is done – the client does not need to do anything else. All subsequent actions are performed by P2P’s off-chain service using the data logged on-chain.

2. Off-Chain Data Generation: Once the addEth transaction is emitted, P2P’s infrastructure picks up the events to identify the new deposit. Off-chain, P2P generates the necessary validator keys and deposit data:

  • P2P picks a set of SSV operators (from an allowed list) and uses its Distributed Validator (DVT/SSV) tools to split the new validator’s private key into encrypted shares for those operators.
  • P2P also constructs the official ETH2 deposit data for each validator: this includes the validator pubkey, a signature, and the deposit data root needed by the Ethereum deposit contract.
  • These pieces (pubkey, key shares, deposit signature/root) are prepared for each validator in the batch. For example, if the user deposited enough ETH for 3 validators, P2P will generate 3 sets of key data.
  • Throughout this, the user’s withdrawal address (provided in step 1) is embedded in the deposit data, meaning the user remains the withdrawer of the validators’ funds.

P2P correlates the generated data with the on-chain depositId and the proxy address from the earlier events. The depositId ties together the user’s ETH and the FeeDistributor/proxy; P2P uses it to ensure the deposit parameters (ETH per validator, etc.) match the data it generated.

3. Beacon Deposit and Validator Registration: P2P then initiates the on-chain activation of the validators by calling the factory’s administrative function makeBeaconDepositsAndRegisterValidators (accessible only to P2P’s operator account). This is a single atomic transaction that performs two main tasks:

  • (a) Beacon chain deposits: The factory calls P2pOrgUnlimitedEthDepositor.makeBeaconDeposit to transfer the staked ETH from the escrow contract into the official Beacon Deposit Contract. P2pOrgUnlimitedEthDepositor uses the depositId to look up the stored deposit record and then processes the batch:

    • It ensures the deposit is in the expected state (not already used or refunded) and that there is enough ETH for the number of validators (e.g. deposit contract will receive 32 ETH for each pubkey in the list).
    • It updates the stored record: deducting the amount for the validators being staked and marking any remainder. Typically, if the user provided exactly 32 ETH * N, the record will drop to zero and be deleted (emitting a Eth2DepositCompleted event). If there were any excess ETH beyond whole validators (unlikely in normal use before partial staking is enabled), the contract would mark the deposit as partially used and keep the rest for future use or refund, emitting Eth2DepositInProgress.
    • The contract then loops through each validator in the batch and calls the official DepositContract’s deposit() function, passing in one validator’s pubkey, the user’s withdrawal credentials (as bytes), the signature, and deposit data root, with 32 ETH attached for each call. This effectively creates the validators on the beacon chain, consuming the 32 ETH per validator from the escrow. After completing the loop, an event P2pOrgUnlimitedEthDepositor__Eth2Deposit is emitted, recording the depositId and the count of validators deposited in this batch.
  • (b) SSV validator registration: Immediately after initiating the beacon deposits, the factory finalizes the SSV cluster setup on the user’s behalf. It transfers the required amount of SSV tokens (the network’s native token used to pay operator fees) from P2P’s token reserves to the user’s newly deployed P2pSsvProxy contract. Then, the factory invokes the proxy’s function (e.g. bulkRegisterValidators) to register the validator(s) with the SSV Network, providing the list of validator pubkeys, the chosen operator IDs, and the encrypted shares for each validator. The P2pSsvProxy as the cluster owner calls the SSV network contract to officially register the validators with the given operators and deposits the SSV tokens for initial operation. This step links the validator(s) to a distributed cluster controlled by the P2pSsvProxy. Once done, the factory emits P2pSsvProxyFactory__RegistrationCompleted, signaling that the end-to-end setup (deposit + SSV registration) is finished for that proxy.

At the end of step 3, the user’s ETH has been converted into active validator stakes on the beacon chain, and those validators are running under P2P.org’s distributed validator setup (SSV) without the user having had to manage keys or SSV tokens. The user’s withdrawal address remains the recipient of any eventual withdrawals from those validators, while the FeeDistributor ensures that execution-layer rewards (and any configured fee cuts) are properly routed. For example, if using an ElOnlyFeeDistributor, the validator client is likely configured to send transaction fees/MEV to the FeeDistributor contract, which will then split those rewards per the client/referrer/P2P percentages configured. (In other configurations, the FeeDistributor could also be the withdrawal credential itself, or use an oracle to split consensus rewards – but regardless, it intermediates reward sharing off-chain.)

4. Timeout and Refund Safety: The system has a built-in safety mechanism in case P2P fails to perform step 3 in a timely manner. When the user’s ETH was first added, an expiration (typically 1 day) was recorded for that deposit batch. If for any reason P2P does not complete the beacon deposit within that window, the user can call the refund function on P2pOrgUnlimitedEthDepositor to retrieve their ETH back on-chain. The refund can only be invoked by the original client (it checks that the caller matches the stored client address from the FeeDistributor) and only after the timeout has passed. Upon refund, the contract pays out the entire remaining deposit amount to the user’s address and emits a P2pOrgUnlimitedEthDepositor__Refund event. This ensures that users are not at risk of indefinite fund lock-up – either P2P uses the ETH to stake within the agreed time or the user can reclaim it. There’s also an admin “rejectService” option that P2P can invoke (e.g. if a deposit cannot be serviced), which immediately enables the client to refund. In practice, under normal operation P2P will perform the deposits well within the window, and a Eth2DepositCompleted event signifies the escrowed ETH has been successfully staked.

Integration of UnlimitedEthDepositor and ProxyFactory

The interaction betweenP2pOrgUnlimitedEthDepositor and P2pSsvProxyFactory is central to the P2P.org staking flow. Summarizing their integration:

  • The ProxyFactory uses the UnlimitedEthDepositor as a backend service for handling ETH. The factory never holds the user’s ETH itself; instead it passes it to P2pOrgUnlimitedEthDepositor.addEth and gets back a depositId reference. This depositId is then included in the factory’s own event to correlate with the new proxy. Essentially, the factory “outsources” the ETH escrow and beacon deposit logic to the UnlimitedDepositor contract.

  • The UnlimitedEthDepositor contract, in turn, trusts calls only from the factory’s owner/operator. The critical call to actually perform deposits (makeBeaconDeposit) is restricted to P2P’s control and expects the factory (or a designated operator) to supply the correct validator data. This separation of concerns means the UnlimitedDepositor doesn’t need to know about SSV or proxies – it only manages ETH deposits and refunds – while the ProxyFactory doesn’t handle raw ETH movements to the beacon chain (reducing its complexity and risk).

  • Data flow: after a user calls addEth, both contracts emit events that P2P’s off-chain system listens to. The P2pOrgUnlimitedEthDepositor__ClientEthAdded event includes the depositId, amount, and user metadata. The P2pSsvProxyFactory__EthForSsvStakingDeposited event includes the same depositId plus the new proxy address and fee distributor address. Using these, the off-chain system can link the user’s ETH deposit to the deployed proxy and proceed with key generation.

  • Finally, when P2P calls makeBeaconDepositsAndRegisterValidators, the factory drives both contracts in one go: it invokes the UnlimitedDepositor to move ETH into validators and then calls functions on the new P2pSsvProxy to register those validators. The successful completion results in events from both: Eth2Deposit from the depositor (showing how many validators were created) and RegistrationCompleted from the factory, tying to the proxy address.

In summary, P2pOrgUnlimitedEthDepositorandP2pSsvProxyFactory work in tandem: the depositor contract guarantees the secure handling of the user’s ETH (with the ability to refund) and performs the low-level beacon deposits, while the factory orchestrates the overall workflow – from taking the user’s request to deploying the needed contracts and finally registering the validators with the SSV network. This architecture allows P2P.org to offer a one-stop staking service where the user contributes only ETH and a withdrawal address and P2P’s system takes care of validator creation, distributed key management, and operator registration behind the scenes. The result is a seamless staking process that leverages smart contracts for security and transparency (fund custody and on-chain actions) while relying on off-chain coordination for the heavy-lifting of key generation and SSV cluster setup.


Smart contract deployments

P2pOrgUnlimitedEthDepositor (direct)

Mainnet 0x23BE839a14cEc3D6D716D904f09368Bbf9c750eb

Hoodi 0x933AcC6f337489D78188CBef2141cD9D6466d07D

Github

Audit


P2pOrgUnlimitedEthDepositor (SSV)

Mainnet 0x7c58c03AABf3Cc131903592B40C192BB4F949f1F

Hoodi 0xd01bF2297a153cE00B64972ffB6e7B767Bf0b285

Github

Audit


P2pSsvProxyFactory

Mainnet 0xcb924D4BE3Ff04B2d2116fE116138950373111d9

Hoodi 0x1f72FC2585D283DfEcF748cc5d19c014158A7C6f

Github

Audit