Ethereum API Usage Example

Have a look at an example showing how to send a transaction using Blockdaemon's Ethereum Staking API.

In this section, you will find a TypeScript example showing how to send an Ethereum transaction using the Ethereum Staking API.

The transaction is signed and sent with the web3.js Ethereum API. For RPC access, Native RPC 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 Stake Intent. 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 Goerli/Prater testnet
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);
  });

👋 Need Help?

Contact us through email or our support page for any issues, bugs, or assistance you may need.