Interacting with the Ethereum Blockchain in Clojure

Dec 9, 2017 11:36 · 620 words · 3 minutes read

Recently, the Ethereum blockchain has been the subject of much interest, driven in part by the huge success of CryptoKitties. Unfortunately for those of us hoping to perform kitty market analysis using our favorite language, there isn’t any good information for how to interact with the Ethereum Blockchain in Clojure. This blog post aims to get you up and running talking to the Ethereum Blockchain.

Prerequisites

This post assumes you have a Geth node running locally on the default port. The easiest way to achieve this is with Docker. See: https://github.com/ethereum/go-ethereum/wiki/Running-in-Docker

Step 1: Create a new project

First, we use Leiningen to scaffold a new project for us:

$ lein new app catscan

Step 2: Add Web3j dependencies

In our project.clj file, we need to add the following dependency:

[org.web3j/core "3.1.1"]

Web3j is a lightweight, reactive, type safe Java and Android library for integrating with nodes on Ethereum blockchains

Java, you say? Good thing Clojure has fantastic Java interop!

Step 3: Import dependencies

The Clojure compiler needs to know what Java classes you’ll be using, and for that, we use the :import macro. I have this namespace declaration in a file I named blockchain.clj:

(ns catscan.blockchain
  (:import (org.web3j.protocol Web3j)
           (org.web3j.protocol.http HttpService)
           (rx.functions Action1)))

Now we have access to the Web3j tools we need for interacting with the blockchain!

Step 4: Interacting with the Blockchain

The getting started examples in the Web3j docs provide the following Java snippet for us:

Web3j web3 = Web3j.build(new HttpService()); // defaults to http://localhost:8545/
Web3ClientVersion web3ClientVersion = web3.web3ClientVersion().send();
String clientVersion = web3ClientVersion.getWeb3ClientVersion();

Let’s convert this to Clojure!

(def http (new HttpService))
(def web3j (Web3j/build http))
(-> web3j
    (.web3ClientVersion)
    (.send)
    (.getWeb3ClientVersion))

This makes a synchronous request, which (for me) returns the following result:

"Geth/v1.7.3-stable-4bb3c89d/linux-amd64/go1.7.3"

Synchronous requests are all well and good, but they are not conducive to allowing you to react to events on the blockchain. Fortunately, the next example provided in the documentation shows how to do the same thing asynchronously:

Web3j web3 = Web3j.build(new HttpService());  // defaults to http://localhost:8545/
web3.web3ClientVersion().observable().subscribe(x -> {
    String clientVersion = x.getWeb3ClientVersion();
    ...
});

Note the use of a Java 8 Lambda Function to respond to events asynchronously. Unfortunately, Java’s strong typing doesn’t allow us to simply pass a Clojure function as a callback, so we need to do a bit of research.

The RxJava documentation says that the subscribe method accepts arguments that extend the Action interface. There are a couple sub-interfaces that instantiate Action, and in our case, Action1 is one such sub-interface.

We need to reify the Action1 interface into a callback so that the subscribe function will accept as an argument for firing our requests asynchronously. So the Java example above, in Clojure, looks like:

(def callback
  (reify Action1
    (call [_ x] (println "received value:" x))))
   
(-> web3j
    (.web3ClientVersion)
    (.observable)
    (.subscribe callback))

Conclusion

The biggest hurdles (for me) when it comes to using Clojure to interact with the Ethereum blockchain boiled down to Java interop: knowing which Java classes to import, and how to interact with Java 8 Lambda functions.

Having solved both of those problems, the next steps for creating your own smart contracts (or services that respond to them) is to read up on all the great features provided by Web3j, and get coding!

With Gratitude

When I was stuck on the issue of how to deal with a Java 8 Lambda function in Clojure, I took to Clojurians Slack to see if kind strangers would fill in the gaps in my knowledge. Two such individuals stepped up:

It is with gratitude to the both of them for pointing me in the right direction with the use of reify for creating callback functions, and the mechanics for Java’s lambda functions work.