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 Hoodi (an Ethereum testnet) ETH to our best-in-class staking infrastructure using the Coinbase Staking API.
Install the CDP SDK using your preferred package manager:
Copy
Ask AI
npm install @coinbase/coinbase-sdk
Install the CDP SDK using your preferred package manager:
Copy
Ask AI
npm install @coinbase/coinbase-sdk
Install the Coinbase SDK:
Copy
Ask AI
go get github.com/coinbase/coinbase-sdk-go
Match your wallet address model with your use cases
Staking 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 Hoodi ETH in your wallet. If you don’t have any, you can request some from the Ethereum Hoodi Faucet.
Create a new file named stake.ts and paste the code block below:
stake.ts
Copy
Ask AI
import { Coinbase, ExternalAddress, StakeOptionsMode,} from "@coinbase/coinbase-sdk";// highlight-startconst apiKeyFilePath = "YOUR_API_KEY_FILE_PATH";const walletAddress = "YOUR_WALLET_ADDRESS";// highlight-end/** * Stake 0.005 ETH on the ethereum-hoodi testnet network. */async function stake() {Coinbase.configureFromJson({ filePath: apiKeyFilePath });// Create a new external address on the ethereum-hoodi testnet network.const address = new ExternalAddress( Coinbase.networks.EthereumHoodi, 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 <= stakeableBalanceprocess.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);}})();
Be sure to replace the placeholder values with your own for:
Copy
Ask AI
YOUR_API_KEY_FILE_PATHYOUR_WALLET_ADDRESS
Then run the code to create a staking transaction:
Copy
Ask AI
npx ts-node stake.ts
Create a new file named stake.ts and paste the code block below:
stake.ts
Copy
Ask AI
import { Coinbase, ExternalAddress, StakeOptionsMode,} from "@coinbase/coinbase-sdk";// highlight-startconst apiKeyFilePath = "YOUR_API_KEY_FILE_PATH";const walletAddress = "YOUR_WALLET_ADDRESS";// highlight-end/** * Stake 0.005 ETH on the ethereum-hoodi testnet network. */async function stake() {Coinbase.configureFromJson({ filePath: apiKeyFilePath });// Create a new external address on the ethereum-hoodi testnet network.const address = new ExternalAddress( Coinbase.networks.EthereumHoodi, 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 <= stakeableBalanceprocess.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);}})();
Be sure to replace the placeholder values with your own for:
Copy
Ask AI
YOUR_API_KEY_FILE_PATHYOUR_WALLET_ADDRESS
Run the code:
Copy
Ask AI
go run stake.go
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.
Copy
Ask AI
Stakeable balance of address 0x87Bf57c3d7B211a100ee4d00dee08435130A62fA is 207.65555527344569 ETHBuilding stake operation for 0.005 ETH ... Done.Unsigned payloads: ["7b2274797065223a22307832222c22636861696e4964223a22307834323638222c226e6f6e6365223a223078313030222c22746f223a22307861353534313664653564653631613061633161613839373061323830653034333838623164653462222c22676173223a2230783364303930222c226761735072696365223a6e756c6c2c226d61785072696f72697479466565506572476173223a223078323534306265343030222c226d6178466565506572476173223a223078323534306265343065222c2276616c7565223a2230783131633337393337653038303030222c22696e707574223a2230783361346236366631222c226163636573734c697374223a5b5d2c2276223a22307830222c2272223a22307830222c2273223a22307830222c2279506172697479223a22307830222c2268617368223a22307832623335363130643637653936313864326338343739613638623362383163626232323734323933353935326331626334626536313364363965366662643037227d"]
5b. Create a Staking Transaction using a Wallet Address
Create a new file named stake.ts and paste the code block below:
stake.ts
Copy
Ask AI
import { Coinbase, Wallet, StakeOptionsMode } from "@coinbase/coinbase-sdk";// highlight-startconst apiKeyFilePath = "YOUR_API_KEY_FILE_PATH";// highlight-endconst 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-hoodi` testnet network.process.stdout.write("Creating wallet...");const wallet = await Wallet.create({ networkId: Coinbase.networks.EthereumHoodi });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-hoodi 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, StakeOptionsMode.PARTIAL);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, StakeOptionsMode.PARTIAL);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);}})();
Be sure to replace the placeholder values with your own for:
Copy
Ask AI
YOUR_API_KEY_FILE_PATH
Then run the code to create a staking transaction:
Copy
Ask AI
npx ts-node stake.ts
Create a new file named stake.ts and paste the code block below:
stake.ts
Copy
Ask AI
import { Coinbase, Wallet, StakeOptionsMode } from "@coinbase/coinbase-sdk";// highlight-startconst apiKeyFilePath = "YOUR_API_KEY_FILE_PATH";// highlight-endconst 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-hoodi` testnet network.process.stdout.write("Creating wallet...");const wallet = await Wallet.create({ networkId: Coinbase.networks.EthereumHoodi });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-hoodi 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, StakeOptionsMode.PARTIAL);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, StakeOptionsMode.PARTIAL);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);}})();
Be sure to replace the placeholder values with your own for:
Copy
Ask AI
YOUR_API_KEY_FILE_PATH
Then run the code to create a staking transaction:
Copy
Ask AI
npx ts-node stake.ts
Copy
Ask AI
Creating wallet...Done.Wallet created with ID: ab6efe3b-ea0d-4e1d-8360-cc7b47d4d024 and default address: 0x962e0561475a34e933a41b0dA6e5c4F4b9f39315Saving wallet seed information to local directory...Done.Funding wallet with testnet funds...Done.Wallet funding transaction: https://holesky.etherscan.io/tx/0x1963584c3953f0cd72c31f1f3560ba20f2295a4ae87b173c778a633ca6535eeaWaiting for transaction to finalize.......Done.Getting stakeable balance of wallet...Done.Stakeable balance of wallet: 0.01 ETHStaking 0.00001 ETH from your wallet...Done.View your stake transaction: https://holesky.etherscan.io/tx/0xb6ef8dec0464569ba303d435cf5d32a5d1c9b27740fa42ebe57726f05029916e
6. [Optional]: Sign and Broadcast your Staking Transaction (External Address only)
This optional step is only applicable to external address model only
The 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.
showLineNumbers stake.ts
Copy
Ask AI
import { Coinbase, ExternalAddress, StakeOptionsMode } from "@coinbase/coinbase-sdk";// highlight-startimport { ethers } from "ethers";// highlight-endconst apiKeyFilePath = "YOUR_API_KEY_FILE_PATH";const walletAddress = "YOUR_WALLET_ADDRESS";/** * Stake 0.005 ETH on the ethereum-hoodi testnet network. */async function stake() {Coinbase.configureFromJson({ filePath: apiKeyFilePath });// Create a new external address on the ethereum-hoodi testnet network.const address = new ExternalAddress(Coinbase.networks.EthereumHoodi, 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 <= stakeableBalanceprocess.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.");// highlight-start// 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 Hoodi RPC endpoints can be found here https://chainlist.org/chain/560048const hoodiNodeURL = "HOODI_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(hoodiNodeURL);// 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);}// highlight-end}(async () => {try { await stake();} catch (error) { console.error("Error during stake operation", error);}})();
Be sure to replace the placeholder values with your own for:
Copy
Ask AI
YOUR_API_KEY_FILE_PATHYOUR_WALLET_ADDRESS
Run the code:
Copy
Ask AI
npx ts-node stake.ts
showLineNumbers stake.ts
Copy
Ask AI
import { Coinbase, ExternalAddress, StakeOptionsMode } from "@coinbase/coinbase-sdk";// highlight-startimport { ethers } from "ethers";// highlight-endconst apiKeyFilePath = "YOUR_API_KEY_FILE_PATH";const walletAddress = "YOUR_WALLET_ADDRESS";/** * Stake 0.005 ETH on the ethereum-hoodi testnet network. */async function stake() {Coinbase.configureFromJson({ filePath: apiKeyFilePath });// Create a new external address on the ethereum-hoodi testnet network.const address = new ExternalAddress(Coinbase.networks.EthereumHoodi, 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 <= stakeableBalanceprocess.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.");// highlight-start// 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 Hoodi RPC endpoints can be found here https://chainlist.org/chain/560048const hoodiNodeURL = "HOODI_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(hoodiNodeURL);// 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);}// highlight-end}(async () => {try { await stake();} catch (error) { console.error("Error during stake operation", error);}})();
You should see the transaction being created, signed, and then broadcast to the network:
Copy
Ask AI
Stakeable balance of address 0x87Bf57c3d7B211a100ee4d00dee08435130A62fA is 207.64830262344410791 ETHBuilding a transaction to stake 0.005 ETH... Done.Unsigned payloads: ['7b2274797065223a22307832222c22636861696e4964223a22307834323638222c226e6f6e6365223a223078313031222c22746f223a22307861353534313664653564653631613061633161613839373061323830653034333838623164653462222c22676173223a2230783364303930222c226761735072696365223a6e756c6c2c226d61785072696f72697479466565506572476173223a223078323534306265343030222c226d6178466565506572476173223a223078323534306265343065222c2276616c7565223a2230783131633337393337653038303030222c22696e707574223a2230783361346236366631222c226163636573734c697374223a5b5d2c2276223a22307830222c2272223a22307830222c2273223a22307830222c2279506172697479223a22307830222c2268617368223a22307839346364373935376334373962396266396464623233326561333939393366653234636661313464663839396563343938386234613038663862613065623936227d']Signing the stake operation... Done.Broadcasting the stake operation... Broadcasted transaction hash: 0x2c66c1d716ceadeef25115cc5c2834c600cd9c35292195d9e2511c7f8c89a123
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.