How to create a bitcoin wallet in Java

Ihor Biedin
4 min readNov 5, 2022

--

If you are interested in crypto, you might also be wondering where to keep your coins. People, who like trading, usually keep their money on exchanges, like Binance and others. But to be honest, it’s not the safest way. If you want your money to be in a safe place, it’s nice to hold it in a wallet to which only you can have access. What could be better than doing it by yourself? ;) In this article, I’m going to show you how can we build a bitcoin wallet in java using the bitcoinj library (you can read more about it here). We will also use Spring Boot's dependency management to make developing easy.

I assume that you are familiar with creating java projects and Spring, so let’s start with developing the wallet directly.

Firstly, let’s add bitcoinj dependency to our project.

dependencies {
// other your dependencies
implementation 'org.bitcoinj:bitcoinj-core:0.16.1'
}

Configuration

As bitcoinj’s documentation says, it uses callbacks for keeping track of transactions inside the blockchain. By default, these callbacks create an executor inside the methods, but it can affect our performance, so let’s create our executor.

@Configuration
public class ExecutorConfiguration {

@Bean
public Executor executor() {
//keep it simple just for example
return Executors.newFixedThreadPool(10);
}

}

Now, we need to configure network parameters. Bitcoin network has three modes: main (aka prod), test, and regtest. The first one is a real network with real coins, you should be careful with it and use it only after you are sure that everything works fine. The last one lets you run the blockchain by yourself and issue all coins you need manually. For now, we will use test net, it’s a network very similar to the main, but all coins have no value. You cannot create coins by yourself, but you can get them, there are a lot of services that allow you to borrow some coins, for example, https://bitcoinfaucet.uo1.net/. But be a responsible person, and send these coins back after testing, these days it’s hard to get those ones.

@Bean
public NetworkParameters networkParameters(@Value("${condition}") String condition) {
if("main".equals(condition)) {
//prod network
return MainNetParams.get();
} else {
//test network
return TestNet3Params.get();
}
}

Also, we need a context for managing all bounded objects. As we use Spring, we can just create a bean with the Context class.

@Bean
public Context context(NetworkParameters networkParameters) {
return new Context(networkParameters);
}

The last and most important configuration is the WalletAppKit. It combines a lot of service classes to simplify for you, as a developer, interaction with wallets, transactions and etc.

@Bean
public WalletAppKit walletAppKit(Context context) {
return new WalletAppKit(context, Script.ScriptType.P2SH, KeyChainGroupStructure.DEFAULT,
new File("path_to_file"), "file_prefix") {

@Override
protected void onSetupCompleted() {
//here we create a new key for the first time if it's not exist
if (wallet().getKeyChainGroupSize() < 1) {
wallet().importKey(new ECKey());
}

}
};
}

As you can see, the WalletAppKit requires context, which we created earlier. Besides that, we should set the script type, there are a few of them and it’s out of our article, you can learn about it more if you want. In our example, we will use P2SH. The library makes a backup of your wallet at runtime and writes it to the file, so you need to provide a path to the file and the file name.

When WalletAppKit is configured, we must run it in another thread. To do this, we can just use its built-in methods:

walletAppKit.startAsync();
walletAppKit.awaitRunning();

Usage

Ok, we set everything up. What’s next?

Let’s review some must-have methods, that the typical wallet does.

To see the balance of our wallet, we should:

BigDecimal balance = walletAppKit.wallet().getBalance().toBtc();

If you want to receive some money in your wallet, you need an address for it. Moreover, you may want to have more privacy and not use the same address for different transactions. Bitcoinj gives you this possible without any additional effort:

//generates a brand-new address every time
String yourAddress = walletAppKit.wallet().freshAddress(KeyChain.KeyPurpose.RECEIVE_FUNDS).toString();

It’s time to talk about how can we send the coins. Firstly, let’s define what we actually need for sending money to someone else. We need some amount to know how many coins will be sent, the recipient’s address, and the fee. You may already know, that all transactions in the bitcoin network cannot be done without a fee. Imagine, you use an app or an exchange, and they estimate a fee for you, but in our case, we need to do this by ourselves.

Bitcoinj requires a fee per Vkbyte. You can read more about it here: https://river.com/learn/terms/v/vbyte/. In our example, we set 20,000 satoshis.

We defined all the required parameters for building a transaction. You have an address, and with library API we can parse and validate it:

Address.fromString(walletAppKit.params(), recipientAddress);

Then, let’s create a SendRequest:

SendRequest sendRequest = SendRequest.to(address, amount);
sendRequest.setFeePerVkb(Coin.ofSat(20000l));

Now, we can eventually send coins to your recipient:

Wallet.SendResult sendResult = walletAppKit.wallet().sendCoins(walletAppKit.peerGroup(), sendRequest);

How can we track the success of the transaction? Every transaction has an id. To get it, we can take it from SendResult:

String id = sendResult.tx.getFee().toFriendlyString();

This id can be used in blockchain explorer, for instance: https://www.blockchain.com/ru/explorer.

Summary

Following these straightforward steps, we built our own bitcoin wallet ;) For more information about bitcoin APIs, you can find it in the documentation. Happy coding!

--

--