Q&A: Give me a JavaScript example for creating a BCH key pair and then sending BCH to...

Block answers a tech-support question about Give me a JavaScript example for creating a BCH key pair and then sending BCH to an address.

Question:

Give me a JavaScript example for creating a BCH key pair and then sending BCH to an address.


Answer:

Here’s a practical walkthrough. The easiest, most reliable path for BCH (as opposed to BTC) is the Permissionless Software Foundation (PSF) “Cash Stack” minimal-slp-wallet library, since it handles BCH’s CashAddr format and UTXO selection automatically. I’ll show that first, then a lower-level key-derivation example.

Option 1 — minimal-slp-wallet (recommended, high-level)

Install:

npm install minimal-slp-wallet --save

Create a key pair (HD wallet → mnemonic → address → WIF):

const BchWallet = require('minimal-slp-wallet')

async function main () {
  // Create a brand-new wallet (generates a 12-word BIP39 mnemonic)
  const bchWallet = new BchWallet()
  await bchWallet.walletInfoPromise // wait until the wallet is built

  const w = bchWallet.walletInfo
  console.log('Mnemonic  :', w.mnemonic)        // 12 words — KEEP SECRET
  console.log('CashAddr  :', w.cashAddress)     // bitcoincash:q...
  console.log('Legacy    :', w.legacyAddress)   // 1...
  console.log('PrivateKey:', w.privateKey)      // WIF (starts with K or L) — KEEP SECRET

  // You can also restore from an existing mnemonic:
  // const bchWallet2 = new BchWallet('minor bench until split suffer shine series bag avoid cruel orient aunt')
}
main()

The default derivation path is m/44'/245'/0'/0/0 (the SLP-aware BCH standard). You can pass { HdPath: "m/44'/145'/0'/0/0" } to use the standard BCH path.

Send BCH to an address:

const BchWallet = require('minimal-slp-wallet')

async function send () {
  // Initialize with the wallet's mnemonic so it can load its UTXOs/balance
  const bchWallet = new BchWallet(
    'minor bench until split suffer shine series bag avoid cruel orient aunt',
    { interface: 'rest-api', restURL: 'https://api.fullstack.cash/v5/' }
  )
  await bchWallet.initialize() // fetches balance, UTXOs, tokens

  const receivers = [
    {
      address: 'bitcoincash:qp2rmj8heytjrksxm2xrjs0hncnvl08xwgkweawu9h',
      amountSat: 100000 // 100,000 sats = 0.001 BCH. 1 BCH = 100,000,000 sats
    }
  ]

  try {
    const txid = await bchWallet.send(receivers)
    console.log('Sent! txid:', txid)
    // View at https://explorer.bitcoin.com/bch/tx/<txid>
  } catch (err) {
    console.error(err)
    if (err.message && err.message.includes('Insufficient')) {
      console.log('Not enough BCH to cover amount + fees.')
    }
  }
}
send()

Notes:

  • The minimal-slp-wallet send() call handles UTXO selection, fee calculation (typically 250–550 sats), signing, and broadcast for you.
  • Keep amountSat above the dust limit (546 sats).
  • You can send to multiple recipients by adding more entries to the receivers array.

Option 2 — Raw key derivation with @psf/bch-js

If you want finer control over key generation (still using BCH-aware tooling):

npm install @psf/bch-js --save
const BCHJS = require('@psf/bch-js')
const bchjs = new BCHJS()

async function createKeyPair () {
  // 1. Generate a 12-word mnemonic (128 bits of entropy)
  const mnemonic = bchjs.Mnemonic.generate()

  // 2. Derive the HD wallet node
  const rootSeed = await bchjs.Mnemonic.toSeed(mnemonic)
  const masterHDNode = bchjs.HDNode.fromSeed(rootSeed)

  // 3. Derive the BCH account (coin type 145; use 245 for SLP-aware wallets)
  const childNode = masterHDNode.derivePath("m/44'/145'/0'/0/0")

  const cashAddress = bchjs.HDNode.toCashAddress(childNode)
  const legacyAddress = bchjs.HDNode.toLegacyAddress(childNode)
  const wif = bchjs.HDNode.toWIF(childNode)
  const pubKey = bchjs.HDNode.toPublicKey(childNode)

  console.log({ mnemonic, cashAddress, legacyAddress, wif })
  return { childNode, cashAddress, wif }
}
createKeyPair()

