Java Spring Boot Wallet
I have been a Java developer for more than ten years. But since I pursued a career with Blockchain, I was not able to do some java coding for more than a year now. One lazy Saturday morning, I decided to create something using Java — a bitcoin wallet.
I used bitcoinj for this wallet. Most of the codes came from their “Getting Started” page. Bitcoinj is a java implementation of the bitcoin protocol. What I like about this library is that it does not have to download a whole copy of the Bitcoin Core. Instead it uses simplified verification process. You can read this to learn more. So this is a very good option if you want to implement a wallet for mobile devices like an android app.
But this is not going to be an android app. But more of a web app which you can easily run in your machine. To do so, I also used Spring Boot. Do note that you can also create a desktop app using spring boot. I just prefer a web app.
Assumptions
- You know Java, Spring and Springboot.
- You understand Blockchain and Bitcoin.
- Understand that this is not a production ready wallet.
Create the Spring Boot App
Start by going to https://start.spring.io/ and create your spring boot app. We just need to add “Web” for the dependency. Generate and download the project then open it using your favorite Java IDE. I am using IntelliJ by the way. Add these additional dependencies
<dependency>
<groupId>org.bitcoinj</groupId>
<artifactId>bitcoinj-core</artifactId>
<version>0.15</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>27.1-jre</version>
</dependency>
Spring Beans
Let’s now create the beans that will be used by the wallet. I created a configuration file that contains all of these beans.
NetworkParameter
From the documentation: “NetworkParameters contains the data needed for working with an instantiation of a Bitcoin chain.” Almost all of the APIs of Bitcoinj needs a NetworkParameter so this needs to be defined first.
// Other properties here
@Value("${bitcoin.network}")
private String network;
@Bean
public NetworkParameters networkParameters() {
if(network.equals("testnet")) {
return TestNet3Params.get();
} else if(network.equals("regtest")) {
return RegTestParams.get();
} else {
return MainNetParams.get();
}
}
bitcoinJ supports 3 types of networks: testnet, mainnet and a regression test. This wallet will only connect to the testnet. The value for network will come from the application.properties with a key of bitcoin.network.
WalletAppKit
To be able to receive and send money, BitcoinJ needs to have a properly configured instances of BlockChain, BlockStore, PeerGroup and Wallet. Good thing they provided a wrapper WalletAppKit to simplify the setup. So let’s just use that
@Value("${bitcoin.file-prefix}")
private String filePrefix;@Value("${bitcoin.file-location}")
private String btcFileLocation;@Bean
public WalletAppKit walletAppKit(@Autowired NetworkParameters networkParameters) {
return new WalletAppKit(networkParameters,
new File(btcFileLocation), filePrefix) {
@Override
protected void onSetupCompleted() {
if (wallet().getKeyChainGroupSize() < 1) {
wallet().importKey(new ECKey());
}
}
};
}
Once you start downloading the blockchain, 2 files will be create under btcFileLocation: .wallet file and .spvchain file with a prefix filePrefix. The onSetupCompleted will check if the wallet has at least one key. If not, then it will just create one.
Your configuration file should look like this
MyWallet
Let’s now create a new java class MyWallet (or pick a name you are comfortable with). This class is responsible for downloading the blockchain and sending and receiving money.
Download the blockchain
Let’s add the dependencies as well as the method that will download the blockhain.
@Autowired
private WalletAppKit walletAppKit;@Autowired
private NetworkParameters networkParameters;@PostConstruct
public void start() {
walletAppKit.startAsync();
walletAppKit.awaitRunning(); Address sendToAddress = LegacyAddress.fromKey(networkParameters,
walletAppKit.wallet().currentReceiveKey());
System.out.println("Send coins to: " + sendToAddress);
}
The WalletAppKit is needed to perform receiving and sending money. NetworkParameters like I mentioned before is needed by almost all of bitcoinJ’s APIs. We begin the blockchain download by calling startAsync(). As the method implies, this will start the download asynchronously. The awaitRunning() call is to block any other operations before startAsync() finishes. There are better ways to make sure we download everything before we can do anything on this wallet, but this will suffice for now.
At this point you can already start your app. Once the download is done you should see your wallet address from the console like so. This is your wallet address. Keep this safe.

