Q&A: Give me a JavaScript example for creating a BCH key pair and then sending BCH to...
- Option 1 — minimal-slp-wallet (recommended, high-level)
- Option 2 — Raw key derivation with @psf/bch-js
- Option 3 — bitcoinjs-lib (raw P2PKH derivation)
- Modern alternative — mainnet-js
- Key points & safety
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-walletsend()call handles UTXO selection, fee calculation (typically 250–550 sats), signing, and broadcast for you. - Keep
amountSatabove the dust limit (546 sats). - You can send to multiple recipients by adding more entries to the
receiversarray.
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 BTCbc1...(segwit) address — funds can be lost. Legacy1...addresses are still valid for BCH. - Derivation paths: BIP44 coin type
145is standard BCH;245is 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.,
localStoragewith 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