README.md

# 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
```