How to create decentralised apps with Clojurescript re-frame and Ethereum

I decided to create this tutorial after creating funny Ethereum lottery http://emojillionaire.com/ with Clojurescript front-end, to share my experience.

In this tutorial, we will go through creating very simple decentralised “Twitter”. 
Code is on Github
Deployed at https://clojurescript-ethereum-example.herokuapp.com with smart contract on Ethereum Testnet.

TLDR; Summary:

My experience of using Clojurescript’s re-frame for writing front-end for Ethereum smart contracts is super positive. Both, with their event-based, real-time nature make perfect fit. At beginning, it was bit of a drag, since cljs libraries weren’t there yet, but after I created 2 interops (cljs-web3 and re-frame-web3-fx) it was a breeze. I dare to say using Clojurescript and re-frame gives you really significant advantage compared to developing Ethereum front-ends with Javascript.

This tutorial is primarily for Clojurescript developers with little or no knowledge of Ethereum.

Warning for Clojurescript developers: App uses re-frame v0.8, if you’re not familiar with it, you may not understand why some things are way they are.

So what is that Ethereum?

Ethereum is public blockchain platform, similar to Bitcoin, but it stores not only currency, but also arbitrary data with pieces of code to execute on those data. This code, stored in blockchain, is called Smart Contract and is written in language Solidity, which some kind of typed Javascript.

So basically, Ethereum is decentralised, immutable database with built-in currency support, where you can not only store your app’s data, but also API for accessing & changing those data. Interestingly, once you deploy your smart contract code into blockchain, it’s there “forever”, impossible to update, only new ones can be deployed.

Your smart contract can have 2 types of functions. Ones that change state (write into DB) and ones, which just read state, called “constant”. Constant functions are executed for free. Whereas for changing state you must pay. You pay in what’s called “gas”. Amount of gas user needs to pay is calculated from size of a binary code given function have. Usually it’s just few cents. This is to compensate for CPU work for all nodes in network. Remember, calling state-changing function of your smart contract, will be executed on all nodes in Ethereum network.

To use Ethereum decentralised apps (also called “dapps”) we need wallet, which unlocks our Ether (Ethereum currency) and signs transaction. Currently, you can use official Mist browser, or Chrome extension MetaMask. Mist will first download whole blockchain into your machine (several GB). MetaMask uses remote server to access blockchain. For this tutorial you don’t need any of it, we will see, that we can unlock account in REPL and don’t need to use any wallet.

This is far from complete or best description of Ethereum, if you’re still unclear what it is, please search internet, it’s full of it :)

How to setup private Ethereum blockchain

For development we will create private blockchain on our machine, because it’s faster, smaller and customisable. Other than that, Ethereum has 2 public blockchains. 1. Mainnet, for real stuff. 2. Testnet for testing & dev purposes.

  1. Install geth
  2. Save this script somewhere as “check-work.js”. It is to start mining new block only when there are some transactions. This is good for development since, mining all the time would be awful for your CPU.
  3. To start private blockchain run code below. Replace -datadir param with path, where you want to store it.
geth --dev --maxpeers 0 --port 30304 --shh --rpc --rpcport 8545 --keystore devnet --datadir /Users/matus/Documents/eth-devnet/ --minerthreads 1 --rpccorsdomain "*" --rpcapi "eth,net,web3,personal,shh"

4. In new terminal window let’s attach to geth console. Replace path with yours.

geth attach ipc://Users/matus/Documents/eth-devnet/geth.ipc

In geth console, let’s see coinbase address. It is where newly issued Ether goes to, when you mine a new block.

> eth.coinbase
"0x6fce64667819c82a8bcbb78e294d7b444d2e1a29"

If you don’t have any, you need to create a new account

> personal.newAccount("somePassword")
"0xb2a5481b18e3d4edb52af1514397ef41b0e9eaff"

Now, in accounts you should see at least one address

> eth.accounts
["0x6fce64667819c82a8bcbb78e294d7b444d2e1a29" "0xb2a5481b18e3d4edb52af1514397ef41b0e9eaff"]

To see balance of your coinbase in ether use this command. (Wei is smallest unit of Ether)

> web3.fromWei(eth.getBalance(eth.coinbase), "ether")
2226.51951314

If you don’t have any, you need to mine some.

miner.start()

After few minutes, you’ll have more than enough, and you can stop it. Check your coinbase balance.

miner.stop()

5. Now, you can close existing console and open new one with check-work.js script. Again, replace paths according to your environment.

geth --preload ~/Documents/eth-devnet/check-work.js attach ipc://Users/matus/Documents/eth-devnet/geth.ipc

Note, there is faster way to setup local blockchain with in-memory testrpc. I didn’t have best experience with it, but by the time you’re reading this it’s probably much better already. Beware it may behave slightly different than real blockchain, but definitely give it a shot!

Okay, now we’re all set to start developing!

Creating a smart contract

As I mentioned above, we’re gonna create a very simple Twitter. Code for this project can be found here. I recommend you to clone it and follow along. Our Twitter backend is one smart contract written in Solidity. I’m not going too much into explaining Solidity code, since this is about Clojurescript :) One day maybe, Clojure will also target EVM (Ethereum Virtual Machine) and we’ll be able to write smart contracts in Clojure -> ClojurETH :)

