# 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](https://eth.wiki/json-rpc/api) relying on library
[Ethreumex](https://hex.pm/packages/ethereumex).
## 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
- Abstract the Etherex contract API () in order to avoid the clients
to know about opaque type `Etherex.contract`. All stuff is already
implemented in Etherex.Contract.Manager.
- 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](#supported-api).
- 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:
```elixir
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
- Install erlang using kerl (likely you will need to install a C compiler and some libraries to be installed in order to build releases): https://github.com/kerl/kerl
- Install elixir using kiex: https://github.com/taylor/kiex
- Install nodejs v12.x: https://nodejs.org/es/download/package-manager/
- Install solc: https://docs.soliditylang.org/en/v0.7.5/installing-solidity.html. Easy in Ubuntu:
```bash
sudo add-apt-repository ppa:ethereum/ethereum
sudo apt-get update
sudo apt-get install solc
```
- Install Ganache and Ethnode and add them to your path
```bash
cd $HOME
npm install ganache ethnode
PATH=$HOME/node_modules/.bin:$PATH
```
### Start Ethereum in a terminal
You can use ethnode
```bash
ethnode openethereum
```
or ganache
```bash
ganache-cli
```
### Compile and start Elixir in other terminal
```bash
mix deps.get
mix compile
iex -S mix
```
## Documentation
Add `etherex` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:etherex, "~> 1.0.0-rc19"}
]
end
```
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc):
```bash
mix docs
```