Derive Deposit Adddress

Sometimes you'll want to programatically generate deposit addresses. Importantly deposit addresses can be generated completely offline. While it's unfortunately not standardized like bip32 it's a pretty straight forward process (and in-fact simpler than bip32).

The fundingPublicKey

So the first thing we need is the fundingPublicKey, this is the servers public key. Our goal is to send the bitcoin in such a way that it can be spent by spent by the server (using the corresponding private key) but also in a way that the server can know who was responsible for sending the bitcoin. The fundingPublicKey is just a constant, which is available from the hookedin-lib (if you choose to use it)

Result: "pubmp1q0aw9t2lz4hmw3pmxsde437uj9e245h87lf7ss9h7kqla3qwwhpf5hk69pg"

Our Address Generator

Next we're going to want to generate a (secp256k1) keypair to use as the basis for generating all our deposit addresses. This will function as the spiritual equivalent to an xpub in bip32. The moneypot wallet exposes its address generator public key, so it makes it easy to generate (and check) you're generating compatible addresses.

In this example we will be using the address generator (public key) pubmp1qvgth3qvw69ethjpqcc9ncvj8m4zuz7rkht728s46uqvalcn453w294q5zg, which in compressed format is:

Result: "0310bbc40c768b95de41063059e1923eea2e0bc3b5d7e51e15d700ceff13ad22e5"

Note: You don't need the private key of the Address Generator to generate deposits addresses. But without it, you'll be unable to claim the funds deposited to it. So you probably want to make sure you have it safely backed up.

Deriving from the Address Generator

We now want to offset the Address Generator public key by an index. The index represents which number deposit address we're generating. The most obvious way of deriving from the Address Generator would be using a simple ECC addition addressGenerator + index*G. This would work perfectly fine, but it has some very undesirable properties (namely someone can use the result of the derivation to derive all your deposit addresses). So we want an operator more akin to: addressGenerator + hash(concat(index, addressGenerator))*G.

This functionality is packaged up in the moneypot library as .derive function on a PublicKey. (TODO: link to impl detail). This works on arbitrary sized integers (and buffers). You can even try really big numbers like: 1234567890000000000000000000000n

So to derive our sample public key with the index 0 we get:

Result: "pubmp1q0072kr68hr6ngh8t9x2flkz44rkn2cfkfw38m0g5m7kye2ej8lpk7uz2yk"

Generating the final address

What need to convert our derivedPublicKey into a scalar, so we can add it to fundingPublicKey. For this we're going to use: hmacsha256('tweak', derivedPublicKey) then multiply it by the eliptic curve generator G and add it fundingPublicKey. With the resultant public key, we can convert it to a bitcoin (native segwit) address in the usual way (sha256 it, then rmd160 it, prepend 0 and convert to bech32). hookedin-lib provides convience functions for these operations. ECC addition is the .tweak method on a public key, and .toBitcoinAddress() will turn it into a bitcoin address (string). So putting everything together:

Result: "tb1qlsjvs2et0g09dp6wx8a3xlwgt67dvsplvj4ac6"

Remember you can edit the code (and run). So try increasing the index, and you'll get different addresses.