A little guide on Ethereum Development with Java — part I

Xyz Zyx
4 min readDec 19, 2018

--

This little buide is to serve as a general help guide for anyone wanting to develop Ethereum applications using the Java programming language. It’s meant to provide a starting point if you’re already pretty familiar with Ethereum and Java but don’t know where to to start on bringing it all together. You’ll learn how to interact with smart contracts and perform general blockchain tasks and queries using Java.

Forget about abigen

When you get started you notice that you can generate code from your smart contract using abigen

abigen — abi MyAbi.abi — pkg main — lang java — out MyContract.java

just forget about it. The code generated needs manual editing, it’s full of outdated stuff and in some cases it’s wrong.

Reading from the blockchain

considering this function in solidity

function getSomething(address ofAddress) public view returns (string memory) {
return something[ofAddress];
}

in Java you would read it something like this

notice the executeRemoteCallSingleValueReturn

public RemoteCall<String> getSomething(String ofAddress) {
final Function function = new Function(FUNC_SOMETHING,
Arrays.asList(new org.web3j.abi.datatypes.Address(ofAddress)),
Arrays.asList(new TypeReference<Utf8String>() {
}));
return executeRemoteCallSingleValueReturn(function, String.class);
}

this should sit in your contract class, and not where you would call it.

To call it you can use something like this

Single.fromCallable(() -> YourContractClass.getSomething(address)
.send())
.subscribeOn(io.reactivex.schedulers.Schedulers.io())
.subscribe(new SingleObserver<String>() {
@Override
public void onSubscribe(Disposable d) {
}

@Override
public void onSuccess(String something) {
Log("Something is " + something);
}

@Override
public void onError(Throwable e) {
Log(e);
}
});

things to notice. You can use this model to customize it according to your functions, like, for example:

calling a function that has 3 parameters

// gets a message at position
public RemoteCall<String> getMessageAtPosition(String fromAddress, String toAddress, String position) {
final Function function = new Function(FUNC_GETMESSAGEATPOSITION,
Arrays.asList(new org.web3j.abi.datatypes.Address(fromAddress), new org.web3j.abi.datatypes.Address(toAddress),
new org.web3j.abi.datatypes.Uint(new BigInteger(position))),
Arrays.asList(new TypeReference<Utf8String>() {
}));
return executeRemoteCallSingleValueReturn(function, String.class);
}

=====================

Writing to the blockchain

notice the executeRemoteCallTransaction

// Set Something
public RemoteCall<TransactionReceipt> updateSomething(String newID) {
final Function function = new Function(
FUNC_UPDATESOMETHING,
Arrays.asList(new org.web3j.abi.datatypes.Utf8String(newID)), Collections.emptyList());
return executeRemoteCallTransaction(function);
}

calling it like this

Single.fromCallable(() -> chat.updateSomething(newID)
.send())
.subscribeOn(io.reactivex.schedulers.Schedulers.io())
.subscribe(new SingleObserver<TransactionReceipt>() {
@Override
public void onSubscribe(Disposable d) {
}

@Override
public void onSuccess(TransactionReceipt txReceipt) {
Log("Tx Receipt: " + txReceipt);
}

@Override
public void onError(Throwable e) {
Log("onError: ", e);
}
});

==========

Congratulations, now you know how to read and how to write.

Bonus! Events

Events are a little trickery , but good thing you found this blog post. I’ll save you couple of hours of digging

Let’s take a complex event like this:

event messageSentEvent(address indexed from, address indexed to, string message, uint256 timestamp);

First write a simple class from it

public static class MessageSentEventEventResponse {
public Log log;
public String from;
public String to;
public String message;
public BigInteger timestamp;
}

uint256 translates into BigInteger. The rest usually fit into string.

You can read the events using the transaction receipt or the “log”

notice that you have indexed values and nonIndexed values.