First you need to install Solidity compiler. If you have it, you should be able to execute following command

solc --version

If you cloned project, you can recompile contract on change by running

lein auto compile-solidity

So this is all our backend code. On Github here. Basically, we will use just 2 functions: one constant getSettings and other one state-changing addTweet

After compiling, it will create SimpleTwitter.json. That’s what we need to deploy contract.

What is Web3.js and how to use it in Clojurescript?

Web3.js is a JS library for interacting with Ethereum nodes. I created 2 Clojurescript libraries for making Web3.js super easy to use:

  1. cljs-web3 is a interop library
  2. re-frame-web3-fx is a re-frame effect handler for interacting with web3

Please read README of both of those libraries as it’s essencial for this app. Good news is once you understand them, working with web3.js is trivial.

Running front-end code

I assume, you have still running private blockchain with RPC on port 8545, with check-work.js script running in background in other terminal window.

Let’s start our app

lein repl
(clojurescript-ethereum-example.core/-main)
(figwheel-sidecar.repl-api/start-figwheel! (figwheel-sidecar.config/fetch-config))
(figwheel-sidecar.repl-api/cljs-repl)

Now you should have app running on http://localhost:6655/ , with clojurescript REPL. Open JS console also.

Deploying contract to a private blockchain

There’s numerous ways how you can deploy compiled contract. Since, we’re Clojure developers, we’re going to do it via REPL ;)

First we need to unlock an account that will deploy contract. For deploying contract you need “gas” also. I’ve prepared line for you in handlers.cljs. Replace address with your first address from eth.accounts in geth console, and instead of “m”, your password. Note, don’t use MetaMask extension or Mist now, just plain Chrome.

Send to REPL:

(dispatch [:blockchain/unlock-account "0x6fce64667819c82a8bcbb78e294d7b444d2e1a29" "m"])

In JS console, you should see

Account was unlocked.

Let’s see unlocking piece of code.

As you can see there’s not much going on. This basically just instructs ethereum node to call personal.unlockAccount

In handlers.cljs I’ve prepared for you a line with which you can start deploying contract.

(dispatch [:contract/fetch-compiled-code [:contract/deploy-compiled-code]])

check-work.js script should kick in, mine a new block and you should see in console something like

Contract deployed at 0x6c91a36b8e43d54dd0ce8d214e0fc1e8c6a0c296

Let’s see code:

First we download compiled contract code at /contracts/build/SimpleTwitter.json by regular ajax request. Then we deploy it with contract-new function, which under the hood translates into JS similar to this.

If you successfully deployed contract and have its address, paste it into default-db here and refresh the app. If you have some Ether, you should be able to post a tweet with UI now.

Understanding code

Let’s go event by event through code to see how front-end code interacts with a blockchain and the smart contract.

At the beginning, we initialise DB with default db. We also need fetch ABI (Application Binary Interface) of our smart contract. This is needed to create JS instance of the contract. :provides-web3? is there just to check if we’re using MetaMask or Mist or Dev env. If yes, we get available accounts. The same as you got from geth console by eth.accounts, but asynchronously, since we’re in a browser.

Once we load user’s addresses, we want also load balances for them — how much Ether user has. Simple by passing :watch? true, we get real time updates for balance changes on these addresses. User can spend/add Ether to this address in completely different app, and our UI will be real-time updated! Not too bad for such a small snippet, can your local bank’s web app do this? :)

Once we load contract’s ABI, we create instance. That’s easy. Next we setup event listener for contract event. Remember that event onTweetAdded in contract’s source code? That’s exactly listening for that event. But it’s more than just a listening for upcoming events. Since we passed {:from-block 0}, it will rewind whole past of those events, basically firing :contract/on-tweet-loaded on each tweet ever happened (and upcoming tweets also). This way we get all tweets. Obviously if we had thousands of tweets this would bloat browser, and we would have to choose different way to load data. If we want to listen just for newly created events we can just pass {:from-block “latest”}
Lastly, there’s :get-settings, which calls our contract’s getSettings constant method. Since, getSettings is constant, it’s completely free to call.

Okay, so :contract/on-tweet-loaded is called every-time new tweet is added, we just add it to DB, and re-frame will take care of displaying it in DOM.

This is callback for our getSettings constant function. It just loads come contract’s settings, so we can make use of them in UI.

Finally, posting a new tweet! Account must be unlocked for that. 
:add-tweet will call our contract’s addTweet. This is state changing, therefore we must pass gas limit and address for transaction. 
:new-tweet/confirmed will be fired once user confirms transaction, which in case of unlocked account is instant, but if user was using MetaMask or Mist, it’s fired after he clicks on “Accept” on a popup window. 
Next :new-tweet/transaction-receipt-loaded is called once transaction is processed by blockchain. It returns what’s called transaction receipt. Receipt contains info about how much gas transaction used. If it’s exactly the same amount as gas limit you passed, you can be pretty sure (not 100% tho) that something went wrong. This is currently the only way to check if your contract thrown exception.

And that’s it! This is basically all “brain” of the app. Rest is just some regular re-frame & reagent stuff.

As you can see, once you have everything set up (blockchain running, contract deployed), creating front-end for a contract is very simple. It’s just listening for events and calling contract’s methods. To see more advanced Clojurescript Ethereum app see emojillionaire on Github.

Thank you for reading this :)