Skip To Content

Blockdaemon Documentation

Staking Integration

The Staking Integration API enables custodians and crypto-savvy traders to use a standardized and trusted API. This automates transaction signing and enables the scaling of the solution over time.

The Staking Integration API endpoint in this page allows you to create your stake intents.

POST Create a New Stake Intent (V1)
Authentication Using API Key as an X-API-Key


POST Create a New Stake Intent (V1)

https://svc.blockdaemon.com/boss/v1/{protocol}/{network}/stake-intents

Reserves one or more available validators and returns an unsigned transaction.

Learn more: Usage Example

The validators will remain reserved for 90 days or until the returned transaction is signed and confirmed in the network. Blockdaemon reserves the right to free any allocated resources if the transaction is not confirmed in time.

In the case of Ethereum, requests containing two or more validators will be routed through the Blockdaemon batch deposit to reduce gas costs.

Note: You need to authorize your request via an API key provided when signing up for a staking account.

Path Parameters

You should specify a protocol and a network as path parameters, e.g.:

  • ethereum/prater

Supported Protocols and Networks

Here is a full list of supported protocols and networks with their path parameters:

Protocol Network Path parameters
Ethereum Mainnet ethereum/mainnet
Ethereum Prater ethereum/prater

Request Body

The request body schema depends on the protocol.

Ethereum

To create a new staking intent on Ethereum, pass an array of stakes (1–100 items). For each stake you need to specify:

  • amount: The amount of ETH (in Gwei) you want to stake
    • This is the amount per validator to be deployed (rather than the total amount)
    • This should usually be set to "amount": "32000000000",
  • withdrawal credentials: Hexadecimal encoded withdrawal credentials. These can be either a BLS public key or an account address. For more information, please see the official Ethereum Launchpad FAQ.
  • quantity (optional): It represents the number of validators (>= 1) that will share the same withdrawal credentials.
{
    "stakes": [
        {
            "amount": "32000000000",
            "withdrawal_credentials": "0x0092c20062cee70389f1cb4fa566a2be5e2319ff43965db26dbaa3ce90b9df99",
            "quantity": 1
        }
    ]
}

Example Queries

The query in this section provides a useful example for POST Create a New Stake Intent (V1).

To create a new stake intent in the Ethereum Prater, use the following:

https://svc.blockdaemon.com/boss/v1/ethereum/prater/stake-intents

Example Response

The following is a typical response from POST Create a New Stake Intent (V1):

{
    "stake_intent_id": "stake_intent_Wgx98Rbi8nQuL9ddn3mTk1",
    "platform": "ethereum",
    "network": "prater",
    "ethereum": {
        "stakes": [
            {
                "stake_id": "stake_Wgx98Rbi8nQuL9ddn3mTk1",
                "amount": "32000000000",
                "validator_public_key": "0xa1d1ad0714035353258038e964ae9675dc0252ee22cea896825c01458e1807bfad2f9969338798548d9858a571f7425c",
                "withdrawal_credentials": "0x0092c20062cee70389f1cb4fa566a2be5e2319ff43965db26dbaa3ce90b9df99"
            },
            {...more items...}
        ],
        "contract_address": "0x3F124C700fb5E741F128e28985267D44f56B242F",
        "unsigned_transaction": "0x592c0b7d0000000000000000000000000000000000000000000000000000000063e7a459000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000d097fff47b1ce70d65a1ffa48bd2606298e73f9bcfc4d6407e0362ee75e50c2cc6653921ab734f2fc525a713f66e7e30a2010000000000000000000000ecee073513bcc187d5a36dfb723eec36db7ac0a4b5922fe70c705a4773f352e3d54c6eac8db47c85186c12024f39cbd5b3b4b11b404636638769ceac0026a87ed37899360a267eb9bbd1874663afb60bd3ab5c3c05b21988b4e5d38ca1929a2cd0667dc4117bacf574ad222617ea609a2d40ad6e6487f40d20c8b1332b99ecff74ff1cd25defe51fb18eed7f3cec0b563967415900000000000000000000000000000000"
    },
    "customer_id": "4d2517d1-3059-4720-8e52-a51c6a0f1f77"
}

Usage Example

Below you will find a TypeScript example showing how to send an Ethereum transaction using the data returned by POST Create a New Stake Intent (V1). The transaction is signed and sent with the web3.js Ethereum API. For RPC access, Ubiquity Native Access API is used. However, you can choose other tools and methods for these purposes.

In the example we provide, the following steps are taken to send a transaction:

Step 1. A stake intent is created with POST Create a New Stake Intent (V1). The unsigned_transaction field returns a base64 string.

Step 2. The signTransaction() web3.js method is used to sign the transaction. The unsigned_transaction string is passed to the data property of the transaction object to be signed.

Step 3. The sendSignedTransaction() web3.js method is used to send the signed transaction.

