Coinbase Staking API support for Ethereum Pectra features is in Beta on the Ethereum Hoodi testnet. Try it out and share feedback on Discord.
Coinbase Staking API has added the following powerful features with Ethereum’s Pectra upgrade:
  1. Provision larger validators with Max Effective Balance up to 2048 ETH and reward auto-compounding
  2. Initiate partial withdrawals or full exits via execution layer credentials
  3. Consolidate smaller pre Pectra validators to larger post Pectra validators
SDK Availability:
  • Go SDK: Pectra features available starting version v0.0.24
  • Node.js SDK: Pectra features available starting version v0.23.0
If you’re new to Coinbase Staking API, see the quickstart to familiarize yourself with basic usage. The remainder of this guide covers how to use the new features introduced with the Pectra upgrade.

Stake

To stake, you can now specify whether you’re creating a post Pectra or pre Pectra validator. With Pectra, the minimum stake amount is still 32 ETH while the maximum has been increased to 2048 ETH. Ensure that the address contains the staking amount plus additional ETH to cover transaction fees. The example below illustrates how to stake from an external address.
Dedicated ETH Staking can take up to 5 minutes to generate a staking transaction, as it involves provisioning dedicated backend infrastructure. Until it’s ready, the Transaction field in the StakeOperation will remain empty.
import { Coinbase, ExternalAddress, StakeOptionsMode } from "@coinbase/coinbase-sdk";

// Create a new external address on the ethereum-hoodi testnet network.
let address = new ExternalAddress(
    Coinbase.networks.EthereumHoodi,
    "YOUR_WALLET_ADDRESS",
);

// Find out how much ETH is available to stake.
let stakeableBalance = await address.stakeableBalance(
    Coinbase.assets.Eth,
    StakeOptionsMode.NATIVE,
);

console.log("Stakeable balance: %s", stakeableBalance)

// Build a stake operation for an amount <= stakeableBalance.
// Post Pectra this amount can be >=32 and <=2048 ETH. In this case, 100 ETH.
// This step triggers the provisioning of a single post Pectra validator.
// Select 0x02 for post Pectra validators or 0x01 for pre Pectra validators.
let stakingOperation = await address.buildStakeOperation(
    100,
    Coinbase.assets.Eth,
    StakeOptionsMode.NATIVE,
    {"withdrawal_credential_type": "0x02"},
);

console.log("Staking Operation ID: %s", stakingOperation.getID())

// Native ETH staking involves setting up infrastructure, which can take time.
// Example of polling the stake operation status until it reaches a terminal
// state using the SDK.
await stakingOperation.wait();
Once the stake operation has been built, relay the transactions to your end-user for signing and broadcasting. Refer to the Signing and Broadcasting Transactions section for an example using Ethers.js.

Unstake (via Execution Layer)

Post Pectra, validators can be unstaked from the execution layer directly via the withdrawal address connected to the validator without having to go through the consensus layer. Both partial withdrawals and full exits are supported.

Partial Withdrawals

import {
    Coinbase,
    ExternalAddress,
    StakeOptionsMode,
    ExecutionLayerWithdrawalOptionsBuilder,
} from "@coinbase/coinbase-sdk";
import Decimal from "decimal.js";

// Create a new external address on the ethereum-hoodi testnet network
// corresponding to the withdrawal address of the validators you want
// to partially withdraw from.
let withdrawalAddress = new ExternalAddress(
    Coinbase.networks.EthereumHoodi,
    "YOUR_WITHDRAWAL_ADDRESS",
);

// Build a partial withdrawal staking operation to withdraw 1 ETH and 2 ETH
// from 2 different post Pectra validators.

let builder = new ExecutionLayerWithdrawalOptionsBuilder(withdrawalAddress.getNetworkId());
builder.addValidatorWithdrawal("YOUR_VALIDATOR_PUBKEY_1", new Decimal("1"));
builder.addValidatorWithdrawal("YOUR_VALIDATOR_PUBKEY_2", new Decimal("2"));

let options = await builder.build();

let stakingOperation = await withdrawalAddress.buildUnstakeOperation(
    0, // Amount here doesn't matter.
    Coinbase.assets.Eth,
    StakeOptionsMode.NATIVE,
    options,
);

console.log("Staking Operation ID: %s", stakingOperation.getID())

// Wait for the partial withdrawal transactions to be built.
await stakingOperation.wait();
Once the full exit staking operation has been built, relay the transactions to your end-user for signing and broadcasting. Refer to the Signing and Broadcasting Transactions section for an example using Ethers.js.

