Hello
I am very new to using crypto related library and its my first time. I installed bitcoinjs-lib using npm, now I just want to generate address by using already created xpub that I have.
How exactly can I do this because I do not see example in homepage regarding importing xpub wallet.
I need this to use on server so whenever the api is called, it generates a new address for same wallet (read-only wallet in server)
There are a lot of examples of how to use BIP32 in the tests.
Please read through the tests.
https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js
But it in tests they require xpriv key, I don't want to use private key in server, just the xpub key.
Also, do I need to run bitcoin node to use this library ? i.e. download blockchain ?
fromBase58 accepts xpub public keys as well.
@WhiteBookmark Here's a working example for generating a 1... address given the public key for the abandon abandon ability passphrase (so you can test on your end and assure this is working correctly. Derivation path is hardened and follow m/44'/0'/0'/0/1
As said before, fromBase58 accepts a multiple input, be sure to read the tests and UNDERSTAND WHAT YOU ARE DOING.
PS ; No you do not need to download a blockchain to make this work. This is only derivation, not interaction with a blockchain.
const bjs = require('bitcoinjs-lib');
const bip32 = require('bip32');
const xpub = 'xpub6CzDCPbtLrrn4VpVbyyQLHbdSMpZoHN4iuW64VswCyEpfjM2mJGdaHJ2DyuZwtst96E16VvcERb8BBeJdHSCVmAq9RhtRQg6eAZFrTKCNqf';
const { address } = bjs.payments.p2pkh({
pubkey: bip32.fromBase58(xpub).derive(0).derive(1).publicKey,
});
console.log(`the addressString is ${address}`);
In case someone comes looking for the 3... segwit address format from xpub you can use this:
const bjs = require('bitcoinjs-lib');
const bip32 = require('bip32');
const xpub =
'xpub6CzDCPbtLrrn4VpVbyyQLHbdSMpZoHN4iuW64VswCyEpfjM2mJGdaHJ2DyuZwtst96E16VvcERb8BBeJdHSCVmAq9RhtRQg6eAZFrTKCNqf';
const { address } = bjs.payments.p2sh({
redeem: bjs.payments.p2wpkh({
pubkey: bip32
.fromBase58(xpub)
.derive(0)
.derive(1).publicKey,
}),
});
console.log(`the addressString is ${address}`);
I tried both methods.
Segwit method (3...) by @nopslip generated unique addresses and then we tried to do some transactions but we could not receive the BTC the explorer showed the transaction but it was not appearing to our wallets. I assume it is incompatible with the wallets.
Should we use ypub in order to make the Segwit address functional?
Legacy method (1...) by @robiiinos generated the address and we can see the transactions and funds but the issue is that its not unique for every new request.
Maybe someone can share a code snippet for the Legacy method that would generate a unique address for every request?
the explorer showed the transaction
That means the address got your transaction.
but it was not appearing to our wallets.
Which wallet? Most likely your wallet is deriving a different path. the example given by @nopslip was {xpub}/0/1 but used an xpub.
You mention ypub... are you using Electrum?
Something like this works (though it is not very efficient)
const bjs = require('bitcoinjs-lib');
const bip32 = require('bip32');
const b58 = require('bs58check');
const mainnet = bjs.networks.bitcoin; // Bitcoin mainnet
function anypubToXpub(xyzpub) {
let data = b58.decode(xyzpub)
data = data.slice(4)
data = Buffer.concat([Buffer.from('0488b21e','hex'), data])
return b58.encode(data)
}
function getYpubAddress(ypub, index = 0, isChange = false, network = mainnet) {
if (ypub.slice(0, 4) !== 'ypub') throw new Error('Not ypub')
const xpub = anypubToXpub(ypub)
return bjs.payments.p2sh({
redeem: bjs.payments.p2wpkh({
pubkey: bip32
.fromBase58(xpub)
.derive(isChange ? 1 : 0)
.derive(index).publicKey,
network,
}),
network,
}).address
}
// try it out
const myYpub = 'ypub6XpUW4GoVYQFuo1cSLm2YNh8cKy1juMZe22JqtmpaychiqAG1xSCCLxAFBs9woXoYjLoqyXAh5wg4UFsLyrDHzrS1mQK1KVautcuF7DMj1x'
console.log(getYpubAddress(myYpub, 0))
console.log(getYpubAddress(myYpub, 1))
console.log(getYpubAddress(myYpub, 2))
console.log(getYpubAddress(myYpub, 3))
/*
3Kwuyq6aq64YHWyoE2C5CmA1rQLsZtGsMv
3AVaU84ZdkFsxZDU5f9N6D75xx36nWVvMm
36ai7CX3i9SKY2gHfEr248cPWjrzcaPJ6K
3BXFYU5GmTt4WwPAHCp5k6jTVHusbyzGAj
*/
@junderw so what you are saying if we change index in last derive it gonna generate unique address? I assume this works with any type of key generation if its Segwit or Legacy? And I assume it has to be sequential?
At this point, I tested the legacy method and I can see the transaction if I generate a sequentially changed index or I set to the maximum index to 9. I think i get the point now that standard wallets scan 20 indexes range without transactions. So storing the index for every xPub generated address gonna solve the problem and there will be never more than 20 addresses without the transactions in between.
One more question. I can see you used first derive argument called isChange what exactly that means. Is it that it regenerates new address for this index if you put isChange = 1?
I ended up with this solution that provides me unique addresses per transaction.
const bjs = require('bitcoinjs-lib');
const bip32 = require('bip32');
async function getUnusedAddress(xpub, index = 0){
const { address } = bjs.payments.p2pkh({ pubkey: bip32.fromBase58(xpub).derive(0).derive(index).publicKey })
const { n_tx } = await GET({ url: 'https://blockchain.info/rawaddr/' + address })
if(!!n_tx) return getUnusedAddress(xpub, index + 1)
// You can store the index to the db here for the next requests
return address
}
@egislook Please read BIP32. All your questions are answered there.
Also, BIP44, BIP49, BIP84 explain specific derivation schemes for BIP32 that you are using.
Rather than trying to guess things based on snippets passed back and forth on Github, read the BIPs, it's faster.
Also, isChange should only be true or false.
If true, I pass 1 to derive, if false I pass 0.
The reason is in BIP44/49/84
I assume this works with any type of key generation if its Segwit or Legacy? And I assume it has to be sequential?
It does, but remember that ypub means that it must be p2sh-p2wpkh, zpub means it should be p2wpkh and xpub means legacy p2pkh. So with each type of extended pubkey you need a different payments combination.
Again, read BIP32/44/49/84
Most helpful comment
In case someone comes looking for the
3...segwit address format from xpub you can use this: