Coinbase Staking API empowers developers to deliver a fully-featured staking experience in their applications using one common interface across protocols. This quickstart shows you how to stake Holesky (an Ethereum testnet) ETH to our best-in-class staking infrastructure using the Coinbase Staking API.

What you will learn

  • Installing the CDP SDK
  • How to check your stakeable balance
  • How to create a transaction to stake Holesky ETH
  • How to sign and broadcast a staking transaction

1. Prerequisites

The Coinbase Typescript SDK requires Node.js version 18+. To verify your Node version:
node -v

2. API Key Setup

To use the CDP SDK, you need a CDP secret API key. If you need to create one, follow this guide. Once you have the key, place it on your filesystem. You will need to reference this file path later in your code.

3. Create a Workspace

In your preferred shell, create a new directory:
mkdir staking-demo
cd staking-demo

4. Install the CDP SDK

Install the CDP SDK using your preferred package manager:
npm install @coinbase/coinbase-sdk
match your wallet address model with your use casesStaking can be done using two different models types depending on your use case.
  • External Address (recommended) - The External Address represents the End User Custody model where the private keys are not managed by the CDP SDK. All of our networks will support this address model. Read more in the External Addresses documentation.
  • Wallet Address - The Wallet Address represents the Developer Custody model where the private keys are managed by the CDP SDK. Only a subset of our networks support this address model. Find out more in the Wallet Addresses section.

5a. Create a Staking Transaction using an External Address

To proceed with the stake example below, you need some Holesky ETH in your wallet. If you don’t have any, you can request some from the Ethereum Holesky Faucet.
Create a new file named stake.ts and paste the code block below:
import {
Coinbase,
ExternalAddress,
StakeOptionsMode,
} from "@coinbase/coinbase-sdk";

const apiKeyFilePath = "YOUR_API_KEY_FILE_PATH";
const walletAddress = "YOUR_WALLET_ADDRESS";

/**
* Stake 0.005 ETH on the ethereum-holesky testnet network.
*/
async function stake() {
Coinbase.configureFromJson({ filePath: apiKeyFilePath });

// Create a new external address on the ethereum-holesky testnet network.
const address = new ExternalAddress(
Coinbase.networks.EthereumHolesky,
walletAddress,
);

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

console.log("Stakeable balance of address %s is %s ETH", walletAddress, stakeableBalance);

// Build a stake transaction for an amount <= stakeableBalance
process.stdout.write("Building a transaction to stake 0.005 ETH...");
const stakingOperation = await address.buildStakeOperation(
0.005,
Coinbase.assets.Eth,
StakeOptionsMode.PARTIAL,
);

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

console.log("Done.");
}

(async () => {
try {
await stake();
} catch (error) {
console.error("Error during stake operation", error);
}
})();
Note
  • Be sure to replace the placeholder values with your own for:
YOUR_API_KEY_FILE_PATH
YOUR_WALLET_ADDRESS
Then run the code to create a staking transaction:
npx ts-node stake.ts
The transaction that was generated is an unsigned transaction. This still needs to be signed and broadcasted to the network to stake successfully. See the next section for instructions on how to sign and broadcast the transaction.

5b. Create a Staking Transaction using a Wallet Address

Create a new file named stake.ts and paste the code block below:
import { Coinbase, Wallet } from "@coinbase/coinbase-sdk";

const apiKeyFilePath = "YOUR_API_KEY_FILE_PATH";

const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));

/**
* Perform a stake operation.
*/
async function stake() {
// Import the CDP API key from the file.
const coinbase = Coinbase.configureFromJson({ filePath: apiKeyFilePath });

// Create a wallet on the `ethereum-holesky` testnet network.
process.stdout.write("Creating wallet...");
const wallet = await Wallet.create({ networkId: Coinbase.networks.EthereumHolesky });
console.log(
`Wallet created with ID: ${wallet.getId()} and default address: ${(await wallet.getDefaultAddress()).getId()}`,
);

// Save wallet seed locally for future use.
process.stdout.write("Saving wallet seed information to local directory...");
wallet.saveSeed("wallet-seed", true);
console.log("Done.");

// Fund your wallet from a faucet with some ethereum-holesky testnet funds.
process.stdout.write("Funding wallet with testnet funds...");
const faucetTransaction = await wallet.faucet();
await faucetTransaction.wait();

console.log("Done.");
console.log(`Wallet funding transaction: ${faucetTransaction.getTransactionLink()}`);

// Find out how much ETH is available to stake from your wallet.
process.stdout.write("Getting stakeable balance of wallet...");
const stakeableBalance = await wallet.stakeableBalance(Coinbase.assets.Eth);
console.log("Done.");
console.log(`Stakeable balance of wallet: ${stakeableBalance} ETH`);

// Stake a small amount of ETH.
process.stdout.write("Staking 0.00001 ETH from your wallet...");
const stakingOperation = await wallet.createStake(0.00001, Coinbase.assets.Eth);
console.log("Done.");
console.log(
`View your stake transaction: ${stakingOperation.getTransactions()[0].getTransactionLink()}`,
);
}

