UNPKG

accept-bitcoin-payment

Version:

A self hosted JS libarary which you can use to accept bitcoin payments directly.

114 lines (103 loc) 3.48 kB
import bitcoin from "bitcoinjs-lib"; import bs58check from "bs58check"; import { BIP32Factory } from "bip32"; import * as ecc from "tiny-secp256k1"; const bip32 = BIP32Factory(ecc); /** * convertZpubToXpub * * Converts a zpub (BIP84) to an xpub by replacing its version bytes. * (zpub version: 0x04b24746; xpub version: 0x0488b21e) * @param {string} zpub - The zpub string. * @returns {string} The converted xpub string. * @throws Will throw an error if the zpub is invalid. */ export function convertZpubToXpub(zpub) { try { const data = Buffer.from(bs58check.decode(zpub)); // Replace the version bytes with xpub's version bytes. data.writeUInt32BE(0x0488b21e, 0); return bs58check.encode(data); } catch (error) { throw new Error("Invalid zpub provided: " + error.message); } } /** * getTxnsForAddress * * Uses Blockstream’s API to fetch transactions for a given address. * @param {string} address - The bitcoin address. * @returns {Promise<Array>} A promise that resolves to an array of transactions. */ async function getTxnsForAddress(address) { try { const response = await fetch( `https://blockstream.info/api/address/${address}/txs` ); if (!response.ok) { throw new Error(`HTTP error: ${response.status}`); } return await response.json(); } catch (error) { console.error("Error fetching transactions for address", address, error); return []; } } /** * deriveAddress * * Derives the P2WPKH address for a given node and index. * @param {object} baseNode - The BIP32 node for the branch. * @param {number} index - The child index. * @param {object} network - The bitcoin network object. * @returns {string} The derived bitcoin address. */ function deriveAddress(baseNode, index, network) { const child = baseNode.derive(index); return bitcoin.payments.p2wpkh({ pubkey: Buffer.from(child.publicKey), network, }).address; } /** * fetchPaymentInfo * * Scans addresses from a given zpub (external branch by default) to determine the next unused address: * - nextAddr: one selected next unused address based on offset. * * The scan stops after encountering a gap of `gapLimit` consecutive unused addresses. * * @param {string} zpub - The extended public key (zpub format). * @param {number} branch - Derivation branch (0 for external, 1 for change), default 0. * @param {number} offset - Offset from the last used address to select next address (default 1). * @param {number} gapLimit - Maximum consecutive unused addresses to allow (default 20). * @returns {Promise<object>} Object with property { nextAddr }. */ export async function nextAddress(zpub, offset = 0) { // Convert zpub to xpub and initialize the BIP32 node const xpub = convertZpubToXpub(zpub); const network = bitcoin.networks.bitcoin; const node = bip32.fromBase58(xpub, network); // Cache the branch node to avoid repeated derivation. const branchNode = node.derive(0); let currentIndex = 0; let nextAddrx = []; let offsetCount = offset; while (nextAddrx.length < 100) { const address = deriveAddress(branchNode, currentIndex, network); const txns = await getTxnsForAddress(address); if (txns.length === 0) { if (offsetCount > 0) { offsetCount--; } else { nextAddrx.push(address); } } currentIndex++; } return { index: currentIndex, address: nextAddrx, nextAddress: nextAddrx[0], }; }