How Rust and Elixir Read Ethereum and other EVM Smart Contracts: Functional Programming & Blockchain by@leeduckgo

How Rust and Elixir Read Ethereum and other EVM Smart Contracts: Functional Programming & Blockchain

The Series will focus on two functional programming languages: Rust&Elixir. I would like to share the thinking and practices of functional programming. The two repos of Elixir I prefer is **[Ethereumex](https://://**: Elixir client for the. Ethereum Smart Contract. Elixir is Elixir’s Elixir JSON-RPC client for. the. the Ethereum blockchain. Elixir will show the function of reading the. contract by Elixir&Rust in this article.
leeduckgo HackerNoon profile picture


Epic Programmer

twitter social icongithub social icon

The Series will focus on two functional programming languages: Rust&Elixir. I would like to share the thinking and practices of functional programming.

Maybe I will reference other PL for auxiliary:).

I am going to show the function of reading Ethereum Smart Contract by Elixir&Rust in this article. It's important that the program is not only working on Ethereum but also any blockchain that is supporting EVM, for example, Moonbeam on Polkadot! @[email protected]

0x01 Ethereumex & ExABI

The two repos of Elixir I prefer is Ethereumex: Elixir JSON-RPC client for the Ethereum blockchain.


ExABI: The Application Binary Interface (ABI) of Solidity describes how to transform binary data to types which the Solidity programming language understands.

Tips for ABI:

ABI (Application Binary Interface) in the context of computer science is an interface between two program modules.

It is very similar to API (Application Program Interface), a human-readable representation of a code’s interface. ABI defines the methods and structures used to interact with the binary contract, just like API does but on a lower level.


The .abi file is including the description of function interfaces and events by json.