Creating a Stake Intent and Sending a Transaction in Ethereum Prater

import Web3 from 'web3';
import { TransactionConfig, TransactionReceipt } from 'web3-core';

const batchDepositContractAddress = {
  prater: '0x6D144323aED2326255e9cE36a429ad737a1ccE37',
};

const gwei = 10n ** 9n;

async function example() {
  const network: 'mainnet' | 'prater' = 'prater';

  const { ethereumSenderAddress, rpcUrl, bossApiKey } =
    getEnvironmentVariables();

  const response = await createStakeIntent(bossApiKey, {
    stakes: [
      {
        amount: '32000000000',
        withdrawal_credentials:
          '0x0000000000000000000000000000000000000000000000000000000000000000',
      },
    ],
  });

  const { unsigned_transaction, stakes } = response.ethereum;
  const totalDepositAmount =
    stakes.reduce((sum, next) => sum + BigInt(next.amount), 0n) * gwei;

  const web3 = new Web3(rpcUrl);

  const txReceipt = await sendTx(web3, {
    from: ethereumSenderAddress,
    to: batchDepositContractAddress[network],
    value: totalDepositAmount.toString(10),
    data: unsigned_transaction,
  });

  console.log(txReceipt);
}

function getEnvironmentVariables() {
  if (!process.env.ETHEREUM_SENDER_ADDRESS) {
    throw new Error('Please set the ETHEREUM_SENDER_ADDRESS env variable.');
  }

  if (!process.env.ETH_RPC_URL && !process.env.UBIQUITY_API_KEY) {
    throw new Error(
      'Please set either ETH_RPC_URL or UBIQUITY_API_KEY env variables.',
    );
  }

  if (!process.env.BOSS_API_KEY) {
    throw new Error('Please set the BOSS_API_KEY env variable.');
  }

  return {
    ethereumSenderAddress: process.env.ETHEREUM_SENDER_ADDRESS,
    rpcUrl:
      process.env.ETH_RPC_URL ??
      getUbiquityNativeWeb3Url(process.env.UBIQUITY_API_KEY!),
    bossApiKey: process.env.BOSS_API_KEY,
  };
}

function getUbiquityNativeWeb3Url(
  apiKey: string,
  network: 'mainnet' | 'prater' = 'prater',
) {
  return `https://svc.blockdaemon.com/ethereum/${network}/native?apiKey=${apiKey}`;
}

export type CreateStakeIntentRequest = {
  stakes: {
    withdrawal_credentials: string;
    amount: string;
  }[];
};

export type CreateStakeIntentResponse = {
  stake_intent_id: string;
  ethereum: {
    stakes: {
      stake_id: string;
      amount: string;
      validator_public_key: string;
      withdrawal_credentials: string;
    }[];
    contract_address: string;
    unsigned_transaction: string;
  };
};

function createStakeIntent(
  bossApiKey: string,
  request: CreateStakeIntentRequest,
): Promise {
  const requestOptions = {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      Accept: 'application/json',
      'X-API-Key': bossApiKey,
    },
    body: JSON.stringify(request),
  };

  return fetch(
    'https://svc.blockdaemon.com/boss/v1/ethereum/prater/stake-intents',
    requestOptions,
  ).then(response => response.json() as Promise);
}

export async function sendTx(
  web3: Web3,
  txConfig_: Pick<TransactionConfig, 'from' | 'to' | 'data' | 'value'>,
): Promise {
  const txConfig: TransactionConfig = txConfig_;
  txConfig.nonce = await web3.eth.getTransactionCount(txConfig.from as string);
  txConfig.gas = await web3.eth.estimateGas(txConfig);

  const { baseFeePerGas } = await web3.eth.getBlock('latest');
  if (!baseFeePerGas) {
    throw new Error('Could not get block base fee');
  }
  txConfig.maxFeePerGas = (BigInt(baseFeePerGas) + 2n * gwei).toString();

  const signedTx = (await web3.eth.signTransaction(
    txConfig,
  )) as unknown as string;

  return await web3.eth.sendSignedTransaction(signedTx);
}

example()
  .then(() => process.exit(0))
  .catch(err => {
    console.error(err);
    process.exit(1);
  });

Authentication Using API Key as an X-API-Key

When signing up for a staking account, you are provided with an API key.

To authenticate a Staking API request, pass your API key in the X-API-Key header:


curl -X POST \
'https://svc.blockdaemon.com/boss/v1/ethereum/prater/stake-intents' \
-H 'X-API-Key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{"stakes": [{"amount": "32000000000", "withdrawal_credentials": "0x0092c20062cee70389f1cb4fa566a2be5e2319ff43965db26dbaa3ce90b9df99", "quantity": 1}]}'

We don't support Internet Explorer

Please use Chrome, Safari, Firefox, or Edge to view this site.