Ethereum Smart Contracts and Elixir
This is the first post in a series of blog posts about interacting with Ethereum’s JSON-RPC using Elixir. In this first part we will look at how to get ETH balances for accounts and smart contract tokens. We’ll do this from the ground up, only using low level libraries and a JSON-RPC interface to a full node. In future posts we’ll explore more complex smart contract methods, such as ownership and meta information for the two most common EIP standards: ERC20 and ERC721.
Why Elixir ?
Most use cases for interacting with the Ethereum blockchain involve either a blockchain enabled web application (dApp) or a cryptocurrency wallet. Every time you use a dApp to mutate the state of a smart contract or transfer ETH, you need to sign transactions using your private key. If you’re using a browser extension like MetaMask or a dApp browser, that’s where those private keys live. In either of those cases you will probably use a Javascript library like Ethers or Web3.
While building Sqrly.io we needed to scan hundreds of smart contracts to automatically detect ERC20 or ERC721 tokens owned by an address.
Because Ethereum’s JSON-RPC speaks JSON we can use any language we want to send requests. Elixir is a great choice for this job because of its many awesome features like GenServer, GenStage and Supervision trees. Imagine running a WebSockets connection to a full ETH node that automatically restarts itself if the connection fails. Or massively parelelizing ETH contract calls using GenStage.
ETH Node
The fist thing you’ll need is an Ethereum node that acceps JSON-RPC connections. You can run you own or use Infura.
Elixir Project
If you’re following along, note that I’m using Elixir 1.6.5 and OTP 20.
$ elixir -v
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Elixir 1.6.5 (compiled with OTP 19
We are going to use two great hex packages for this exploration: ethereumex is a great wrapper for a JSON-RPC ethereum node and ABI is an Elixir ABI JSON parser. I’m going to assume you are familiar with the ABI format and how JSON-RPC works - there are plenty of resources out there to get you up to speed.
I’ll start a new mix project and import the dependencies:
$ mix new eth_explore
eth_explore $ mix deps.get
JSON-RPC Client
Next let’s configure the EthereumEx JSON-RPC client. Edit your config.exs file and add the url for your node. We’re using my own Parity node, but as I mentioned earlier you can grab a free one from Infura.
Now let’s iex this and verify that we can connect to the node
iex(1)> Ethereumex.HttpClient.web3_client_version
{:ok, "Geth/v1.8.6-patched-leveldb-8818ab0b/linux-amd64/go1.9.2"}
If you have a wallet address handy you can check your balance as well.
iex(1)> Ethereumex.HttpClient.eth_get_balance("0x456196dfa8eb4534669a01c5933c3bba2ba0d018")
{:ok, "0x15d3be8fee89e4"}
Note that the returned value is hex encoded 256bit number. We’re first going to slice the 0x prefix, then decode the hex into binary, then use Erlang’s decode_unsigned to get the WEI value.
Smart Contract Balances
This is fine for getting an account balance but what if we wanted to the balance for an EIP20 token ? A token balance is really just a mapping of an ETH address to an amout, all stored in the internal state of a smart contract. The EIP20 standard defines a method called balanceOf that allows us to pass an ETH address and retrieve the balance. Because this call doesn’t mutate the state of the smart contract we can call it for free, without having to sign a transaction and spend ETH on gas.
Unline the eth_get_balance method we just used to get our account balance, in order to call the balanceOf of a smart contract we need a few more steps. First off, JSON-RPC does not define a method for us — instead we’ll have to use general the eth_call method and pass the following parameters
- to: Contract address
- data: SHA3 Hash of the method signature
The contract address is simple enough, we’ll use of the many EIP20 Tokens out there. I happen to have a little OMG in my test wallet so I’ll grab their contract: 0xd26114cd6EE289AccF82350c8d8487fedB8A0C07
Normally, a client like ethers js would handle hashing of method signatures and arguments but we’re doing it the hard way — well, almost the hard way. We’re going to use the ABI package to encode the method call:
Notice how we’re passing balanceOf(address) manually in there. Normally this is what the ABI format defines for you: what contract methods are available, what arguments and outputs you can expect.
Now with the data and to portion figured out, the call is simple:
Again, the value is hex encoded. If we decode that similar to how we decoded the account balance we get this:
390000000000000000
With the account balance we know how to convert WEI to ETH, but what about OMG. This is where the decimal value comes in. Since OMG has a decimal value of 18 (sames as WEI to ETH conversion), my balance is 0.39 OMG — not enough to buy a Lambo but patience is key.
Conclusion
We just had the first encounter with Ethereum method calls and encoding/decoding tasks. The advantage with using a low lever library like ABI to encode method calls is we can use a few common method signatures for a variety of smart contracts. For example, the balanceOf method is present in all ERC20 and ERC721 smart contracts. This saves us from having to manage multiple ABI JSON interfaces for example.
In the next post we’ll explore more complicated smart contract calls and look at how we can decode Events.