Receiving Money
Receiving money in bitcoinJ is an event. So to be able to do something if we received money, we just need to listen to this event. To receive money, we just need to register a listener by calling the method Wallet#addCoinsReceivedEventListener.
walletAppKit.wallet().addCoinsReceivedEventListener(
(wallet, tx, prevBalance, newBalance) -> {
Coin value = tx.getValueSentToMe(wallet);
System.out.println("Received tx for " + value.toFriendlyString());Futures.addCallback(tx.getConfidence().getDepthFuture(1),
new FutureCallback<TransactionConfidence>() {
@Override
public void onSuccess(TransactionConfidence result) {
System.out.println("Received tx " +
value.toFriendlyString() + " is confirmed. ");
}
@Override
public void onFailure(Throwable t) {}
}, MoreExecutors.directExecutor());
});
Add these code after awaitRunning().
With bitcoin, we could avoid double spending with what they call “confidence” system where a transaction can be validated by the “miners”. BitcoinJ has Confidence class which contains data that can help us decide if a transaction is valid or not.
Also BitcoinJ makes heavy use of Futures. When it comes to concurrent programming, Futures is a really helpful class. Checkout the Guava project for more information. For our received money listener, we just need 1 confirmation for the transaction to be valid. To change the minimum confirmation, just update this code: tx.getConfidence().getDepthFuture(1)
Sending Money
public void send(String value, String to) {
try {
Address toAddress =
LegacyAddress.fromBase58(networkParameters, to); //1
SendRequest sendRequest = SendRequest.to(toAddress,
Coin.parseCoin(value)); //2
sendRequest.feePerKb = Coin.parseCoin("0.0005"); //3
Wallet.SendResult sendResult =
walletAppKit.wallet().sendCoins(walletAppKit.peerGroup(),
sendRequest); //4
sendResult.broadcastComplete.addListener(() ->
System.out.println("Sent coins onwards! Transaction hash is " + sendResult.tx.getTxId()),
MoreExecutors.directExecutor()); //5
} catch (InsufficientMoneyException e) {
throw new RuntimeException(e);
}
}
For the sake of simplicity, this send method does not do any validation. To call this method, the amount in your wallet should be enough. Let’s discuss this code line by line
- We convert the argument to to an Address instance. The simplest way is to just use LegacyAddress.fromBase58. Please consult the documentation for other ways that will suit your needs
- Here we create an instance of SendRequest class using the to address and the value.
- feePerKb is not required. But if we want to have a faster confirmation, we need to set a fee. For this code, we are giving 0.005 per kilobyte of created transaction
- And finally we send the coins.
- The last part is just a listener telling us that the bitcoin network accepts our transaction.
That’s it. You now have a fully working Wallet. Your MyWallet class should now look like this.
Some more coding
Since this is a web app, we need to create controllers.
import com.gcbjr.btcnetwork.bitcoin.MyWallet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HomeController {
@Autowired
private MyWallet myWallet;
@RequestMapping
public String index() {
return "Greetings from Spring Boot!";
}
@RequestMapping(value = "/send")
public String send(@RequestParam String amount, @RequestParam String address) {
myWallet.send(amount, address);
return "Done!";
}
}
We need a way to invoke sending of coins. So we created the endpoint /send for that purpose. The index() is a good way to know if our spring boot app started successfully.
Lastly, make sure you have application.properties under src/main/resources which contains the settings needed by the app
bitcoin.network=testnet
bitcoin.file-prefix=my-wallet-service-testnet
bitcoin.file-location=/location/of/your/key
You can now start the app by running your main class. Go to http://localhost:8080/ to check if your app is up and running.
Test receiving coins
To receive coins, we can simply request from test faucet like this https://coinfaucet.eu/en/btc-testnet/. Paste your wallet address and you should see this printed in your console. The amount varies.

Once we have at least 1 confirmation, you should see this printed next

At this point, we can already assume that our transaction is valid. Check your new balance. We are going to send coins to another address.
Test sending coins
To test sending coins, we just need to call http://localhost:8080/send?amount={amount}&address={address_to_send_to} You need a second testnet address which you can easily find in the internet or just send it back to the testnet address the faucet displayed when you requested for funds. Our /send endpoint will display “done” if it does not encounter any errors. Check your console for the transaction hash. To check the details of this transaction, you can go to https://live.blockcypher.com/btc-testnet/ and paste the hash.

That’s it! You now have a working Bitcoin wallet written entirely using Java. If you are interested and want to create a production ready wallet app, you need to read more about BitcoinJ especially the security part. They have a really good documentation. Shout-out to the bitcoinJ team and all contributors for making this awesome library! The complete wallet app can be found here.