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.