This is an example ABI for HelloWorld.sol

 "constant": true,
 "inputs": [],
 "name": "get",
 "outputs": [{
     "name": "",
     "type": "string"
 "payable": false,
 "stateMutability": "view",
 "type": "function"

0x02 Config for Ethereumex

Firstly, let us add Ethereumex to the field of depsand application in mix.exs!

# mix.exs:
def application do
    mod: {TaiShang.Application, []},
    extra_applications: [:logger, :runtime_tools, :ethereumex]
defp deps do
		 {:ethereumex, "~> 0.7.0"}

Then,in config/config.exs, add Ethereum protocol host params to your config file:

# config.exs
config :ethereumex,
  url: "http://localhost:8545" # node url

0x03 Tx Struct

Show in Elixir

It's very easy to understanding Struct in Elixir by the code.

The tx of Ethereum showed in Elixir:

  nonce: nonce, # counter to ensure the sequence of txs
  gas_price: @gas.price, # gas fee
  gas_limit: @gas.limit, # gas gas limit
  to: bin_to, # addr in binary
  value: 0, # the eth u are going to send
  init: <<>>, # bytecode
  data: data # the data u are going to send

We have just read the data in Ethereum (the writing of data will show in another article), so the nonce is useless. The nonce is needed and changed only when we writing data to the contract.


Executes a new message call immediately without creating a transaction on the blockchain.

  • Object - The transaction call object

  • from: DATA, 20 Bytes - (optional) The address the transaction is sent from

  • to: DATA, 20 Bytes - The address the transaction is directed to

  • gas: QUANTITY - (optional) Integer of the gas provided for the transaction execution eth_call consumes zero gas, but this parameter may be needed by some executions

  • gasPrice: QUANTITY - (optional) Integer of the gasPrice used for each paid gas

  • value: QUANTITY - (optional) Integer of the value sent with this transaction

  • data: DATA - (optional) Hash of the method signature and encoded parameters

  • QUANTITY|TAG - integer block number, or the string "latest", "earliest" or "pending", see the default block parameter

For details see Ethereum Contract ABI in the Solidity documentation


DATA - the return value of the executed contract.

// Request
curl -X POST --data '{"jsonrpc":"2.0","method":"eth_call","params":[{see above}],"id":1}'

// Result
  "jsonrpc": "2.0",
  "result": "0x"


The mechanism of gas is not friendly for freshmen, so we can set gas_price and gas_limit to a certain number now:

@gas %{price: 0, limit: 300_000}

Show in Rust

It's a similar struct in Rust:

/// from:
let tx = TransactionRequest {
        from: accounts[0],
        to: Some(accounts[1]),
        gas: None, // gaslimit
        gas_price: None,
        value: Some(U256::from(10000)),
        data: None,
        nonce: None,
        condition: None

Now there are two params of tx we should handle:

to & data.

0x04 String to Binary for Address

The address using in blockchain(such as 0x769699506f972A992fc8950C766F0C7256Df601f) could be translated to binary in Elixir program:

@spec addr_to_bin(String.t()) :: Binary.t()
def addr_to_bin(addr_str) do
  |> String.replace("0x", "")
  |> Base.decode16!(case: :mixed)

0x05 Smart Contract Function to Data

We would like to generate data by string style of Ethereum functions and params list:

@spec get_data(String.t(), List.t()) :: String.t()
def get_data(func_str, params) do
  payload =
  |> ABI.encode(params)
  |> Base.encode16(case: :lower)

  "0x" <> payload

The examples of "String style of Ethereum functions":

@func %{
    balance_of: "balanceOf(address)",
    token_of_owner_by_index: "tokenOfOwnerByIndex(address, uint256)",
    token_uri: "tokenURI(uint256)",
    get_evidence_by_key: "getEvidenceByKey(string)",
    new_evidence_by_key: "newEvidenceByKey(string, string)",
    mint_nft: "mintNft(address, string)",
    owner_of: "ownerOf(uint256)"

The abstract of string style of eth function is "function_name(param_type1, param_type2,...)"

It's good to go deeper to see the implementation of encode function!

def encode(function_signature, data, data_type \\ :input)

# string type of function to function_selector
# then call encode function again with function_selector
def encode(function_signature, data, data_type) when is_binary(function_signature) do
  |> Parser.parse!()
  |> encode(data, data_type)

def encode(%FunctionSelector{} = function_selector, data, data_type) do
  TypeEncoder.encode(data, function_selector, data_type)

The Struct of FunctionSelector:

iex(5)> ABI.Parser.parse!("baz(uint8)")
  function: "baz",
  input_names: [],
  inputs_indexed: nil,
  method_id: nil,
  returns: [],
  type: nil,
  types: [uint: 8]

It's the work of TypeEncoder.encode to compile data, function_selector and data_type translate to data.

You can see the details here:

0x06 The Translator of Smart Contract Response

It's good to write a TypeTransalator to change the hex data to normal data in Elixir for response of Smart Contract:

defmodule Utils.TypeTranslator do

  def data_to_int(raw) do
    |> hex_to_bin()
    |> ABI.TypeDecoder.decode_raw([{:uint, 256}])
    |> List.first()

  def data_to_str(raw) do
    |> hex_to_bin()
    |> ABI.TypeDecoder.decode_raw([:string])
    |> List.first()

  def data_to_addr(raw) do
    addr_bin =
      |> hex_to_bin()
      |> ABI.TypeDecoder.decode_raw([:address])
      |> List.first()

    "0x" <> Base.encode16(addr_bin, case: :lower)


The functions we are going to choose is based on the response's type, we can fetch it in ABI:

    "constant": true,
    "inputs": [],
    "name": "get",
    "outputs": [{
        "name": "",
        "type": "string"  # The response is string!
    "payable": false,
    "stateMutability": "view",
    "type": "function"

0x07 The Caller in Elixir

It's the last step! Just mix the functions above in a mixed-function, the data reading of the smart contract is working!

For Example - reading the balance of ERC20 token:

@spec balance_of(String.t(), String.t()) :: Integer.t()
def balance_of(contract_addr, addr_str) do
  {:ok, addr_bytes} = TypeTranslator.hex_to_bytes(addr_str)
  data = get_data("balanceOf(address)", [addr_bytes])

  {:ok, balance_hex} =
    Ethereumex.HttpClient.eth_call(%{ # the tx is encapsulated by ethereumex.
    data: data,
    to: contract_addr


0x08 The Caller in Rust

The last one is the example that calling ethereum by rust-web3

extern crate hex;
use hex_literal::hex;

use web3::{
    contract::{Contract, Options},
    types::{U256, H160, Bytes},

async fn main() -> web3::contract::Result<()> {
    let _ = env_logger::try_init();
    let http = web3::transports::Http::new("")?;
    let web3 = web3::Web3::new(http);

    let addr_u8 = hex::decode("7Ad11de6d4C3DA366BC929377EE2CaFEcC412A10").expect("Decoding failed");
    let addr_h160 = H160::from_slice(&addr_u8);

    let contra = Contract::from_json(

    // let acct:[u8; 20] = hex!("f24ff3a9cf04c71dbc94d0b566f7a27b94566cac").into();
    let result = contra.query::<String, _, _,_>("get", (), None, Options::default(), None).await?;
    println!("{}", result);


The Code Example of Elixir in this article: here | The Code Example of Rust in this article: here

react to story with heart
react to story with light
react to story with boat
react to story with money
. . . comments & more!