Etherex

Etherex is an Elixir high level library for the Ethereum blockchain. It is is based on the methods of the Json RPC API of Ethereum relying on library Ethreumex.

Ethereum Clients (or nodes or EVMs)

  • Ganache(https://github.com/trufflesuite/ganache): It is not an actual Ethereum node but "an Ethereum simulator that makes developing Ethereum applications faster, easier, and safer".
    • Status: all tests pass
    • Version: Ganache CLI v6.12.2 (ganache-core: 2.13.2)
    • Install: use npm
  • Geth(https://geth.ethereum.org/): "The official Go implementation of the Ethereum protocol"
    • Status: some tests pass, but important tests cannot be tested because we need to work in adding accounts by using config/genesis files
    • Version: 1.10.15-stable
    • Install: use apt
  • Openethereum(https://openethereum.github.io/): "Fast and feature-rich Ethereum client." implemented in Rust
    • Status: some tests pass, but important tests cannot be tested because we need to work in adding accounts by using config/genesis files
    • Version: v3.1.0-stable
    • Install: manual installation following instructions (just a binary)
  • Ethnode(https://github.com/vrde/ethnode): It is not an actual Ethereum network is a zero configuration tool to run a local Ethereum node (Openethereum and Geth).
    • Status with Openethereum: most tests pass, but there are two important errors:
      • We cannot get a hash when a transaction "fails" (see test/etherex_errors_test.exs)
      • There is an unexpected error: a transaction that should revert it is not failing (see test/etherex_compose_test.exs)
    • Status with Geth: some tests pass, but important tests arise
    • Version: 0.0.24
    • Install: use npm

Similar Applications

  • ExW3 (https://github.com/hswick/exw3): ExW3 is a wrapper around ethereumex to provide a high level, user friendly json rpc api. This library is focused on providing a handy abstraction for working with smart contracts, and any other relevant utilities.

Note. There is tool already named Etherex (https://github.com/etherex/etherex). The tool allows exchange in Ethereum by using a set of contracts.

TODO

  • If a call returns a contract address, the address cannot be used as an instance() value: thinks about it.
  • Improve error() type: a string is not enough. Our errors rely on responses like
    <     "error": {
    <       "message": "VM Exception while processing transaction: revert not creator",
    <       "code": -32000,
    <       "data": {
    <         "0xda850732121ff8b49f2922a40568ac8ecc2e2b6b46ce2f9a949184debfd33032": {
    <           "error": "revert",
    <           "program_counter": 216,
    <           "return": "0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b6e6f742063726561746f72000000000000000000000000000000000000000000",
    <           "reason": "not creator"
    <         },
    <         "stack": "c: VM Exception while processing transaction: revert not creator\n    at Function.c.fromResults (/home/angel/node_modules/ganache-cli/build/ganache-core.node.cli.js:4:194812)\n    at w.processBlock (/home/angel/node_modules/ganache-cli/build/ganache-core.node.cli.js:42:53376)\n    at runMicrotasks (<anonymous>)\n    at processTicksAndRejections (internal/process/task_queues.js:97:5)",
    <         "name": "c"
    Nevertheless, this message is specifically from Ganache (in non-compliant mode), so in a real blockchain that doesn’t work. The only way to return a result from a state-changing method is to use logs (called events in Solidity), but a revert (which is how require works) also rolls back all logs.The responses eth_sendTranscation in other Ethereum clients can be hash (try test/etherex_error_test.exs with Openethereum or Geth). Just to show the response to the same request in different clients:
    • Ganache:
13:35:12.843 [warn]  %{"code" => -32000, "data" => %{"0x4983a9bb2ef373ca1797b1d692befdda12e4e3f54804a4feff305eb2787daec7" => %{"error" => "revert", "program_counter" => 294, "reason" => "message_of_revert", "return" => "0x08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000116d6573736167655f6f665f726576657274000000000000000000000000000000"}, "name" => "c", "stack" => "c: VM Exception while processing transaction: revert message_of_revert\n    at Function.c.fromResults (/home/angel/node_modules/ganache-cli/build/ganache-core.node.cli.js:4:194812)\n    at /home/angel/node_modules/ganache-cli/build/ganache-core.node.cli.js:42:52863"}, "message" => "VM Exception while processing transaction: revert message_of_revert"}
  • Openethereum:
    12:59:50.382 [warn]  %{"code" => -32015, "data" => "Reverted 0x08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000116d6573736167655f6f665f726576657274000000000000000000000000000000", "message" => "VM execution error."}
  • Geth:
    13:39:19.953 [warn]  %{"code" => 3, "data" => "0x08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000116d6573736167655f6f665f726576657274000000000000000000000000000000", "message" => "execution reverted: message_of_revert"}
    In Openethereum and Geth, the first 4 bytes (08c379a0) are the function selector - they can be discarded in order to get the actual error:
    iex(2)> data = "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000116d6573736167655f6f665f726576657274000000000000000000000000000000"
    iex(3)> data |> Base.decode16!(case: :lower) |> ABI.TypeDecoder.decode_raw([:string])
    ["message_of_revert"]
  • All functions must allow an opts argument to introduce optional parameters (eg. gas, gasPrice, nonce, tag).
  • Compilation of Solidity via RPC is deprecated, avoid making the call, just use the local Solidity compiler.
  • Create a DSL to run Etherex as a "command line tool". This will require Etherex to be an application instead of a library. Ideas to explore: https://samuelmullen.com/articles/customizing_elixirs_iex. In any case, ask Ángel about "bottle", he knows.
  • Design a proper configuration for Etherex to be used in client applications. Likely, the library must be transformed in an application that load configuration on start up. Configuration will rely on Ethereumex configuration. Maybe the only configuration key is URL.
  • Functions related to logs (eth_getFilterChanges, eth_getFilterLogs, eth_getLogs) and log extraction
  • Proper types and their encoding/decoding for transactions, receipts, logs, events, etc. Maybe, all of them can be defined in Etherex.Types.
  • Pay attention to the deprecated functions in the JSON RPC API. For example, Openethereum logs say "eth_accounts is deprecated and will be removed in future versions: Account management is being phased out see", or "eth_sendTransaction is deprecated and will be removed in future versions: Account management is being phased out see". See APIs.
  • Add functions that implements the "Management APIs" (eg. create account, lock/unlock account, etc.)
  • More functions to control the time Etherex.Time (eg. snapshot, revert, increase time)
  • Asynchronous (callbacks) for waiting a transaction to be mined or rejected by the blockchain. Review some common libraries like web3.js (https://web3js.readthedocs.io). For more examples visit https://ethereum.org/en/developers/docs/programming-languages
  • Use metaprogramming to generate an Elixir module with an interface to operations and events. Something like:
    defmodule SolidityExamples.Counter do
    use Etherex.SolidityContract, source: "priv/solidity/Counter.sol"
    end
  • Start Geth with some accounts (do we need a "genesis file" for every client?).
  • Add a new client module for Hyperledger Besu (https://besu.hyperledger.org). Different clients has different characteristics, for instance, besu seems to not expose the list of accounts.
  • The following post is interesting, ilustrative, and crazy!: https://medium.com/coinmonks/common-attacks-in-solidity-and-how-to-defend-against-them-9bc3994c7c18

Supported API

According to Openethereum wiki (https://openethereum.github.io/), these are the APIs although not all of them are enabled:

  • debug
    • debug_getBadBlocks
  • eth (enabled by default in Openethereum)
    • eth_accounts (Etherex.accounts)
    • eth_blockNumber (Etherex.blockNumber)
    • eth_call (Etherex.call)
    • eth_chainId
    • eth_coinbase
    • eth_estimateGas (Etherex.estimate_gas and Etherex.estimate_gas_cost)
    • eth_gasPrice (Etherex.gas_price)
    • eth_getBalance (Etherex.get_balance)
    • eth_getBlockByHash (Etherex.get_block)
    • eth_getBlockByNumber (Etherex.get_block)
    • eth_getBlockTransactionCountByHash (Etherex.get_block)
    • eth_getBlockTransactionCountByNumber (Etherex.get_block)
    • eth_getCode
    • eth_getFilterChanges
    • eth_getFilterLogs
    • eth_getLogs (Etherex.get_events)
    • eth_getStorageAt
    • eth_getTransactionByBlockHashAndIndex (Etherex.get_transaction)
    • eth_getTransactionByBlockNumberAndIndex (Etherex.get_transaction)
    • eth_getTransactionByHash (Etherex.get_transaction)
    • eth_getTransactionCount
    • eth_getTransactionReceipt (Etherex.get_transaction_receipt)
    • eth_getUncleByBlockHashAndIndex
    • eth_getUncleByBlockNumberAndIndex
    • eth_getUncleCountByBlockHash
    • eth_getUncleCountByBlockNumber
    • eth_getWork
    • eth_hashrate
    • eth_mining
    • eth_newBlockFilter
    • eth_newFilter
    • eth_newPendingTransactionFilter
    • eth_protocolVersion (Etherex.protocol_version)
    • eth_sendRawTransaction
    • eth_sendTransaction (Etherex.transfer, Etherex.deploy, Etherex.call_transaction)
    • eth_sign
    • eth_signTransaction
    • eth_submitHashrate
    • eth_submitWork
    • eth_syncing
    • eth_uninstallFilter
  • eth_pubsub (enabled by default in Openethereum)
    • eth_subscribe
    • eth_unsubscribe
  • net (enabled by default in Openethereum)
    • net_listening (Etherex.net_listening)
    • net_peerCount
    • net_version (Etherex.net_version)
  • parity (enabled by default in Openethereum)
    • parity_allTransactionHashes
    • parity_allTransactions
    • parity_call
    • parity_cidV0
    • parity_composeTransaction
    • parity_consensusCapability
    • parity_decryptMessage
    • parity_encryptMessage
    • parity_futureTransactions
    • parity_getBlockHeaderByNumber
    • parity_getBlockReceipts
    • parity_hardwarePinMatrixAck
    • parity_listOpenedVaults
    • parity_listStorageKeys
    • parity_listVaults
    • parity_localTransactions
    • parity_lockedHardwareAccountsInfo
    • parity_releasesInfo
    • parity_signMessage
    • parity_submitWorkDetail
    • parity_verifySignature
    • parity_versionInfo
  • parity_accounts
    • parity_allAccountsInfo
    • parity_changePassword
    • parity_deriveAddressHash
    • parity_deriveAddressIndex
    • parity_exportAccount
    • parity_importGethAccounts
    • parity_killAccount
    • parity_listGethAccounts
    • parity_newAccountFromPhrase
    • parity_newAccountFromSecret
    • parity_newAccountFromWallet
    • parity_removeAddress
    • parity_setAccountMeta
    • parity_setAccountName
    • parity_testPassword
  • parity_pubsub (enabled by default in Openethereum)
    • parity_subscribe
    • parity_unsubscribe
  • parity_set
    • parity_acceptNonReservedPeers
    • parity_addReservedPeer
    • parity_dropNonReservedPeers
    • parity_executeUpgrade
    • parity_hashContent
    • parity_removeReservedPeer
    • parity_setAuthor
    • parity_setChain
    • parity_setEngineSigner
    • parity_setExtraData
    • parity_setGasCeilTarget
    • parity_setGasFloorTarget
    • parity_setMaxTransactionGas
    • parity_setMinGasPrice
    • parity_setMode
    • parity_setTransactionsLimit
    • parity_upgradeReady
  • personal
    • personal_ecRecover
    • personal_listAccounts
    • personal_newAccount
    • personal_sendTransaction
    • personal_sign
    • personal_sign191
    • personal_signTransaction
    • personal_signTypedData
    • personal_unlockAccount
  • private
    • private_call
    • private_composeDeploymentTransaction
    • private_contractKey
    • private_sendTransaction
  • secretstore (enabled by default in Openethereum)
  • signer
    • signer_confirmRequest
    • signer_confirmRequestRaw
    • signer_confirmRequestWithToken
    • signer_generateAuthorizationToken
    • signer_generateWebProxyAccessToken
    • signer_rejectRequest
    • signer_requestsToConfirm
    • signer_subscribePending
    • signer_unsubscribePending
  • trace (enabled by default in Openethereum)
    • Ad-hoc Tracing
      • trace_call
      • trace_callMany
      • trace_rawTransaction
      • trace_replayBlockTransactions
      • trace_replayTransaction
    • Transaction-Trace Filtering
      • trace_block
      • trace_filter
      • trace_get
      • trace_transaction
  • web3 (enabled by default in Openethereum)
    • web3_clientVersion
    • web3_sha3

Non standard API

  • evm (in Ganache-cli)
    • evm_snapshot
    • evm_revert
    • evm_increaseTime
    • evm_mine (Etherex.Time.mine)

Requirements

  • Erlang 21 or 22
  • Elixir 1.7
  • Ganache 6 (depends on Node 12, npm 6)
  • solc (solidity compiler) 0.7.5
  • Experimental: ethnode 0.0.24

Use and tests

Start Ethereum in a terminal

You can use ethnode

ethnote openethereum

or ganache

ganache-cli

Compile and start Elixir in other terminal

mix deps.get
mix compile
iex -S mix

Documentation

Add etherex to your list of dependencies in mix.exs:

def deps do
  [
    {:etherex,
     git: "https://gitlab.com/babel-upm/blockchain/etherex.git",
     tag: "0.3.0"}
  ]
end

Documentation can be generated with ExDoc:

mix docs