Full Exits

import {
    Coinbase,
    ExternalAddress,
    StakeOptionsMode,
    ExecutionLayerWithdrawalOptionsBuilder,
} from "@coinbase/coinbase-sdk";
import Decimal from "decimal.js";

// Create a new external address on the ethereum-hoodi testnet network
// corresponding to the withdrawal address of the validators you want
// to fully exit.
let withdrawalAddress = new ExternalAddress(
    Coinbase.networks.EthereumHoodi,
    "YOUR_WITHDRAWAL_ADDRESS",
);

// Build a full exit staking operation to exit 2 different post Pectra validators.

let builder = new ExecutionLayerWithdrawalOptionsBuilder(withdrawalAddress.getNetworkId());
builder.addValidatorWithdrawal("YOUR_VALIDATOR_PUBKEY_1", new Decimal("0"));
builder.addValidatorWithdrawal("YOUR_VALIDATOR_PUBKEY_2", new Decimal("0"));

let options = await builder.build();

let stakingOperation = await withdrawalAddress.buildUnstakeOperation(
    0, // Amount here doesn't matter.
    Coinbase.assets.Eth,
    StakeOptionsMode.NATIVE,
    options,
);

console.log("Staking Operation ID: %s", stakingOperation.getID())

// Wait for the partial withdrawal transactions to be built.
await stakingOperation.wait();
Once the partial withdrawal staking operation has been built, relay the transactions to your end-user for signing and broadcasting. Refer to the Signing and Broadcasting Transactions section for an example using Ethers.js.

Unstake (via Consensus Layer)

Post Pectra, unstaking via consensus layer remains the same. Both pre and post Pectra validators can be unstaked using this mechanism. Like before it requires a voluntary exit message to be signed by the validator and submitted to the network to initiate the unstaking process. For external addresses, this can be done two ways:

Coinbase Managed Unstake

There are 2 options to build the coinbase managed unstake operation.
By Amount
Coinbase managed unstake by amount currently only supports selection of pre Pectra validators for unstaking.
For 0x01 validators this amount should be in multiples of 32. If amount = 64 ETH, we pick 2 0x01 validators and exit them. This behind the scenes will identify validators to be exited, generate a voluntary exit message per validator, sign it with the validator’s private key and broadcast them for you.
import { Coinbase, ExternalAddress, StakeOptionsMode } from "@coinbase/coinbase-sdk";

// Create a new external address on the ethereum-hoodi testnet network.
let address = new ExternalAddress(
    Coinbase.networks.EthereumHoodi,
    "YOUR_WALLET_ADDRESS",
);

// To know how much ETH balance is available for unstaking, use `unstakeableBalance`.
// Unstakeable balance depends on your CDP account validators, not your address.
// It's surfaced on the address object for simplicity.
// Set `withdrawal_credential_type` to 0x01 or 0x02 to query specific validators.
// By default, it returns the unstakeable balance for 0x01 validators.
let unstakeableBalance = await address.unstakeableBalance(
    Coinbase.assets.Eth,
    StakeOptionsMode.NATIVE,
);

console.log("Unstakeable balance: %s", unstakeableBalance)

// Build unstake operation for amount = 32 ETH.
let stakingOperation = await address.buildUnstakeOperation(
    32,
    Coinbase.assets.Eth,
    StakeOptionsMode.NATIVE,
    {"immediate": "true"},
);

console.log("Staking Operation ID: %s", stakingOperation.getID())

// Immediate native eth unstaking is completely handled by the API
// with no user action needed.
// Example of polling the unstake operation status until it reaches
// a terminal state using the SDK.
await stakingOperation.wait();
By Validator
We support unstaking of both pre & post Pectra validators by validator pub keys. The amount is ignored in this case.
import {
    Coinbase,
    ExternalAddress,
    StakeOptionsMode,
    ConsensusLayerExitOptionBuilder,
} from "@coinbase/coinbase-sdk";

// Create a new external address on the ethereum-hoodi testnet network.
let address = new ExternalAddress(
    Coinbase.networks.EthereumHoodi,
    "YOUR_WALLET_ADDRESS",
);

let options: { [key: string]: string } = { immediate: "true" };

const builder = new ConsensusLayerExitOptionBuilder();
builder.addValidator("YOUR_VALIDATOR_PUBKEY_1");
builder.addValidator("YOUR_VALIDATOR_PUBKEY_2");
options = await builder.build(options);

