Web3j is a Java library which provides a wrapper for the ethereum JSON-RPC API and allows you to easily interact with the ethereum blockchain.
From the official documentation (see here: https://docs.web3j.io), Web3j is described as a highly modular, reactive, type safe Java and Android library for working with Smart Contracts and integrating with clients (nodes) on the Ethereum network. This allows you to work with the Ethereum blockchain, without the additional overhead of having to write your own integration code for the platform.
It’s features include:
- Complete implementation of Ethereum’s JSON-RPC client API over HTTP and IPC
- Ethereum wallet support
- Auto-generation of Java smart contract wrappers to create, deploy, transact with and call smart contracts from native Java code (Solidity and Truffle definition formats supported)
- Reactive-functional API for working with filters
- Ethereum Name Service (ENS) support
- Support for Parity’s Personal, and Geth’s Personal client APIs
- Support for Infura, so you don’t have to run an Ethereum client yourself
- Support for ERC20 and ERC721 token standards
- Comprehensive integration tests demonstrating a number of the above scenarios
- Command line tools
- Android compatible
- Support for JP Morgan’s Quorum via web3j-quorum
Including Web3j as a Dependancy
The easiest way to start using Web3j is to include it as a maven dependancy in your project. Web3j requires at least Java 8 or above. At the time of writing, the latest version is 4.5.0. To add the core module to your Java project use the following maven dependancy:
<dependency>
<groupId>org.web3j</groupId>
<artifactId>core</artifactId>
<version>4.5.0</version>
</dependency>
If you are using Spring Boot you can include the starter dependancy which will automatically auto-wire in the Web3j objects as beans in your Spring Boot application:
<dependency>
<groupId>org.web3j</groupId>
<artifactId>web3j-spring-boot-starter</artifactId>
<version>4.0.3</version>
</dependency>
There are also other Web3j modules which you can include as dependancies for specific purposes.
Starting an Ethereum Client
In order to start using Web3j we first need a running Ethereum node to interact with. The easiest way to get a test blockchain running is to use ganache-cli. We can use npm to install the cli using the following command:
npm install -g ganache-cli
ganache-cli is a lightweight Ethereum client which is designed for testing and makes it very easy to play around with an Ethereum node for testing purposes.
To start the node simply run:
ganache-cli
In the console output you should see the tool generate 10 account address and private keys which will each be pre-funded with 100 Ether. The JSON-RPC endpoint will also be running on localhost:8545:
$ ganache-cli
Ganache CLI v6.6.0 (ganache-core: 2.7.0)
Available Accounts
==================
(0) 0x72650Af3c6c8aD13B7Bf1461BA2A1C2546f0A042 (100 ETH)
(1) 0x42EE33bbBD18637f5aFE803fa0888679D1449c3f (100 ETH)
(2) 0x17c5a310B46051d4A5fb66D8d5d3D1C6Ea66B5bF (100 ETH)
(3) 0x542846DE951896b36df0E17815ce9E7f2e3cEB6b (100 ETH)
(4) 0xAA3707431E86306198C3640150BdC9D40621ba3E (100 ETH)
(5) 0xB756945eE6EA83e1f4519ebA971F9162d6D8B36a (100 ETH)
(6) 0x604FF707eE400A2cF265a157D40A13e81F714112 (100 ETH)
(7) 0xd70b732e505d04Fc243A06d1eb1Cd071F56a57a7 (100 ETH)
(8) 0x4be2068F59B11035EB057969ba395688A1f59888 (100 ETH)
(9) 0x67Ad08bf75BfAcd46a1BE3b5F9EDc8cF5911a74E (100 ETH)
Private Keys
==================
(0) 0xa01d91201c369fa87ccf0bbe6bc2a0018920ac96ac66c72b23a4ad01b86bac19
(1) 0x27b7c00f6f6ccb433743e25b1f39b87c6ea1418b7f10070bf2babbcd4266d6ff
(2) 0x162ff482d4ec829d2e321c9372e26feef3b70f601ee48f3384c5b519bba8f150
(3) 0x74cae28f96b82ab6c7aaab5b4e83671064d4b321e93fdd5adc5ee81c91b436f3
(4) 0x783464cdaf17466a720441dc6ac5bf0883e01df278f7c96f728d7d932097cdae
(5) 0x38957404abcfb583e2cfb520f50fc29e2f368adbb56c25e5d2fdaa9ee345aed3
(6) 0x75620f1ac930067dcd6033ef18e29f54be4cce098a58a1c7231cb2df62b67d28
(7) 0xff14ca6614e4144daf2629436d0cfa8dea9b3483b59741f7ec8b1848e2957712
(8) 0x0b777f1194257ce74012b54e5ce1d9121d5269aa257681fe74d877e9615cf8f7
(9) 0x1d6c34198d35ff2a97fad7771c245fcbf3464d672f6783884a6bd8e7fec99f69
HD Wallet
==================
Mnemonic: room venue hidden height rural reopen wage congress thought bleak fork sugar
Base HD Path: m/44'/60'/0'/0/{account_index}
Gas Price
==================
20000000000
Gas Limit
==================
6721975
Listening on 127.0.0.1:8545
Building a Web3j instance:
To start interacting with our local test Ethereum node we will first need to create a Web3j instance:
Web3j web3j = Web3j.build(new HttpService("http://localhost:8545"));
When you are finished you can cleanup any resources and disconnect using:
web3j.shutdown();
If you are using the Spring Boot starter as a dependancy you can simply auto-wire in the Web3j bean as follows:
@Autowired
private Web3j web3j;
And add the following to your application properties file:
web3j.client-address = localhost:8545
Sending Requests with Web3j
Whenever you call a Web3j method to interact with ethereum you can either call the send(), sendAsync(), or flowable(), methods to get and handle the response.
The send() method will simply wait and get a synchronous response containing the result of the call. This is the simplest option but may not always be the best one, depending on how long the request is expected to take. In this example, we connect to our running node and print the client version using the send() method:
Web3j web3j = Web3j.build(new HttpService("http://localhost:8545"));
Web3ClientVersion web3ClientVersion = web3j.web3ClientVersion().send();
String clientVersion = web3ClientVersion.getWeb3ClientVersion();
System.out.println(clientVersion);
Alternatively you can use the sendAsync() method which will make an asynchronous call without blocking the calling thread and will return a Java CompletableFuture object. Call the get() method on the future object to get the response of the method call. When the get() method is called, the calling thread will block until the result is returned or an exception is thrown (if there is an error):
CompletableFuture<Web3ClientVersion> future = web3j.web3ClientVersion().sendAsync();
Web3ClientVersion web3ClientVersion = future.get();
String clientVersion = web3ClientVersion.getWeb3ClientVersion();
System.out.println(clientVersion);
For more information about Java Completable Futures check out this link: https://www.baeldung.com/java-completablefuture
The final option is to use the flowable() method which will also make the call asynchronously and won’t block the calling thread but instead returns an RxJava Flowable object. To get the response of the call we need to add a call back which will process the result by calling the subscribe method:
Flowable<Web3ClientVersion> flowable = web3j.web3ClientVersion().flowable();
flowable.subscribe((web3ClientVersion) -> {
String clientVersion = web3ClientVersion.getWeb3ClientVersion();
System.out.println(clientVersion);
});
For more information about RxJava Flowables check out this link: https://www.baeldung.com/rxjava-2-flowable
After running the above code our application prints the ganache-cli Ethereum version to the console:
EthereumJS TestRPC/v2.7.0/ethereum-js
And the ganache-cli node running in the console shows that the JSON-RPC method was called:
Listening on 127.0.0.1:8545
web3_clientVersion
web3_clientVersion
web3_clientVersion
Interacting with Smart Contracts
First we need to compile a smart contract which we will later deploy and interact with using our local ganache Ethereum node. For this you will first need to install both the Web3j CLI and the Solidity compiler. On mac you can use these brew commands:
brew install web3j
brew install solidity
Compile a Smart Contract
Let’s start by creating a simple Solidity smart contract which simply provides functions to set a string and also to get the data back again. Start by creating a file containing this code:
pragma solidity ^0.5.11;
contract HelloWorld {
string private message;
function setMessage(string memory _message) public {
message = _message;
}
function getMessage() public view returns (string memory) {
return message;
}
}
After creating the file use the Solidity compiler CLI (solc) to compile the code and output the EVM binary and ABI which we will use later when we deploy the contract to the blockchain:
solc HelloWorld.sol --bin --abi --optimize -o target/
In the target directory we should now have two files generated containing the smart contract ABI and binary:
$ ls target/
HelloWorld.abi HelloWorld.bin
Next we can use the Web3j CLI to generate the smart contract wrapper for the HelloWorld contract:
$ web3j solidity generate -b target/HelloWorld.bin -a target/HelloWorld.abi -o src -p io.web3developer
_ _____ _ _ | | |____ (_) (_)
_ | | / / _
\ \ /\ / / _ \ '_ \ \ \ | | | / _ \
\ V V / / |) |./ / | | || () | _/_/ _|./ _/| |()|| _/
/ | |_/
Generating io.web3developer.HelloWorld … File written to src
Deploy a Smart Contract
Next we will deploy the HelloWorld smart contract to our local Ethereum blockchain. We can copy one of the private keys from the ganache-cli console logs to use to fund the deployment (Note: this is just for testing purposes. You should never hard code private keys in production code):
Web3j web3j = Web3j.build(new HttpService("http://localhost:8545"));
Credentials credentials = Credentials.create("0xea06d4fe50a5ddfd7f80d58cf807d20ccd25c7d7a0a00e8b2f474767baad63fe");
HelloWorld contract = HelloWorld.deploy(web3j, credentials,
DefaultGasProvider.GAS_PRICE, DefaultGasProvider.GAS_LIMIT).send();
You should see the following in the ganache-cli console logs indicating that the smart contract was deployed successfully:
Transaction: 0x66d308f298cd96e7f7606d474832827db0ec8398ae31bb71b65842e196330807
Contract created: 0x1adf47df368e6c477ef392c9013d7168f0daa554
Gas usage: 248266
Block Number: 1
Block Time: Tue Sep 10 2019 22:08:07 GMT+0800 (Australian Western Standard Time)
eth_getTransactionReceipt
Load a Smart Contract
To send a transaction we can use the instance of the smart contract wrapper which was returned when we deployed it or alternatively we can load the contract using an Ethereum address. We can load our HelloWorld contract using the contract address printed in the ganache-cli logs above:
HelloWorld contract = HelloWorld.load(
"0x1adf47df368e6c477ef392c9013d7168f0daa554", web3j, credentials,
DefaultGasProvider.GAS_PRICE, DefaultGasProvider.GAS_LIMIT);
Send a Transaction
Whenever you want to change the state of a smart contract you will need to send a transaction to the blockchain. To send a transaction we can use the smart contract wrapper and call the setMessage() method which will call the function with the same name on the smart contract in the blockchain. In this example we use the synchronous send() method which will wait until the transaction is mined and the data is added to the blockchain. We can also use the returned TransactionReceipt object to get the transaction hash of the mined transaction:
TransactionReceipt receipt = contract.setMessage("Hello World!").send();
System.out.println(receipt.getTransactionHash());
Call a Smart Contract
To call a smart contract function which doesn’t change any state we can use the generated smart contract wrapper method to get the data. In this case since we are not sending a transaction but rather are simply getting data from the blockchain, a TransactionReceipt object is not returned. Instead the data is just returned as a string from the smart contract wrapper method call:
String message = contract.getMessage().send();
System.out.println(message);
After running the above code you should see this output in the console:
EthereumJS TestRPC/v2.7.0/ethereum-js
0x66d308f298cd96e7f7606d474832827db0ec8398ae31bb71b65842e196330807
Hello World!
So there you have it. That gives you a basic introduction to using the Java Web3j library. We have successfully started a test Ethereum node, compiled a Solidity smart contract, deployed the contract, sent a transaction to update the smart contract state, and then called the smart contract to get the state data from the blockchain.