web3j is a Java library that can communicate with Ethereum clients (nodes) via JSON-RPC. In this article, I will introduce the basic usage of web3j.
Topic
First, start the Ethereum client and enable JSON-RPC communication.
For example, start Geth
to enable JSON-RPC communication via HTTP.
When you're ready, use web3j to connect to your Ethereum client.
//HTTP connection
Web3j web3j = Web3j.build(new HttpService("http://<ip address>:<port>"));
//IPC connection(Unix)
Web3j web3 = Web3j.build(new UnixIpcService("/path/to/socketfile"));
//IPC connection(Windows)
Web3j web3 = Web3j.build(new WindowsIpcService("/path/to/namedpipefile"));
After successfully connecting to the Ethereum client, let's communicate with JSON-RPC. JSON-RPC communication with web3j is very easy.
web3_clientVersion
For example, to call the JSON-RPC web3_clientVersion
method, write as follows.
(Web3_clientVersion
is a method to check the version of Ethereum client)
//Connect with Ethereum client
Web3j web3j = Web3j.build(new HttpService("http://localhost:8545"));
// JSON-Send RPC request and receive response
Web3ClientVersion response = web3j.web3ClientVersion().send();
//Output result
System.out.println(response.getResult());
output(Example)
Geth/v1.8.17-stable-8bbe7207/windows-amd64/go1.11.1
As you can see, web3j provides its own Java method for requesting each JSON-RPC method.
eth_getBlockByNumber The following is an example where you need to specify method arguments. Here, let's use the JSON-RPC ʻeth_getBlockByNumber` method to get the latest ("latest") block information.
//Connect with Ethereum client
Web3j web3j = Web3j.build(new HttpService("http://localhost:8545"));
// JSON-Send RPC request and receive response
EthBlock response = web3j.ethGetBlockByNumber(
DefaultBlockParameterName.LATEST, //block number. Here is a tag that represents the latest block"latest"To specify
false //If true, get transaction details. If false, get only transaction hash
).send();
//Output result
Block block = response.getResult();
System.out.println("hash:" + block.getHash());
System.out.println("number:" + block.getNumber());
output(Example)
hash:0x7abc74f4b77054a378cd8969b77ca207ed74ff925bd751314186180d37818d33
number:7311
In this way, method arguments can also be specified according to the JSON-RPC specifications, so it's very easy to understand.
eth_sendTransaction The following is an example that involves issuing a transaction. Here, let's send Ether using JSON-RPC ʻeth_sendTransaction`.
But before that, you need to unlock your account.
If the Ethereum client is Geth
, JSON-RPC provides a personal_unlockAccount
method, so use this to unlock your account and then send Ether.
//Connect with Ethereum client
//Admin is a subclass of Web3j"personal_unlockAccount"Request processing is implemented.
Admin web3j = Admin.build(new HttpService("http://localhost:8545"));
// "personal_unlockAccount"Send a request and receive a response
PersonalUnlockAccount unlockAccountResponse = web3j.personalUnlockAccount(
"0x766a1c4737970feddde6f2b8659fca05bd0339ab", //address
"pass1" //password
).send();
//If the unlock is successful, send Ether
if (unlockAccountResponse.getResult()) {
// "eth_sendTransaction"Create an object to pass to the argument of
Transaction transaction = Transaction.createEtherTransaction(
"0x766a1c4737970feddde6f2b8659fca05bd0339ab", // from
null, //nonce. Not specified this time, so null
null, //gasPrice. Not specified this time, so null
null, //gas Limit. Not specified this time, so null
"0x0cbc0fe59d39e58d7369ca436fe5c6b4b71341d5", // to
BigInteger.valueOf(100) // value
);
// "eth_sendTransaction"Send a request and receive a response
EthSendTransaction sendTransactionResponse = web3j.ethSendTransaction(transaction).send();
//Output result
System.out.println(sendTransactionResponse.getResult());
}
output(Example)
0x3557db13b5ac56f1e3e23ab9eeccd11b2c4daf73e416496d53c5bba377202a61
web3j offers useful features that go beyond simply calling JSON-RPC methods. Here, we will introduce the following three functions.
--Processing using a wallet file --Contract wrapper class --Process events reactively
What is a wallet file? You might think that.
This is what is called a "key file" in Geth
. The key file is created when you run geth account new
.
Normally, in Geth
, the key file is placed in" \ <datadir > / keystore ".
This "key file" is called a "Wallet file" in web3j.
In web3j, the process related to unlocking the account is simplified by using the wallet file. Here, let's send Ether using a wallet file.
First, place (copy) the wallet file in a location that can be accessed from the environment in which the web3j program is executed. When you're ready, use your wallet file to send Ether.
//Connect with Ethereum client
Web3j web3j = Web3j.build(new HttpService("http://localhost:8545"));
//Load wallet file
Credentials credentials = WalletUtils.loadCredentials(
"pass1", //Account password
"/pass/to/walletfile"
);
//Send Ether
TransactionReceipt receipt = Transfer.sendFunds(
web3j,
credentials,
"0xdc87fc3ac1ec09ed43623dd9da4c6f3d792a88f5", // to
new BigDecimal("1000"), // value
Unit.ETHER // unit
).send();
Once you load the wallet file, you can use it as many times as you like.
If you want to issue multiple transactions, you don't have to request personal_unlockAccount
multiple times, which simplifies the process.
web3j provides a tool (command line tool) to generate a wrapper class for contracts.
Wrapper classes make contract deployment and method invocation very simple.
(Of course, it is also possible to request JSON-RPC ʻeth_sendTransaction or ʻeth_call
to deploy the contract or call the method without using the wrapper class.)
Here, as a sample, the wrapper class of the following source written in Solidity is generated.
Token.sol
pragma solidity ^0.5.0;
contract Token {
uint256 public totalSupply;
mapping (address => uint256) public balanceOf;
constructor (uint256 _initialSupply) public {
totalSupply = _initialSupply;
balanceOf[msg.sender] = _initialSupply;
}
function transfer(address _to, uint256 _value) public {
require(balanceOf[msg.sender] >= _value);
require(balanceOf[_to] + _value >= balanceOf[_to]);
balanceOf[msg.sender] -= _value;
balanceOf[_to] += _value;
}
}
First, compile the source using Online Compiler. You can get the ABI and bytecode by compiling the source. Save the obtained ABI and bytecode to a file.
In the sample, I saved the ABI and bytecode to a file as follows.
Token.abi
[{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_initialSupply","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]
Token.bin
0x608060405234801561001057600080fd5b506040516020806103a28339810180604052602081101561003057600080fd5b81019080805190602001909291905050508060008190555080600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550506103068061009c6000396000f3fe608060405260043610610051576000357c01000000000000000000000000000000000000000000000000000000009004806318160ddd1461005657806370a0823114610081578063a9059cbb146100e6575b600080fd5b34801561006257600080fd5b5061006b610141565b6040518082815260200191505060405180910390f35b34801561008d57600080fd5b506100d0600480360360208110156100a457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610147565b6040518082815260200191505060405180910390f35b3480156100f257600080fd5b5061013f6004803603604081101561010957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061015f565b005b60005481565b60016020528060005260406000206000915090505481565b80600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101515156101ad57600080fd5b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205481600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054011015151561023c57600080fd5b80600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555080600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540192505081905550505056fea165627a7a72305820f97c3c938c56e1b594db9c9b681f3a08bb4ef81ef0fa5888d5982034167a44ba0029
Next, get the command line tools. You can download the command line tools from the GitHub Release Page (https://github.com/web3j/web3j/releases).
Unzip the downloaded compressed file and execute the executable file under the bin directory as follows.
web3j solidity generate -a=/path/to/Token.abi -b=/path/to/Token.bin -o=/path/to/src/main/java -p=package.name
If the command is successful, a wrapper class with the name "Token.java" will be generated in the directory specified by -o and -p.
Now that we're ready, let's use the wrapper class to deploy the contract and call the method.
//Connect with Ethereum client
Web3j web3j = Web3j.build(new HttpService("http://localhost:8545"));
//Load wallet file
Credentials credentials = WalletUtils.loadCredentials(
"pass1", //Account password
"/pass/to/walletfile"
);
//Deploy contract
Token token = Token.deploy(
web3j,
credentials,
BigInteger.valueOf(1000000000L), // gasPrice
BigInteger.valueOf(4700000L), // gasLimit
BigInteger.valueOf(100000000000L) //initialSupply (constructor argument)
).send();
//Output the address of the contract
System.out.println("contract address: " + token.getContractAddress());
//Method call (no transaction)
System.out.println("totalSupply: " + token.totalSupply().send());
System.out.println("balance of user1: " + token.balanceOf("0x766a1c4737970feddde6f2b8659fca05bd0339ab").send());
//Method call (with transaction)
System.out.println("Sending ether to user2...");
TransactionReceipt tran = token.transfer("0xdc87fc3ac1ec09ed43623dd9da4c6f3d792a88f5", BigInteger.valueOf(1000L)).send();
//Result output
System.out.println("transaction hash: " + tran.getTransactionHash());
System.out.println("balance of user1: " + token.balanceOf("0x766a1c4737970feddde6f2b8659fca05bd0339ab").send());
System.out.println("balance of user2: " + token.balanceOf("0xdc87fc3ac1ec09ed43623dd9da4c6f3d792a88f5").send());
output(Example)
contract address: 0x7b1530efbcbc4e9588fea3e505a95a3aeb84f3ed
totalSupply: 100000000000
balance of user1: 100000000000
Sending ether to user2...
transaction hash: 0x47cea479cd5aa3b4fc2fda5a0fcf6f5edc83ad2692a10355338b43aad9de41ef
balance of user1: 99999999000
balance of user2: 1000
You can see that the contract deployment and method invocation can be described very simply.
Finally, I will introduce the event monitoring function. web3j provides the ability to handle Ethereum events reactively.
Event monitoring using JSON-RPC usually involves first registering a filter using ʻeth_newBlockFilter, ʻeth_newFilter
, etc., and then polling the event using ʻeth_getFilterChanges`. ..
This is very troublesome, isn't it?
By using the function provided by web3j, event monitoring can be described as follows.
//Connect with Ethereum client
Web3j web3j = Web3j.build(new HttpService("http://localhost:8545"));
//Register filter (start event monitoring)
Disposable disposable = web3j.blockFlowable(false).subscribe(ethBlock -> {
//When a new block occurs, perform the following processing
Block block = ethBlock.getBlock();
System.out.println(block.getNumber());
});
//Remove filter (end event monitoring)
disposable.dispose();
It's very simple to write.
Recommended Posts