Build a Bitcoin wallet API Using Nodejs (Part Two)

In the previous article, we learned how to generate a non-custodial Bitcoin wallet and generate addresses. Now, let's dive into the process of creating transactions, signing them, and broadcasting them to the network using the Blockstream Testnet API.

To create and sign transactions, we'll add two new functions to the WalletController: createTransactions and broadcastTransaction.

The createTransactions function handles the creation and signing of transactions. Here's the code for the function:

export const createTransactions = async (
  req: Request,
  res: Response,
  next: NextFunction
): Promise<void> => {
  try {
    // Retrieve necessary information from the request
    const recipientAddress: string = req.body.recipientAddress;
    const amount: number = req.body.amount;
    const addressType: string | unknown = req.query.type;

    // Get the user's XPUB key
    const xpub = reqUser.user.pub || '';

    // Get the user's encrypted private key from the database and decrypt it
    const getPriv = await knex<User>('users').where('email', req.user.email).first();
    const xprv: string = decryptKey(getPriv?.pk || '');

    // Derive the root key from the decrypted private key
    const root = bip32.fromBase58(xprv, networks.testnet);

    // Generate the current address batch and change address batch
    const currentAddressBatch: Address[] = createAddressBatch(xpub, root, addressType);
    const currentChangeAddressBatch: Address[] = changeAddressBatch(xpub, root, addressType);

    // Combine the address batches
    const addresses: Address[] = [...currentAddressBatch, ...currentChangeAddressBatch];

    // Create decorated UTXOs from the addresses
    const decoratedUtxos: DecoratedUtxo[] = await createDecoratedUTXOs(addresses, root);

    // Create the transaction
    const transaction: Psbt = await createTransaction(
      decoratedUtxos,
      recipientAddress,
      amount,
      currentChangeAddressBatch[0],
      addressType
    );

    // Sign the transaction
    const signedTransactionHex: SignedTransactionData = await signTransaction(transaction, root);

    const data = {
      tHex: signedTransactionHex,
      transaction
    };

    responseSuccess(res, 200, 'Successfully created and signed transaction', data);
  } catch (err) {
    next(err);
  }
};

In the createTransactions function, we retrieve the recipient's address and the amount from the request body. We also get the address type from the query parameter. Then, we retrieve the user's XPUB key and encrypted private key from the database, decrypt the private key, and derive the root key from it.

Next, we generate the current address batch and change the address batch using the XPUB key and root key. We combine these address batches into a single array.

Using the addresses, we create decorated UTXOs (unspent transaction outputs) by calling the createDecoratedUTXOs function, which interacts with the Blockstream Testnet API to fetch UTXOs for each address.

Once we have the decorated UTXOs, we create a transaction by calling the createTransaction function. This function takes the decorated UTXOs, recipient address, amount, change address, and address type as parameters.

After creating the transaction, we sign it by calling the signTransaction function, which signs the transaction using the private key derived

To interact with Blockstream API, we have to create a helper file with the following

import axios from 'axios';

axios.defaults.baseURL = 'https://blockstream.info/testnet/api';

import {
  Address,
  BlockstreamAPITransactionResponse,
  BlockstreamAPIUtxoResponse,
} from "../interfaces/blockstream";


export const getTransactionHex = async (txid: string): Promise<string> => {
  const { data } = await axios.get(
    `/tx/${txid}/hex`
  );

  return data;
};

export const getFeeRates = async (): Promise<Object> => {
  const { data } = await axios.get(`/fee-estimates`);

  return data;
};

export const broadcastTx = async (txHex: string): Promise<string> => {
  const { data } = await axios.post(`/tx`, txHex);

  return data;
};

Conclusion:
In this article, we explored the process of creating, signing, and broadcasting Bitcoin transactions using the Blockstream Testnet API. We added two new functions to the WalletController: createTransactions for creating and signing transactions, and broadcastTransaction for broadcasting signed transactions. By leveraging the Blockstream Testnet API, we were able to interact with the Bitcoin network, retrieve UTXOs, create transactions, sign them, and broadcast them to the network. These functionalities are crucial for enabling secure and non-custodial Bitcoin transactions within our application.