public List<MessageSentEventEventResponse> getMessageSentEvents(TransactionReceipt transactionReceipt) {
List<Contract.EventValuesWithLog> valueList = extractEventParametersWithLog(MESSAGESENTEVENT_EVENT, transactionReceipt);
ArrayList<MessageSentEventEventResponse> responses = new ArrayList<>(valueList.size());
for (Contract.EventValuesWithLog eventValues : valueList) {
MessageSentEventEventResponse typedResponse = new MessageSentEventEventResponse();
typedResponse.log = eventValues.getLog();
typedResponse.from = (String) eventValues.getIndexedValues().get(0).getValue();
typedResponse.to = (String) eventValues.getIndexedValues().get(1).getValue();
typedResponse.message = (String) eventValues.getNonIndexedValues().get(0).getValue();
typedResponse.timestamp = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue();
responses.add(typedResponse);
}
return responses;
}

public MessageSentEventEventResponse getEventValuesMessageSent(Log log) {
Contract.EventValuesWithLog eventValues = extractEventParametersWithLog(MESSAGESENTEVENT_EVENT, log);
MessageSentEventEventResponse typedResponse = new MessageSentEventEventResponse();
typedResponse.log = log;
typedResponse.from = (String) eventValues.getIndexedValues().get(0).getValue();
typedResponse.to = (String) eventValues.getIndexedValues().get(1).getValue();
typedResponse.message = (String) eventValues.getNonIndexedValues().get(0).getValue();
typedResponse.timestamp = (BigInteger) eventValues.getNonIndexedValues().get(1).getValue();
return typedResponse;
}

Next is to “listen” to events

you need a filter. than on that filter you need to add the topic that you’re interested in. An easy way to get the topic is by looking at the debug output in remix IDE.

you can double check it online here https://emn178.github.io/online-tools/keccak_256.html

writing eventname(type,type,type,…)

private void listenForMessages() {EthFilter filter = new EthFilter(DefaultBlockParameterName.EARLIEST, DefaultBlockParameterName.LATEST, Chat.CHAT_CONTRACT);
filter.addSingleTopic(Chat.TOPIC_SEND_MESSAGE);

web3j.ethLogObservable(filter).subscribeOn(Schedulers.newThread()).subscribe(log -> {


YourContract.MessageSentEventEventResponse messageSentEventEventResponse = YourContract.getEventValuesMessageSent(log);


Log("From: " + messageSentEventEventResponse.from);
Log("To: " + messageSentEventEventResponse.to);
Log("Message: " + messageSentEventEventResponse.message);
Log("Timestamp: " + messageSentEventEventResponse.timestamp);

}

Congratulations

How about a class that I can use ?

Here’s my take on it

public class MyContract extends Contract {


public static final String MY_CONTRACT = "0xd11e5883246b48b3eb26b1beea082534b5a5e6ef";
public static final String TOPIC_SEND_MESSAGE = "0x19683520b8b6089d9fdceb19b596169f39b992d76be2cc3bc236de82044bf53a";

// Functions
public static final String FUNC_SENDMESSAGE = "sendMessage";
public static final String FUNC_GETMESSAGEATPOSITION = "abc";
// Events
public static final Event MESSAGESENTEVENT_EVENT = new Event("messageSentEvent",
Arrays.asList(new TypeReference<Address>() {
}, new TypeReference<Address>() {
}),
Arrays.asList(new TypeReference<Utf8String>() {
}, new TypeReference<Uint256>() {
}));

private static final String BINARY = "0x0000"; //don't need it

protected MyContract(String str, Web3j web3j, Credentials credentials, BigInteger bigInteger, BigInteger bigInteger2) {
super(BINARY, str, web3j, credentials, bigInteger, bigInteger2);
}

public static MyContract load(String str, Web3j web3j, Credentials credentials, BigInteger bigInteger, BigInteger bigInteger2) {
return new Chat(str, web3j, credentials, bigInteger, bigInteger2);
}
then all the functions from up.

What to do now? If this post saved you some time, please hit the clap button.

--

--

Xyz Zyx
Xyz Zyx

Responses (1)