let stakingOperation = await address.buildUnstakeOperation(
    0, // Amount here doesn't matter.
    "eth",
    StakeOptionsMode.NATIVE,
    options,
);

console.log("Staking Operation ID: %s", stakingOperation.getID())

// Immediate native eth unstaking is completely handled by the API
// with no user action needed.
// Example of polling the unstake operation status until it reaches
// a terminal state using the SDK.
await stakingOperation.wait();
Once the unstake operation has completed successfully, congrats you’ve just exited a validator. Refer to the View Validator Information section to monitor your validator status. When it changes to WITHDRAWAL_COMPLETE, your funds should be available in the withdrawal_address set during staking.

User Managed Unstake

There are 2 options to build the coinbase managed unstake operation.
By Amount
User managed unstake by amount currently only supports selection of pre Pectra validators for unstaking. If you want to be able to unstake both pre & post Pectra validators, use the “Unstake by Validator” option.
For 0x01 validators this amount should be in multiples of 32. If amount = 64 ETH, we pick 2 0x01 validators and exit them. This behind the scenes will identify validators to be exited, generate a voluntary exit message per validator, sign it with the validator’s private key and broadcast them for you.
import { Coinbase, ExternalAddress, StakeOptionsMode } from "@coinbase/coinbase-sdk";

// Create a new external address on the ethereum-hoodi testnet network.
let address = new ExternalAddress(
    Coinbase.networks.EthereumHoodi,
    "YOUR_WALLET_ADDRESS",
);

// To know how much ETH balance is available for unstaking, use `unstakeableBalance`.
// Unstakeable balance depends on your CDP account validators, not your address.
// It's surfaced on the address object for simplicity.
// Set `withdrawal_credential_type` to 0x01 or 0x02 to query specific validators.
// By default, it returns the unstakeable balance for 0x01 validators.
let unstakeableBalance = await address.unstakeableBalance(
    Coinbase.assets.Eth,
    StakeOptionsMode.NATIVE,
);

console.log("Unstakeable balance: %s", unstakeableBalance)

// Build unstake operation for amount = 32 ETH.
let stakingOperation = await address.buildUnstakeOperation(
    32,
    Coinbase.assets.Eth,
    StakeOptionsMode.NATIVE,
);

console.log("Staking Operation ID: %s", stakingOperation.getID())

// Native eth unstaking can take some time as we build the voluntary exit message
// and have it signed by the validator.
// Example of polling the unstake operation status until it reaches
// a terminal state using the SDK.
await stakingOperation.wait();
After unstaking, voluntary exit messages can be read and stored on your end and broadcasted to the network whenever you want to initiate the unstaking process. Refer to the Broadcasting Exit Messages section for an example.
By Validator
We support unstaking of both pre & post Pectra validators by validator pub keys. The amount is ignored in this case.
import {
    Coinbase,
    ExternalAddress,
    StakeOptionsMode,
    ConsensusLayerExitOptionBuilder,
} from "@coinbase/coinbase-sdk";

// Create a new external address on the ethereum-hoodi testnet network.
let address = new ExternalAddress(
    Coinbase.networks.EthereumHoodi,
    "YOUR_WALLET_ADDRESS",
);

const builder = new ConsensusLayerExitOptionBuilder();
builder.addValidator("YOUR_VALIDATOR_PUBKEY_1");
builder.addValidator("YOUR_VALIDATOR_PUBKEY_2");
let options = await builder.build();

let stakingOperation = await address.buildUnstakeOperation(
    0, // Amount here doesn't matter.
    "eth",
    StakeOptionsMode.NATIVE,
    options,
);

console.log("Staking Operation ID: %s", stakingOperation.getID())

// Native eth unstaking can take some time as we build the voluntary exit message
// and have it signed by the validator.
// Example of polling the unstake operation status until it reaches
// a terminal state using the SDK.
await stakingOperation.wait();
After unstaking, voluntary exit messages can be read and stored on your end and broadcasted to the network whenever you want to initiate the unstaking process. Refer to the Broadcasting Exit Messages section for an example.

Consolidation (Coming Soon!)

Consolidation merges multiple smaller pre-Pectra validators into larger post-Pectra validators. This helps users manage fewer validators and unlocks auto-compounding rewards via Pectra, without needing to unstake pre Pectra validators and then provisioning new validators. Coming soon!