I want to generate a new receiving address for my bitcoin wallet for each new user. Bitcoin transferred to all these address should be received in my bitcoin wallet. I don't want to expose my private key or seed on the server.I found that this is how HD wallets work so I tried this.

const bip39 = require('bip39')
const bitcoin = require('bitcoinjs-lib')   //ver 3.0.2

let phrase = 'width humor wheat sad obscure outer ancient grab edit labor record express devote humble recipe occur refuse tooth original fluid learn scheme fuel assault';
let seedBuffer = bip39.mnemonicToSeedSync(phrase)
// <Buffer 85 19 18 81 cf 0e cc f7 9c 5d 61 ......

let masterNode = bitcoin.HDNode.fromSeedBuffer(seedBuffer)
// Deriving the first account based on BIP44
let account0 = masterNode.derivePath("m/44'/0'/0'")

let xpubString = account0.neutered().toBase58()
let address0FromXpub = bitcoin.HDNode.fromBase58(xpubString)

let address0 = address0FromXpub.derivePath("0/0").keyPair.getAddress()
let address1 = address0FromXpub.derivePath("0/1").keyPair.getAddress()
let address2 = address0FromXpub.derivePath("0/2").keyPair.getAddress()
let address3 = address0FromXpub.derivePath("0/3").keyPair.getAddress()
// address0.toBase58()

but I tried sending bitcoin to the generated address and didn't received any? If anyone could help.

  • Which address did you send to? Additionally, your code above has a seed phrase in it, so you're not really hiding the keys on your server Jun 5, 2020 at 14:58
  • I used address0 and address1. Once I have generated the xpubString I don't need to use the above steps again so only the extended key generation code will be on server. Jun 6, 2020 at 11:58

1 Answer 1


If you want to do Bitcoin stuff in nodejs, I recommend using bcoin:

repo: https://github.com/bcoin-org/bcoin

docs, guides: https://bcoin.io

It has a fully-validating full node implementation, SPV implementation, and BIP44 wallet that is used by Purse.io and several other Bitcoin-accepting merchants for this exact use case: Each customer gets a dedicated account and the wallet can track balances per-user, generating new addresses each time the current receive is used (a la BIP44)

You can set up your server with a watch-only wallet to report incoming deposits, and keep the private keys either offline or on more secure, local machine.


On secure or offline machine:

$ bcoin --daemon
$ bwallet-cli mkwallet --witness=true --id=HOT
$ bwallet-cli --if=HOT account get default
  "name": "default",
  "initialized": true,
  "witness": false,
  "watchOnly": false,
  "accountKey": "tpubDD3BFBuLYg2cspeDSWZgo9qv4qYi7KQF5oov8theqbFRUFqXPmsowPqAY1PQqnEdUe5JrFqf8ZGWLwEQEKB5WjJjxgMwcXmFuu1XDrG4RSr",

On "hot" production server:

$ bcoin --daemon
$ bwallet-cli mkwallet --id=WATCH --watch-only=true --account-key=tpubDD3BFBuLYg2cspeDSWZgo9qv4qYi7KQF5oov8theqbFRUFqXPmsowPqAY1PQqnEdUe5JrFqf8ZGWLwEQEKB5WjJjxgMwcXmFuu1XDrG4RSr
$ bwallet-cli --id=WATCH address default
  "name": "default",
  "account": 0,
  "branch": 0,
  "index": 1,
  "witness": false,
  "nested": false,
  "publicKey": "026f1afcab2942fd932760557943b287957bb8aa6ebe0f8dc64fa25862e23d52a2",
  "script": null,
  "program": null,
  "type": "pubkeyhash",
  "address": "mjdcszSLXHd6awrnM4bgJiFEbTFZdyw7de"


From here you can use API calls to create transaction templates and sign them on the offline machine.

If you are queasy about running an alternative implantation of Bitcoin, you can still run bitcoind on the hot server and run bcoin behind it (even in SPV mode) with bitcoind as it's --only= peer, like a Bitcoin Core Firewall


Generate static addresses

'use strict';

const bcoin = require('bcoin');
const m = bcoin.Mnemonic.fromPhrase(
  'abandon abandon abandon abandon abandon abandon'
  + ' abandon abandon abandon abandon abandon about');
const master = bcoin.HDPrivateKey.fromMnemonic(m);
const key0 = master.derivePath("m'/44'/0'/0'/0/0");
const ring0 = new bcoin.KeyRing({privateKey: key0});

Will output:

  witness: false,
  nested: false,
  publicKey: '03aaeb52dd7494c361049de67cc680e83ebcbbbdbeb13637d92cd845f70308af5e',
  script: null,
  program: null,
  type: 'pubkeyhash',
  address: '1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA'

You can verify this is address 0 from that mnemonic phrase using https://iancoleman.io/bip39/ and of course because this phrase is so abysmally insecure and used by developers testing on mainnet (sigh) there are several transactions to this address: https://blockstream.info/address/1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA

  • I haven't tried this but in my case, I only want to generate addresses for a single account. It's not a wallet and I don't have any users, I just want multiple receiving public addresses for my own account. Won't this be overkill for that? Jun 8, 2020 at 13:08
  • I added a new section to my answer that demonstrates how to generate static addresses from a minimal script using a phrase. This is the same logic the bcoin wallet uses. I noticed your phrase in the original question had a checksum error, so I'm demonstrating with a null mnemonic (a private key of all 0's, commonly used in testing)
    – pinhead
    Jun 8, 2020 at 14:21
  • I tried using the static method and the address I got in ring0.address is exactly same as I got using the method I added in question => address0. And also changing last digit of m'/44'/0'/0'/0/0 to 1 gives me address1 and so on. So I guess I am already generating the right address but when I sent some bitcoin to the address, none showed up in the account? Why is my account balance still 0 if the address is right? Jun 11, 2020 at 0:31
  • What wallet software are you using to check the account balance?
    – pinhead
    Jun 11, 2020 at 0:42
  • edge wallet edge.app Jun 11, 2020 at 11:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.