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: pubhi1qgvq888xee50z5588tzxdnh46j7l64hc6qu9sj4h3v2gtnyasa9tc8mzlzl

Or more standardly, in compressed format:

Result: 0218039ce6ce68f152873ac466cef5d4bdfd56f8d038584ab78b1485cc9d874abc

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 hookedin 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) pubhi1q0jcjekqlgfjw9t03c2zrknug22jnacrfvs87fvf2s4ug3dkv68uvdv5y3h, which in compressed format is:

Result: 03e58966c0fa1327156f8e1421da7c429529f7034b207f2589542bc445b6668fc6

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 hookedin 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: pubhi1qfq7c047ljlvefmhx9xdv9908ulcew7drxh692mc3rqy2myg0zucu2qpa2v

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: tb1q43sw9x3e3nntaakhla29y5zychwd4zqzpzuvre

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