Validator Staking: A Quick Start
Customer Staking Integration Guide
In this guide, you'll learn how to integrate customer staking into your website to allow your customers to stake their validators on Ethereum. It covers all the essentials to get you up to speed in just a few minutes.
ImportantThis staking process is designed for institutions that want to offer non-custodial staking services to their customers. With this system, customers maintain control of their own wallets and funds. This solution is ideal if you're an institution looking to build your own staking offering using our Enterprise Staking product as a foundation.
What's a Validator?
Validators are the heart of the Ethereum network. Before you proceed with staking, it's essential to understand their role.
A validator is an entity that participates in the consensus layer by proposing and validating new blocks on the blockchain. Each validator must stake a minimum of 32 Ether (up to a maximum of 2048 Ether) as collateral to ensure honest behavior. Validators play a crucial role in maintaining the network's security and integrity. In return, they earn rewards in the form of Ether.
However, validators that misbehave can face penalties, including slashing, which can result in a significant loss of staked Ether.
Post-Pectra Staking Changes
The Pectra upgrade introduced a major change to Ethereum staking: withdrawal credentials now support two formats that define how rewards are handled:
- 0x01 (Classic Staking) → Rewards are withdrawn to a fixed payout address.
- 0x02 (Compounding Staking) → Rewards are restaked automatically, increasing the validator's balance.
This provides your customers with more flexibility: either opt for predictable payouts or maximize compounding yield for long-term growth.
0x01 vs 0x02 Staking
Type | Prefix | Behavior | Benefits | Best For |
---|---|---|---|---|
0x01 | 0x01 | Rewards and principal are sent to a withdrawal address. | Liquidity: customers can receive rewards regularly and move them. | Customers that need predictable ETH flow, regular access to rewards. |
0x02 | 0x02 | Rewards are compounded by being added directly to the validator's balance. | Maximized long-term returns through automatic compounding. | Long-term holders, customers who don't need regular payouts and want to grow validator balance. |
Rule of Thumb:
- Use V1 API if your customers need liquidity and frequent access to staking rewards.
- Use V2 API if your customers want to maximize yield by compounding rewards into the validator balance.
Check Remaining Validator Public Keys
While the stakefish API simplifies the staking process to a single API call followed by a blockchain transaction, it's essential to verify that your organization has enough allocated public keys. By performing this check, you ensure that your staking operations are carried out efficiently and within the limits of your allocated resources.
Use the following API to check how many public keys are currently available for your organization:
curl -X GET "https://api.hoodi.stake.fish/v1/eth/stake/v1/remaining-keys" \
-H "Authorization: YOUR_PUBLIC_API_KEY"
ImportantThis API requires Public API Key.
This endpoint returns the number of remaining public keys as text/plain
regardless of your requested response type. For example, a response of 716
indicates that 716 public keys are available for staking.
Verify Wallet Ownership
Your customer must confirm they have control over their wallet and enough Ether balance to stake validators. To do so, they will need to sign a message using their wallet.
Generate Message
To generate a message for your customer to sign, choose between v1 and v2 APIs based on the desired staking type:
V1 API - 0x01 (Classic Staking)
For traditional staking where rewards are withdrawn to a fixed address:
curl -X GET "https://api.hoodi.stake.fish/v1/eth/stake/v1/deposit-challenge?count=VALIDATOR_COUNT&address=DEPOSITOR_WALLET_ADDRESS" \
-H "Content-Type: application/json" \
-H "Authorization: YOUR_PUBLIC_API_KEY"
Explanation of Fields:
count
: Number of validators (each will be exactly 32 ETH)address
: The wallet address from which Ether will be deposited
V2 API - 0x02 (Compounding Staking)
For compounding staking with flexible deposit amounts:
curl -X GET "https://api.hoodi.stake.fish/v1/eth/stake/v2/deposit-challenge?amounts=32&amounts=64&amounts=48&address=DEPOSITOR_WALLET_ADDRESS" \
-H "Content-Type: application/json" \
-H "Authorization: YOUR_PUBLIC_API_KEY"
Explanation of Fields:
amounts
: Deposit amounts in Etheraddress
: The wallet address from which Ether will be deposited
ImportantBoth APIs require Public API Key.
Important Notes:
- V1 API: Always creates 0x01 validators with exactly 32 ETH each
- V2 API: Always creates 0x02 validators with flexible amounts (32 ETH to 2048 ETH)
Example Responses:
V1 API Response
{
"challenge": "I confirm I would like to stake 3 validator(s) and this request is valid until 1725621178."
}
V2 API Response
{
"challenge": "I confirm I would like to deposit [32.0, 64.0, 48.0] ETH for staking and this request is valid until 1725621178."
}
This is the message your customers will need to sign before staking their validators. Note that the message has an expiration time of 5 minutes (or 1 hour for smart contract wallets).
Customer Signs Message
Once you generated the message for your customer, they need to sign it with their wallet.
import { createWalletClient, custom } from 'viem'
import { hoodi } from 'viem/chains'
export const walletClient = createWalletClient({
chain: hoodi,
transport: custom(window.ethereum!),
})
export const [account] = await walletClient.getAddresses()
const signature = await walletClient.signMessage({
account,
message: "I confirm I would like to deposit [32.0, 64.0, 48.0] ETH for staking and this request is valid until 1725621178.",
})
// Example output: "0xa461f509887bd19e312c0c58467ce8ff8e300d3c1a90b608a760c5b80318eaf15fe57c96f9175d6cd4daad4663763baa7e78836e067d0163e9a2ccf2ff753f5b1b"
In this example, we used library called viem
.
This will return a signature that will be used in the staking request.
Customer's Browser
If your customer uses the MetaMask browser extension, they will see the following popup. They need to click the "Sign" button.
Create Validator
After obtaining the wallet signature and confirming that you have enough allocated public keys, the next step is to prepare validators for staking. Choose between v1 and v2 APIs based on your staking preference:
V1 API - 0x01 (Classic Staking)
For traditional staking with fixed 32 ETH deposits:
curl -X POST "https://api.hoodi.stake.fish/v1/eth/stake/v1/create-validators" \
-H "Content-Type: application/json" \
-H "Authorization: YOUR_PUBLIC_API_KEY" \
--data '{
"count": 3,
"depositor": "DEPOSITOR_WALLET_ADDRESS",
"withdrawalCredentials": "DEPOSITOR_WALLET_ADDRESS",
"message": "I confirm I would like to stake 3 validator(s) and this request is valid until 1725621178.",
"signature": "0xa461f509887bd19e312c0c58467ce8ff8e300d3c1a90b608a760c5b80318eaf15fe57c96f9175d6cd4daad4663763baa7e78836e067d0163e9a2ccf2ff753f5b1b"
}'
V2 API - 0x02 (Compounding Staking)
For compounding staking with flexible deposit amounts:
curl -X POST "https://api.hoodi.stake.fish/v1/eth/stake/v2/create-validators" \
-H "Content-Type: application/json" \
-H "Authorization: YOUR_PUBLIC_API_KEY" \
--data '{
"amounts": ["32", "64", "48"],
"depositor": "DEPOSITOR_WALLET_ADDRESS",
"withdrawalCredentials": "DEPOSITOR_WALLET_ADDRESS",
"message": "I confirm I would like to deposit [32.0, 64.0, 48.0] ETH for staking and this request is valid until 1725621178.",
"signature": "0xa461f509887bd19e312c0c58467ce8ff8e300d3c1a90b608a760c5b80318eaf15fe57c96f9175d6cd4daad4663763baa7e78836e067d0163e9a2ccf2ff753f5b1b"
}'
ImportantBoth APIs require Public API Key.
Key Differences:
- V1 API: Always creates 0x01 validators with exactly 32 ETH each
- V2 API: Always creates 0x02 validators with flexible amounts (32 ETH to 2048 ETH)
V1 API Fields:
count
: Number of validators to create (each will be exactly 32 ETH)depositor
: The address from which Ether will be depositedwithdrawalCredentials
: The withdrawal address (will be formatted as 0x01)message
: The signed message from the previous stepsignature
: The wallet signature
V2 API Fields:
amounts
: Array of deposit amounts in gwei (flexible amounts from 32 ETH to 2048 ETH)depositor
: The address from which Ether will be depositedwithdrawalCredentials
: The withdrawal address (will be formatted as 0x02)message
: The signed message from the previous stepsignature
: The wallet signature
Example Responses:
V1 API Response (0x01 - Classic Staking)
{
"pubkeys": "0xb078b58f3801a6afe94ba4233dd96a6ad61f56c4c3e8be69c4a89820efe14db47dc53ea84ae8751e2493a9efdfcd7d28b22447d996887afec5631410abd417de030e659c6d6c33d99af5db930f50a05f7f983bbb52778dae1244484469b8d675",
"withdrawalCredentials": "0x0100000000000000000000001ec7fa23a8468f1f3a135bfefe6d678e1657ee650100000000000000000000001ec7fa23a8468f1f3a135bfefe6d678e1657ee650100000000000000000000001ec7fa23a8468f1f3a135bfefe6d678e1657ee65",
"signatures": "0x89a39b1e8c9488ba1f65af7f0f6fc50641bb2b50e7cb7133729fbc1ff1fa1c25c169a35a685c5ce9d53db42807979ef61689e860fa3001ff9d475c7171fa19fd96717ad3089f3006139076282d87fff1703db57b56934a055207fe8e9978a12a",
"depositDataRoots": [
"0xc01bd131d4307686b280a1d4631550c1d81d09d386582980ac29a657ade2521e",
"0xd12ce242e5418797c391b5d1e2e6c061e82d0ae3974920c0ba40b768bef3632f",
"0xe23df353f6529808d4a2c6d2f3f7d172f93e1bf4085a31d1cb51c879cff4743g"
]
}
V2 API Response (0x02 - Compounding Staking)
{
"pubkeys": "0xc075d448c3c96cd9c7297b2e6a3f4d59b5c2a9e8f1e6d8c4b9a7f5e3d1c9b7ab22447d996887afec5631410abd417de030e659c6d6c33d99af5db930f50a05f7f983bbb52778dae1244484469b8d675a123e456f789a012b345c678d901e234f",
"withdrawalCredentials": "0x0200000000000000000000001ec7fa23a8468f1f3a135bfefe6d678e1657ee650200000000000000000000001ec7fa23a8468f1f3a135bfefe6d678e1657ee650200000000000000000000001ec7fa23a8468f1f3a135bfefe6d678e1657ee65",
"signatures": "0x95c2a9e8f1e6d8c4b9a7f5e3d1c9b7a3f4d59b5c2a9e8f1e6d8c4b9a7f5e3d1c9b7ac8f6e4d2b0a8f6e4d2b0a8f6e4d2b0a8f6e4d2b0a8f6e4d2b0a8f6e4d2b0567d890e123f456a789b012c345d678e901f234a567b890c123d456e789f012",
"depositDataRoots": [
"0xa45fe789a012b345c678d901e234f567a890b123c456d789e012f345a678b901",
"0xb56gf890b123c456d789e012f345a678b901c234d567e890f123a456b789c012",
"0xc67ha901c234d567e890f123a456b789c012d345e678f901a234b567c890d123"
]
}
Note the difference in withdrawal credentials:
- V1 API: All withdrawal credentials start with
0x01
(classic staking) - V2 API: All withdrawal credentials start with
0x02
(compounding staking)
Deposit Validator
Once the validators are prepared and you've generated the necessary deposit details, the final step is to deposit your validators on Ethereum. This process involves sending a deposit transaction on Ethereum.
Encode the Batch Deposit Calldata:
Use the encodeBatchDepositCalldata
function to encode the public keys, withdrawal credentials, signatures, and deposit data roots into a single calldata string. This calldata will be sent to the batch deposit contract.
Send the Transaction
Using the walletClient
, send a transaction to the batch deposit contract address. The transaction should include the encoded calldata, the recipient address (batch deposit contract), and the total value (sum of all Ether amounts being staked).
Check the Transaction Status
After sending the transaction, you'll receive a transaction hash. Use this hash to monitor the transaction status on Etherscan or directly via viem
library.
Here's the completed code snippet:
import { createWalletClient, custom } from 'viem'
import { hoodi } from 'viem/chains'
export const walletClient = createWalletClient({
chain: hoodi,
transport: custom(window.ethereum!),
})
export const [account] = await walletClient.getAddresses()
function encodeBatchDepositCalldata(pubkeys, withdrawalCredentials, signatures, depositDataRoots) {
return viem.encodeFunctionData({
abi: [viem.parseAbiItem('function batchDeposit(bytes,bytes,bytes,bytes32[])')],
functionName: 'batchDeposit',
args: [pubkeys, withdrawalCredentials, signatures, depositDataRoots],
})
}
// This is obtained from the create-validators endpoint
const depositPayload = {
pubkeys: '0xc075d448c3c96cd9c7297b2e6a3f4d59b5c2a9e8f1e6d8c4b9a7f5e3d1c9b7ab22447d996887afec5631410abd417de030e659c6d6c33d99af5db930f50a05f7f983bbb52778dae1244484469b8d675a123e456f789a012b345c678d901e234f',
withdrawalCredentials: '0x0200000000000000000000001ec7fa23a8468f1f3a135bfefe6d678e1657ee650200000000000000000000001ec7fa23a8468f1f3a135bfefe6d678e1657ee650200000000000000000000001ec7fa23a8468f1f3a135bfefe6d678e1657ee65',
signatures: '0x95c2a9e8f1e6d8c4b9a7f5e3d1c9b7a3f4d59b5c2a9e8f1e6d8c4b9a7f5e3d1c9b7ac8f6e4d2b0a8f6e4d2b0a8f6e4d2b0a8f6e4d2b0a8f6e4d2b0a8f6e4d2b0567d890e123f456a789b012c345d678e901f234a567b890c123d456e789f012',
depositDataRoots: [
'0xa45fe789a012b345c678d901e234f567a890b123c456d789e012f345a678b901',
'0xb56gf890b123c456d789e012f345a678b901c234d567e890f123a456b789c012',
'0xc67ha901c234d567e890f123a456b789c012d345e678f901a234b567c890d123'
]
}
const calldata = encodeBatchDepositCalldata(
depositPayload.pubkeys,
depositPayload.withdrawalCredentials,
depositPayload.signatures,
depositPayload.depositDataRoots,
)
// Calculate total value: 32 + 64 + 48 = 144 ETH (for V2 example)
// For V1: 32 + 32 + 32 = 96 ETH (3 validators × 32 ETH each)
const totalValue = 32n + 64n + 48n // ETH amounts for V2 example
const valueInWei = totalValue * (10n ** 18n)
const txHash = await walletClient.sendTransaction({
data: calldata,
to: BATCH_DEPOSIT_ADDRESS,
value: valueInWei,
type: 'eip1559',
})
console.log("Transaction Hash: ", txHash);
Important Notes:
BATCH_DEPOSIT_ADDRESS
: This is the contract address used to perform batch deposits of validators. You'll receive this address from stakefish once your account is set up.- The
value
field must be the total sum of all deposit amounts in wei. - V1 example: 3 validators × 32 ETH = 96 ETH total
- V2 example: 32 ETH + 64 ETH + 48 ETH = 144 ETH total
If your customer uses MetaMask browser extension they will see the following popup. They will need to confirm the transaction:
Final Steps
After sending the transaction, your Ethereum client will return a transaction hash. You can use this hash to monitor the transaction's progress on Hoodi Etherscan or your preferred block explorer.
Validator Activation: Once the transaction is confirmed, your validators are in the process of being activated. The deposit typically takes between 16 to 24 hours to be processed by the consensus layer. After processing, your validators will be queued for activation, which can take anywhere from a few minutes to several days, depending on the current validator queue on Ethereum.
Different Staking Behaviors:
- V1 validators (0x01): Send rewards and principal to the withdrawal address when exited
- V2 validators (0x02): Compound rewards automatically, growing their balances over time
✅ Your customers have successfully staked their Ethereum validators using stakefish API. Choose V1 for traditional staking with predictable payouts, or V2 for compounding staking with flexible amounts.
Updated 5 days ago