New Validator Creation
Objective
Enable creation of new validators with flexible sizing — from 32 ETH up to 1920 ETH — using 0x01
withdrawal credentials via the standard flow, or 0x02
credentials via the simplified smart contract flow.
Top-ups support amounts starting from 1 gwei, but new validator creation requires at least 32 ETH.
Top-ups require
0x02
withdrawal credentials.
Standard Flow (Multi-step)
Applicable scenarios
- You need fine-grained control over validator count, size per validator, and withdrawal credential type
- You intend to manually broadcast deposit transactions
Endpoints (Ethereum)
POST
/eth/staking/direct/nodes-request/create.GET
/eth/staking/direct/nodes-request/status/{id}POST
/eth/staking/direct/tx/deposit
Endpoints (SSV)
Flow
- Create a validator request by specifying the number of validators and optional parameters like
amountPerValidator
andwithdrawalCredentialsType
. - Retrieve deposit data by polling the request status.
- Broadcast deposit transaction manually.
Behavior
- If
amountPerValidator > 32 ETH
, the system defaults to0x02
withdrawal credentials - You can explicitly set withdrawalCredentialsType to
0x01
or0x02
. For0x02
withdrawal credentials, the standard flow ends after deposit data retrieval. Deposit broadcasting and validator activation are not supported for0x02
in this flow (both in Ethereum and SSV). - The status response includes validator size, credential type, and
depositData[]
- SSV flow supports both
0x01
and0x02
credentials, but for0x02
, it only supports data generation — not broadcasting or activation.
Flow Example
1. Create Validator Request
Send a POST
request to /api/v1/eth/staking/direct/nodes-request/create.
Example request:
curl --request POST \
--url https://api.p2p.org/api/v1/eth/staking/direct/nodes-request/create \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <token>' \
--data '{
"id": "f79ad9fe-6cfc-4480-ba80-683c26914971",
"type": "REGULAR",
"validatorsCount": 2,
"withdrawalAddress": "0x368F823e4df...bf8e4a21",
"controllerAddress": "0x368F823e4df...bf8e4a21",
"feeRecipientAddress": "0x368F823e4df...bf8e4a21",
"nodesOptions": {
"location": "any"
}
}'
Example response:
{
"error": null,
"result": true
}
2. Poll Status to Get Deposit Data
Send a GET
request to /api/v1/eth/staking/direct/nodes-request/status/{id}.
Example request:
curl --request GET \
--url https://api.p2p.org/api/v1/eth/staking/direct/nodes-request/status/f79ad9fe-6cfc-4480-ba80-683c26914971 \
--header 'Authorization: Bearer <token>'
Example response:
{
"error": null,
"result": {
"status": "ready",
"amountPerValidator": "32000000000",
"withdrawalCredentialsType": "0x01",
"depositData": [
{
"pubkey": "0xa6ecac19d...",
"signature": "0xb2a...",
"withdrawalCredentials": "0100000000...bf8e4a21",
"amount": 32000000000,
"depositDataRoot": "0x75348c59...",
"depositMessageRoot": "0x3179df...",
"forkVersion": "10000910",
"eth2NetworkName": "hoodi",
"depositCliVersion": "2.7.0"
},
{
"pubkey": "0xb63e7e5c32f...",
"signature": "0xa0b4...",
"withdrawalCredentials": "0100000000...bf8e4a21",
"amount": 32000000000,
"depositDataRoot": "0x5177528...",
"depositMessageRoot": "0x25c35250...",
"forkVersion": "10000910",
"eth2NetworkName": "hoodi",
"depositCliVersion": "2.7.0"
}
]
}
}
amountPerValidator
— amount in GWEI. (32 ETH = 32_000_000_000 GWEI)
3. Broadcast Deposit Transaction
The following broadcast example is only applicable for validators with
0x01
withdrawal credentials.
Send a POST
request to /api/v1/eth/staking/direct/tx/deposit.
Example request:
curl --request POST \
--url https://api.p2p.org/api/v1/eth/staking/direct/tx/deposit \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <token>' \
--data '{
"withdrawalAddress": "0x368F823e4df...689917bf8e4a21",
"depositData": [
{
"pubkey": "0xa6ecac19d...",
"signature": "0xb2a...",
"withdrawalCredentials": "0100000000...bf8e4a21",
"amount": 32000000000,
"depositMessageRoot": "0x3179df...",
"depositDataRoot": "0x75348c59...",
"forkVersion": "10000910",
"eth2NetworkName": "hoodi",
"depositCliVersion": "2.7.0"
},
{
"pubkey": "0xb63e7e5c32f...",
"signature": "0xa0b4...",
"withdrawalCredentials": "0100000000...bf8e4a21",
"amount": 32000000000,
"depositMessageRoot": "0x25c35250...",
"depositDataRoot": "0x5177528...",
"forkVersion": "10000910",
"eth2NetworkName": "hoodi",
"depositCliVersion": "2.7.0"
}
]
}'
Example response:
{
"error": null,
"result": {
"serializeTx": "0x02f904...",
"value": "64000000000000000000",
"chainId": 560048,
"type": 2,
"maxFeePerGas": "2221240288",
"maxPriorityFeePerGas": "79693564"
}
}
Legacy SSV Note
Deposit broadcasting is not supported for
0x02
credentials via legacy SSV flow.Only deposit data generation is available at this time.
Simplified Flow (via Smart Contract 3.1)
This flow applies only to Ethereum Direct staking via smart contract 3.1.
The example below describes a legacy SSV flow and will be updated once SSV 3.1 integration is available.
Applicable scenarios
- You want to minimize steps and create a validator using a prebuilt transaction
- You intend to use
0x02
withdrawal credentials and avoid manual deposit data handling
Endpoints
- Ethereum:
GET
/eth/staking/direct/p2p/deposit
Flow
- Call the endpoint with query parameters:
amount
,withdrawalAddress
,controllerAddress
,feeRecipientAddress
- Receive a fully constructed transaction ready for signing and broadcasting.
This flow is supported for Ethereum Direct staking only and applies exclusively to validators using
0x02
withdrawal credentials.
Flow Example
1. Get Prebuilt Transaction (Ethereum Direct 3.1)
Example request:
curl --request GET \
--url "https://api.p2p.org/api/v1/eth/staking/direct/p2p/deposit?amount=32&withdrawalAddress=0x8eB2277e54A2A7db9b7c0D9FA9B78dcB35f74866&controllerAddress=0x8eB2277e54A2A7db9b7c0D9FA9B78dcB35f74866&feeRecipientAddress=0x3202B5B0602274197CcB22B8c02CEC9539990c7c" \
--header 'Authorization: Bearer <token>'
Example response:
{
"error": null,
"result": {
"serializeTx": "0x02f9025701808312c36c850727b59762839896809423be839a14cec3d6d716d904f09368bbf9c750eb8903782dace9d9000000b90224a49b131b0200000000000000000000008eb2277e54a2a7db9b7c0d9fa9b78dcb35f74866000000000000000000000000000000000000000000000001bc16d674ec8000000000000000000000000000003fcd8d9acac042095dfba53f4c40c74d19e2e9d9000000000000000000000000000000000000000000000000000000000000251c0000000000000000000000003202b5b0602274197ccb22b8c02cec9539990c7c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000e86b78000ed3b7b820ac6a39b46602356daee0602180fefa69a94774c3209a9f5ad1df42aa2c426b1494a39afc4ee79a528dca95807f5a1b518c8fb0a4df1be92b8a8b6e5f4fccf35f3975f9cf64aa92f4896f2553a5ba9fb0b13cabb25f21a2881f027d9703bfa57f45a514fff7bcc1b8020c2a3ab0581cfbb748dfe612a14acef40bc61de068cf3117e8227ab1315e0302f97e17d5b8948aa8071645b27ad74f669783d363fbc7e9dd58c01e535aff418b2fdbff56a55212e25efbc6b963bb048d98ecc1d03020f890bbf7518fd2463afb82ffdf452863268bdae4eae18d6108dce9ef803669f466000000000000000000000000000000000000000000000000c0",
"to": "0x23BE839a14cEc3D6D716D904f09368Bbf9c750eb",
"gasLimit": "10000000",
"data": "0xa49b131b0200000000000000000000008eb2277e54a2a7db9b7c0d9fa9b78dcb35f74866000000000000000000000000000000000000000000000001bc16d674ec8000000000000000000000000000003fcd8d9acac042095dfba53f4c40c74d19e2e9d9000000000000000000000000000000000000000000000000000000000000251c0000000000000000000000003202b5b0602274197ccb22b8c02cec9539990c7c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000e86b78000ed3b7b820ac6a39b46602356daee0602180fefa69a94774c3209a9f5ad1df42aa2c426b1494a39afc4ee79a528dca95807f5a1b518c8fb0a4df1be92b8a8b6e5f4fccf35f3975f9cf64aa92f4896f2553a5ba9fb0b13cabb25f21a2881f027d9703bfa57f45a514fff7bcc1b8020c2a3ab0581cfbb748dfe612a14acef40bc61de068cf3117e8227ab1315e0302f97e17d5b8948aa8071645b27ad74f669783d363fbc7e9dd58c01e535aff418b2fdbff56a55212e25efbc6b963bb048d98ecc1d03020f890bbf7518fd2463afb82ffdf452863268bdae4eae18d6108dce9ef803669f466000000000000000000000000000000000000000000000000",
"value": "64000000000000000000",
"chainId": 1,
"type": 2,
"maxFeePerGas": "30730983266",
"maxPriorityFeePerGas": "1229676"
}
}
amount is in ETH (e.g., "32")
2. Sign and Broadcast transaction
Use the following example of the code to sign and broadcast the withdrawal transaction.
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");
});
Updated about 23 hours ago