Recipes

Recipes

Stake ETH From Fireblocks

Creates new validator keys and returns a staking transaction to deposit ETH to those validators. Then the staking transaction gets signed w Fireblocks API.

In this Recipe

  1. Provision validators
  2. Submit for Fireblocks signature
  3. Wait for Fireblocks signature

🔥Open Recipe

JavaScript

async function createValidators() {
  const url = '<https://api.figment.io/ethereum/validators>';
  const data = {
    network: 'hoodi',
    validators_count: 1,
    withdrawal_address: '0x8C025F5EdaBE788A7f14Db861420aaE12CDf979f',
    funding_address: '0x8C025F5EdaBE788A7f14Db861420aaE12CDf979f',
    fee_recipient_address: '0x8C025F5EdaBE788A7f14Db861420aaE12CDf979f',
    region: 'ca-central-1'
  };
  
  try {
    const response = await axios.post(url, data, {
      headers: {
        'accept': 'application/json',
        'content-type': 'application/json',
        'x-api-key': process.env.FIGMENT_API_KEY
      }
    });
    
    if (response.data && response.data.meta && response.data.meta.staking_transaction && response.data.meta.staking_transaction.contract_call_data) {
      return response.data.meta.staking_transaction.contract_call_data;
    } else {
      console.log('Contract Call Data not found in the response.');
      return null;
    }
  } catch (error) {
    console.error('Error:', error.response ? error.response.data : error);
    return null;
  }
}

async function signWithFireblocks(contractAddress, contractCallData) {
  console.log("Contract Call Data:", contractCallData);
  return fireblocksApiClient.createTransaction({
    operation: TransactionOperation.CONTRACT_CALL,
    assetId: process.env.FIREBLOCKS_ASSET_ID,
    source: {
      type: PeerType.VAULT_ACCOUNT,
      id: "1"
    },
    destination: {
      type: PeerType.ONE_TIME_ADDRESS,
      oneTimeAddress: {
        address: contractAddress
      }
    },
    note: "Contract Call Transaction",
    amount: "32",
    extraParameters: {
      contractCallData
    }
  }).then(res => {
    console.log(`✔ Submitted to Fireblocks for approval & signature. ID: ${res.id}`);
    return res;
  }).catch(e => {
    console.error(`Fireblocks API Error: ${e.message}`);
  });
}

async function waitForTxCompletion(fbTx) {
  let tx = fbTx;
  
  while (tx.status !== TransactionStatus.COMPLETED) {
    if ([TransactionStatus.BLOCKED, TransactionStatus.FAILED, TransactionStatus.REJECTED, TransactionStatus.CANCELLED].includes(tx.status)) {
      throw new Error(`Exiting the operation due to error. Transaction status: ${tx.status}`);
    }
    
    console.log("Transaction's status:", (await fireblocksApiClient.getTransactionById(fbTx.id)).status);
    await new Promise(resolve => setTimeout(resolve, 4000));
    
    tx = await fireblocksApiClient.getTransactionById(fbTx.id);
  }
  
  return (await fireblocksApiClient.getTransactionById(fbTx.id));
}

async function processValidators() {
  const contractCallData = await createValidators();
  if (contractCallData) {
    const fbTx = await signWithFireblocks('0xA627f94a8F94E4713d38F52aC3a6377B0a111d47', contractCallData); // Use the actual contract address
    if (fbTx) {
      const fbRes = await waitForTxCompletion(fbTx);
      console.log(fbRes);
    }
  }
}

// Execute the combined function
processValidators();