bch-js is the low-level interface library beneath minimal-slp-wallet; building and signing a transaction manually with it is considerably more involved (you’d use its RawTransactions/TransactionBuilder utilities to assemble inputs, outputs, and sign each input with the WIF). For most send-BCH use cases, minimal-slp-wallet.send() is the right tool.

Option 3 — bitcoinjs-lib (raw P2PKH derivation)

If you already use bitcoinjs-lib:

npm install bitcoinjs-lib bip39 bip32 tiny-secp256k1 bchaddrjs --save
const bitcoin = require('bitcoinjs-lib')
const bip39 = require('bip39')
const { BIP32Factory } = require('bip32')
const ecc = require('tiny-secp256k1')
const bchaddr = require('bchaddrjs')

async function main () {
  const bip32 = BIP32Factory(ecc)

  const mnemonic = bip39.generateMnemonic()
  const seed = await bip39.mnemonicToSeed(mnemonic)

  // Use BCH mainnet params (bitcoinjs-lib ships them as networks.bitcoin)
  const root = bip32.fromSeed(seed, bitcoin.networks.bitcoin)
  const child = root.derivePath("m/44'/145'/0'/0/0") // BCH coin type = 145

  // bitcoinjs-lib yields a legacy (base58) BCH address…
  const legacyAddress = bitcoin.payments.p2pkh({
    pubkey: child.publicKey,
    network: bitcoin.networks.bitcoin
  }).address

  // …convert to the modern BCH CashAddr format
  const cashAddress = bchaddr.toCashAddress(legacyAddress)

  console.log({ mnemonic, legacyAddress, cashAddress })
}
main()

Caution: bitcoinjs-lib is BTC-oriented. Its PSBT/segwit tooling is not appropriate for BCH, and building/signing a BCH transaction with it requires the legacy TransactionBuilder (v5) plus BCH-specific sighash handling. For sending, prefer Options 1 or 2.

Modern alternative — mainnet-js

The mainnet.cash library is a clean, modern, token-aware option (all amounts are bigint in v3+):

import { Wallet } from 'mainnet-js'

const wallet = await Wallet.newRandom()
console.log(wallet.cashaddr)      // bitcoincash:q...
console.log(wallet.privateKeyWif) // KEEP SECRET

const tx = await wallet.send([
  { cashaddr: 'bitcoincash:qp2rmj8heytjrksxm2xrjs0hncnvl08xwgkweawu9h', value: 100000n }
])
console.log(tx.txId)

Key points & safety

  • Address format: BCH uses CashAddr (bitcoincash:q...). Never send BCH to a BTC bc1... (segwit) address — funds can be lost. Legacy 1... addresses are still valid for BCH.
  • Derivation paths: BIP44 coin type 145 is standard BCH; 245 is the SLP/token-aware convention. Paths differ, so the same mnemonic yields different addresses for each path.
  • Units: Amounts are usually in satoshis (1 BCH = 100,000,000 sats).
  • Security: Never transmit a mnemonic or WIF to a server or commit it to code. For browser apps, store/encrypt it locally (e.g., localStorage with encryption) and never expose it. For real funds, test on TestNet first (faucet.fullstack.cash).

Sources: PSF LLM Wiki pages minimal-slp-wallet.md and bch-js.md; the minimal-slp-wallet README (Permissionless-Software-Foundation/minimal-slp-wallet); and the mainnet.cash tutorial.


Public Q&A mirror from the Block tech-support bot.


Write a comment