(async () => {
try {
await stake();
} catch (error) {
console.error("Error during stake operation", error);
}
})();
Note
  • Be sure to replace the placeholder values with your own for:
YOUR_API_KEY_FILE_PATH
Then run the code to create a staking transaction:
npx ts-node stake.ts

Note

  • To reuse the wallet created in the steps above, see Re-instantiating a Wallet with the CDP SDK.
  • Network congestion can sometimes delay a transaction, which can cause the funding or stake transaction to fail.

6. [Optional]: Sign and Broadcast your Staking Transaction (External Address only)

This optional step is only applicable to external address model onlyThe external wallet address model assumes developers will sign and broadcast transactions outside of our SDK. The example below shows how this can be done using Ethers.js web library as an example.
The previous step generated an unsigned transaction. To stake successfully, the transaction needs to be signed and broadcasted to the network. Signing and broadcasting functionality is added to the example from above. The additional lines are highlighted for clarity.
import { Coinbase, ExternalAddress, StakeOptionsMode } from "@coinbase/coinbase-sdk";
import { ethers } from "ethers";

const apiKeyFilePath = "YOUR_API_KEY_FILE_PATH";
const walletAddress = "YOUR_WALLET_ADDRESS";

/**
* Stake 0.005 ETH on the ethereum-holesky testnet network.
*/
async function stake() {
Coinbase.configureFromJson({ filePath: apiKeyFilePath });

// Create a new external address on the ethereum-holesky testnet network.
const address = new ExternalAddress(Coinbase.networks.EthereumHolesky, walletAddress);

// Find out how much ETH is available to stake.
const stakeableBalance = await address.stakeableBalance(Coinbase.assets.Eth, StakeOptionsMode.PARTIAL);
console.log("Stakeable balance of address %s is %s ETH", walletAddress, stakeableBalance);

// Build a stake transaction for an amount <= stakeableBalance
process.stdout.write("Building a transaction to stake 0.005 ETH... ");
const stakingOperation = await address.buildStakeOperation(0.005, Coinbase.assets.Eth, StakeOptionsMode.PARTIAL);
console.log("Done.");

// Load your wallet's private key from which you initiated the above stake operation.
const walletPrivateKey = "YOUR_WALLET_PRIVATE_KEY";
const wallet = new ethers.Wallet(walletPrivateKey);
// Additional public Holesky RPC endpoints can be found here https://chainlist.org/chain/17000
const holeskyNodeURL = "HOLESKY_NODE_URL";

// Sign the transactions within staking operation resource with your wallet.
process.stdout.write("Signing the stake operation... ");
await stakingOperation.sign(wallet);
console.log("Done.");

const provider = new ethers.JsonRpcProvider(holeskyNodeURL);

// Broadcast each of the signed transactions to the network.
process.stdout.write("Broadcasting the stake operation... ");
for (const tx of stakingOperation.getTransactions()) {
const resp = await provider.broadcastTransaction(tx.getSignedPayload()!);
console.log("Broadcasted transaction hash: %s", resp.hash);
}
}

(async () => {
try {
await stake();
} catch (error) {
console.error("Error during stake operation", error);
}
})();
Note
  • Be sure to replace the placeholder values with your own for:
YOUR_API_KEY_FILE_PATH
Then run the code to create a staking transaction:
npx ts-node stake.ts
Visit the Etherscan block explorer to view the finalized transaction after it has been broadcasted. Testnet transactions may take up to a minute to be confirmed by a block explorer.
https://holesky.etherscan.io/tx/{BROADCASTED_TX_HASH}

Next Steps

Congratulations! You’ve used the CDP SDK to stake your first ETH on the Holesky testnet. See also: