The bigmi library doesn't include functions for generating raw transactions. It focuses on blockchain data retrieval and transaction broadcasting. For transaction creation, use bitcoinjs-lib which bigmi already depends on.
import * as bitcoin from 'bitcoinjs-lib';
import { getUTXOs } from '@bigmi/core';
async function createTransaction(
client: Client,
fromAddress: string,
toAddress: string,
amount: number,
privateKey: Buffer
) {
// 1. Get UTXOs for the address
const utxos = await getUTXOs(client, { address: fromAddress });
// 2. Create a new transaction
const psbt = new bitcoin.Psbt();
// 3. Add inputs from UTXOs
let inputValue = 0;
for (const utxo of utxos) {
psbt.addInput({
hash: utxo.txId,
index: utxo.vout,
witnessUtxo: {
script: Buffer.from(utxo.scriptHex, 'hex'),
value: utxo.value,
},
});
inputValue += utxo.value;
if (inputValue >= amount + estimatedFee) break;
}
// 4. Add outputs
psbt.addOutput({
address: toAddress,
value: amount,
});
// 5. Add change output if needed
const fee = 1000; // Calculate appropriate fee
const change = inputValue - amount - fee;
if (change > 0) {
psbt.addOutput({
address: fromAddress,
value: change,
});
}
// 6. Sign the transaction
psbt.signAllInputs(bitcoin.ECPair.fromPrivateKey(privateKey));
psbt.finalizeAllInputs();
// 7. Get the raw transaction hex
const rawTx = psbt.extractTransaction().toHex();
return rawTx;
}import * as bitcoin from 'bitcoinjs-lib';
function createPSBT(
utxos: UTXO[],
outputs: { address: string; value: number }[]
): bitcoin.Psbt {
const psbt = new bitcoin.Psbt();
// Add inputs
for (const utxo of utxos) {
psbt.addInput({
hash: utxo.txId,
index: utxo.vout,
witnessUtxo: {
script: Buffer.from(utxo.scriptHex, 'hex'),
value: utxo.value,
},
});
}
// Add outputs
for (const output of outputs) {
psbt.addOutput({
address: output.address,
value: output.value,
});
}
return psbt;
}import * as bitcoin from 'bitcoinjs-lib';
import { createClient, getUTXOs, sendUTXOTransaction, waitForTransaction } from '@bigmi/core';
async function sendBitcoin(
client: Client,
fromAddress: string,
toAddress: string,
amount: number,
privateKey: Buffer
) {
// 1. Get UTXOs using bigmi
const utxos = await getUTXOs(client, {
address: fromAddress,
minValue: amount + 1000 // amount + estimated fee
});
// 2. Create transaction with bitcoinjs-lib
const psbt = new bitcoin.Psbt();
// Add inputs and calculate total
let totalInput = 0;
for (const utxo of utxos) {
psbt.addInput({
hash: utxo.txId,
index: utxo.vout,
witnessUtxo: {
script: Buffer.from(utxo.scriptHex, 'hex'),
value: utxo.value,
},
});
totalInput += utxo.value;
}
// Add output
psbt.addOutput({
address: toAddress,
value: amount,
});
// Add change output
const fee = 1000; // Use fee estimation
const change = totalInput - amount - fee;
if (change > 0) {
psbt.addOutput({
address: fromAddress,
value: change,
});
}
// Sign
const keyPair = bitcoin.ECPair.fromPrivateKey(privateKey);
psbt.signAllInputs(keyPair);
psbt.finalizeAllInputs();
// Get raw transaction
const rawTx = psbt.extractTransaction();
const txHex = rawTx.toHex();
// 3. Broadcast using bigmi
const txId = await sendUTXOTransaction(client, { hex: txHex });
// 4. Wait for confirmation using bigmi
const confirmedTx = await waitForTransaction(client, {
txId,
txHex,
senderAddress: fromAddress,
confirmations: 1,
});
return confirmedTx;
}async function estimateFee(
client: Client,
numInputs: number,
numOutputs: number
): Promise<number> {
// Get recent block stats for fee estimation
const blockStats = await getBlockStats(client, {
blockNumber: await getBlockCount(client),
stats: ['avgfeerate']
});
// Estimate transaction size
// P2WPKH: ~68 bytes per input, ~31 bytes per output, ~10 bytes overhead
const estimatedSize = (numInputs * 68) + (numOutputs * 31) + 10;
// Calculate fee (satoshis per byte * size)
const feeRate = blockStats.avgfeerate || 1; // fallback to 1 sat/byte
return Math.ceil(feeRate * estimatedSize);
}import { getAddressInfo } from '@bigmi/core';
function createInputForUTXO(utxo: UTXO, addressInfo: AddressInfo) {
const input: any = {
hash: utxo.txId,
index: utxo.vout,
};
switch (addressInfo.type) {
case 'p2wpkh':
case 'p2wsh':
// Witness UTXOs for SegWit
input.witnessUtxo = {
script: Buffer.from(utxo.scriptHex, 'hex'),
value: utxo.value,
};
break;
case 'p2pkh':
case 'p2sh':
// Need full transaction for legacy
// You'd need to fetch the full transaction here
input.nonWitnessUtxo = Buffer.from(fullTransactionHex, 'hex');
break;
}
return input;
}import * as bitcoin from 'bitcoinjs-lib';
function createMultisigTransaction(
utxos: UTXO[],
toAddress: string,
amount: number,
redeemScript: Buffer,
signatures: Buffer[]
) {
const psbt = new bitcoin.Psbt();
// Add inputs with redeem script
for (const utxo of utxos) {
psbt.addInput({
hash: utxo.txId,
index: utxo.vout,
witnessUtxo: {
script: Buffer.from(utxo.scriptHex, 'hex'),
value: utxo.value,
},
witnessScript: redeemScript,
});
}
// Add output
psbt.addOutput({
address: toAddress,
value: amount,
});
// Add signatures
signatures.forEach((sig, index) => {
psbt.updateInput(index, {
partialSig: [{
pubkey: extractPubkeyFromSig(sig),
signature: sig,
}],
});
});
psbt.finalizeAllInputs();
return psbt.extractTransaction().toHex();
}function createRBFTransaction(
originalPsbt: bitcoin.Psbt,
newFeeRate: number
): bitcoin.Psbt {
const newPsbt = new bitcoin.Psbt();
// Copy inputs with RBF sequence
originalPsbt.data.inputs.forEach((input, index) => {
newPsbt.addInput({
hash: originalPsbt.txInputs[index].hash,
index: originalPsbt.txInputs[index].index,
sequence: 0xfffffffd, // RBF enabled
witnessUtxo: input.witnessUtxo,
});
});
// Adjust outputs for new fee
const totalInput = calculateTotalInput(originalPsbt);
const totalOutput = calculateTotalOutput(originalPsbt);
const oldFee = totalInput - totalOutput;
const newFee = calculateNewFee(newFeeRate);
originalPsbt.txOutputs.forEach((output, index) => {
if (isChangeOutput(output)) {
// Reduce change by fee difference
newPsbt.addOutput({
script: output.script,
value: output.value - (newFee - oldFee),
});
} else {
// Keep other outputs unchanged
newPsbt.addOutput({
script: output.script,
value: output.value,
});
}
});
return newPsbt;
}// Coin selection algorithms
function selectUTXOs(
utxos: UTXO[],
targetAmount: number,
strategy: 'fifo' | 'largest' | 'optimal'
): UTXO[] {
const sortedUTXOs = [...utxos];
switch (strategy) {
case 'fifo':
// First In First Out - use oldest UTXOs first
sortedUTXOs.sort((a, b) => a.blockHeight - b.blockHeight);
break;
case 'largest':
// Use largest UTXOs first to minimize inputs
sortedUTXOs.sort((a, b) => b.value - a.value);
break;
case 'optimal':
// Branch and bound algorithm for optimal selection
return branchAndBoundSelection(utxos, targetAmount);
}
const selected: UTXO[] = [];
let total = 0;
for (const utxo of sortedUTXOs) {
selected.push(utxo);
total += utxo.value;
if (total >= targetAmount) break;
}
return selected;
}-
@scure/btc-signer - Modern, audited Bitcoin transaction library
import { Transaction } from '@scure/btc-signer';
-
bitcore-lib - Alternative to bitcoinjs-lib
import bitcore from 'bitcore-lib';
-
bcoin - Full Bitcoin implementation with transaction creation
import { MTX } from 'bcoin';
The typical pattern for using bigmi with transaction creation:
-
Fetch blockchain data with bigmi
- Get UTXOs
- Check balances
- Estimate fees from block stats
-
Create and sign transactions with bitcoinjs-lib
- Build PSBT
- Add inputs and outputs
- Sign with private keys
-
Broadcast and monitor with bigmi
- Send raw transaction
- Wait for confirmations
- Handle replacements
- Never expose private keys in client-side code
- Use hardware wallets for production applications
- Validate all inputs before creating transactions
- Test on testnet before mainnet deployment
- Implement proper error handling for all edge cases
async function safeSendBitcoin(
client: Client,
fromAddress: string,
toAddress: string,
amount: number,
privateKey: Buffer,
options?: {
feeRate?: number;
rbfEnabled?: boolean;
}
) {
try {
// Validate addresses
const fromInfo = getAddressInfo(fromAddress);
const toInfo = getAddressInfo(toAddress);
// Check balance
const balance = await getBalance(client, { address: fromAddress });
if (balance < amount + 1000) {
throw new Error('Insufficient balance');
}
// Get UTXOs
const utxos = await getUTXOs(client, {
address: fromAddress,
minValue: amount + 1000,
});
if (utxos.length === 0) {
throw new Error('No UTXOs available');
}
// Create transaction
const psbt = new bitcoin.Psbt();
let totalInput = 0;
for (const utxo of utxos) {
psbt.addInput({
hash: utxo.txId,
index: utxo.vout,
sequence: options?.rbfEnabled ? 0xfffffffd : 0xffffffff,
witnessUtxo: {
script: Buffer.from(utxo.scriptHex, 'hex'),
value: utxo.value,
},
});
totalInput += utxo.value;
}
// Add recipient output
psbt.addOutput({
address: toAddress,
value: amount,
});
// Calculate and add change
const fee = await estimateFee(client, utxos.length, 2);
const change = totalInput - amount - fee;
if (change < 0) {
throw new Error('Insufficient funds for fee');
}
if (change > 546) { // Dust threshold
psbt.addOutput({
address: fromAddress,
value: change,
});
}
// Sign transaction
const keyPair = bitcoin.ECPair.fromPrivateKey(privateKey);
psbt.signAllInputs(keyPair);
psbt.finalizeAllInputs();
// Broadcast
const txHex = psbt.extractTransaction().toHex();
const txId = await sendUTXOTransaction(client, { hex: txHex });
// Wait for confirmation
const confirmed = await waitForTransaction(client, {
txId,
txHex,
senderAddress: fromAddress,
confirmations: 1,
onReplaced: (replacement) => {
console.log('Transaction replaced:', replacement);
},
});
return {
txId,
fee,
confirmed,
};
} catch (error) {
console.error('Transaction failed:', error);
throw error;
}
}This separation of concerns makes sense as transaction creation involves key management and signing, which are intentionally kept separate from blockchain data operations.