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.