Viewing File: /home/ubuntu/efiexchange-node-base/src/controllers/bitcoin/bitcoin.controller.ts
import axios from "axios";
import * as bitcoin from "bitcoinjs-lib";
import * as bip39 from "bip39";
import * as crypto from "crypto";
import * as ecc from "tiny-secp256k1";
import BIP32Factory from "bip32";
import { BITCOIN_HD_WALLET_STRING } from "@config/constant";
const getNetwork = (networkType: "mainnet" | "testnet") => {
return networkType === "mainnet"
? bitcoin.networks.bitcoin
: bitcoin.networks.testnet;
};
function getDeterministicMnemonic(userId: string): string {
const hash = crypto
.createHash("sha256")
.update(BITCOIN_HD_WALLET_STRING + userId)
.digest("hex");
const entropy = hash.substring(0, 32);
return bip39.entropyToMnemonic(entropy);
}
export const generateBitcoinHDWallet = async (req: any, res: any) => {
try {
let inputs = req.body;
if (!inputs.user_id) {
return res.sendError("User Id is required");
}
let { user_id } = inputs;
let networkType: any =
process.env.BITCOIN_NETWORK_TYPE === "mainnet" ? "mainnet" : "testnet";
const network = getNetwork(networkType);
const mnemonic = getDeterministicMnemonic(user_id);
const seed = bip39.mnemonicToSeedSync(mnemonic);
const bip32 = BIP32Factory(ecc);
const root = bip32.fromSeed(seed, network);
// BIP84 - Native SegWit
const purpose = 84;
const coinType = networkType === "mainnet" ? 0 : 1;
// const path = `m/${purpose}'/${coinType}'/0'/0/${user_id}`;
const path = `m/${purpose}'/${coinType}'/0'/0/0`; // Standard dervivation
const child = root.derivePath(path);
const { address } = bitcoin.payments.p2wpkh({
pubkey: Buffer.from(child.publicKey),
network,
});
let walletDetails = {
wallet_address: address,
// privateKey: child.toWIF(),
// mnemonic,
// path,
// publicKey: child.publicKey.toString(),
};
return res.sendResponse(
walletDetails,
"Bitcoin Address generated successfully."
);
} catch (error: any) {
if (error?.message) {
return res.sendError(error.message);
}
}
};
export const generateBitcoinLegacyHDWallet = async (req: any, res: any) => {
try {
let inputs = req.body;
if (!inputs.user_id) {
return res.sendError("User Id is required");
}
let { user_id } = inputs;
let networkType: any =
process.env.BITCOIN_NETWORK_TYPE === "mainnet" ? "mainnet" : "testnet";
const network = getNetwork(networkType);
const mnemonic = getDeterministicMnemonic(user_id);
const seed = bip39.mnemonicToSeedSync(mnemonic);
const bip32 = BIP32Factory(ecc);
const root = bip32.fromSeed(seed, network);
// BIP44 - Legacy Bitcoin
const purpose = 44;
const coinType = networkType === "mainnet" ? 0 : 1;
const path = `m/${purpose}'/${coinType}'/0'/0/0`; // Standard dervivation
const child = root.derivePath(path);
const { address } = bitcoin.payments.p2pkh({
pubkey: Buffer.from(child.publicKey),
network,
});
let walletDetails = {
wallet_address: address,
// privateKey: child.toWIF(),
// mnemonic,
};
return res.sendResponse(
walletDetails,
"Bitcoin Address generated successfully."
);
} catch (error: any) {
if (error?.message) {
return res.sendError(error.message);
}
}
};
export const getWalletDetails = async (req: any, res: any) => {
try {
const { address } = req.body;
if (!address) return res.sendError("Wallet address is required");
const networkType: "mainnet" | "testnet" =
process.env.BITCOIN_NETWORK_TYPE === "mainnet" ? "mainnet" : "testnet";
const baseURL =
networkType === "mainnet"
? "https://mempool.space/api"
: "https://mempool.space/testnet4/api";
// 1. Fetch basic wallet info
const addressRes = await axios.get(`${baseURL}/address/${address}`);
if (!addressRes) {
return res.sendError("Failed to fetch wallet address info");
}
const addressData = await addressRes.data;
if (!addressData) {
return res.sendError("Invalid wallet address");
}
const confirmedBalance = addressData.chain_stats.funded_txo_sum - addressData.chain_stats.spent_txo_sum;
const unconfirmedBalance = addressData.mempool_stats.funded_txo_sum - addressData.mempool_stats.spent_txo_sum;
const totalBalance = confirmedBalance + unconfirmedBalance;
const txCount = addressData.chain_stats.tx_count + addressData.mempool_stats.tx_count;
// 2. Fetch recent transactions
let transactions:any = [];
const txsRes = await fetch(`${baseURL}/address/${address}/txs`, {
method: 'GET',
});
if (!txsRes.ok) {
return res.sendError("Failed to fetch transactions");
}
const txData = await txsRes.json();
if (txData && Array.isArray(txData)) {
transactions = txData.map((tx: any) => {
return {
txid: tx.txid,
fee: tx.fee,
status: tx.status,
inputs: tx.vin?.map((input: any) => {
return {
address: input.prevout?.scriptpubkey_address,
value: (
parseFloat(input.prevout.value) / Math.pow(10, 8)
).toFixed(6),
};
}),
outputs: tx.vout?.map((output: any) => {
return {
address: output.scriptpubkey_address,
value: (parseFloat(output.value) / Math.pow(10, 8)).toFixed(6),
};
}),
};
});
}
const result = {
address,
confirmed_balance: (confirmedBalance / Math.pow(10, 8)).toFixed(6),
unconfirmed_balance: (unconfirmedBalance / Math.pow(10, 8)).toFixed(6),
total_balance: (totalBalance / Math.pow(10, 8)).toFixed(6),
tx_count: txCount,
transactions,
};
return res.sendResponse(result, "Wallet details fetched successfully.");
} catch (error: any) {
console.error("Error fetching wallet details:", error.message || error);
return res.sendError("Failed to fetch wallet details");
}
};
Back to Directory
File Manager