mana-ethereum / ethereumex Goto Github PK
View Code? Open in Web Editor NEWElixir JSON-RPC client for the Ethereum blockchain 0x063D3d782598744AF1252eBEaf3aA97D990Edf72
License: MIT License
Elixir JSON-RPC client for the Ethereum blockchain 0x063D3d782598744AF1252eBEaf3aA97D990Edf72
License: MIT License
Unchecked dependencies for environment dev:
* exthereumex (https://github.com/exthereum/ethereumex.git)
could not find an app file at "_build/dev/lib/exthereumex/ebin/exthereumex.app". This may happen if the dependency was not yet compiled, or you specified the wrong application name in your deps, or the dependency indeed has no app file (then you can pass app: false as option)
** (Mix) Can't continue due to errors on dependencies
To convert the balance into an integer:
balance_bytes
|> String.slice(2..-1)
|> Base.decode16!(case: :lower)
|> TypeDecoder.decode_raw([{:uint, 256}])
|> List.first
If balance is 0 we get "0x0" as the return and we cant pipe that to Base.decode16!(case: :lower) without an error. We need to pad leading zero in this case.
I think a decode/encode function is needed in the library.
Hi,
How do you feel about removing the user home prefix on the IPC path? With the current approach there is no way to set an absolute path. e.g. /tmp/my.socket
I don't know what happend, but Ethereumex doesn't work with geth1.9.
When I try to call Ethereumex.HttpClient.web3_client_version I get {:error, :econnrefused}.
But with Parity it works good. I get
{:ok, "Parity-Ethereum//v2.6.0-beta-e38293b-20190708/x86_64-linux-gnu/rustc1.36.0"}
When I was messing around with the parity versions for the CircleCI build, I noticed that there are 4 test failures.
Can reproduce this with versions of parity between 2.0.6 and 1.11
I haven't had time to dig in-depth, but it looks to me like 0.8 fails with basic auth requests
such that:
username:[email protected]/my_blockchain
to Infura, and downgrading to 0.7.1 things work just fine.
We can pass url
as an additional option, but not http_headers
. For example - for basic auth
Please see PR #111 for a possible fix
Is there a way to serialize the constructor like here?
https://github.com/ethereum/web3.js/blob/develop/lib/web3/contract.js#L36L46
Hello, is there a way I can watch for new blocks? Similar to the Javascript web3 API:
const filter = web3.eth.filter('latest');
filter.watch((e,r) => {
console.log('got new block = ', r);
});
How can I achieve something similar using Ethereumex? I have checked the documentation here, but I think it isn't updated yet.
I am trying the following configs:
config :ethereumex,
url: "https://mainnet.infura.io/v3/MY_KEY",
request_timeout: :infinity
But I get the following response:
{: error, :timeout}
for some requests. Is this response coming from Infura?
In order to get through this, I think I will have to keep making the same call until I get {:ok, response}
. Is this the suggested way?
hi there, thanks for your good work.
when i send transaction using Ethereumex.HttpClient.eth_send_raw_transaction(signed_transaction)
, sometime errors occur just like that:
no match of right hand side value: {:error, %{"code" => -32000, "message" => "rlp: non-canonical integer (leading zero bytes) for *big.Int, decoding into (types.Transaction)(types.txdata).R"}}
if i change any part of transaction, nonce
, gas_price
, etc., errors disappear.
i searched for a long time, it looks like a problem of RLP, mentioned years before:
is there something we ignored unintentionally in Ethereumex or ExRLP? and is it possible to resolve it?
thanks in advance.
IpcClient requests sometimes fail with a timeout error
22:08:52.499 [error] GenServer Ethereumex.IpcClient terminating
** (stop) exited in: GenServer.call(IpcServer, {:request, "{\"params\":[\"0x5D782\",true],\"method\":\"eth_getBlockByNumber\",\"jsonrpc\":\"2.0\",\"id\":914}"}, 5000)
** (EXIT) time out
(elixir) lib/gen_server.ex:924: GenServer.call/3
(ethereumex) lib/ethereumex/ipc_client.ex:8: Ethereumex.IpcClient.post_request/2
(ethereumex) lib/ethereumex/client/server.ex:30: Ethereumex.Client.Server.handle_call/3
(stdlib) gen_server.erl:636: :gen_server.try_handle_call/4
(stdlib) gen_server.erl:665: :gen_server.handle_msg/6
(stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3
Last message (from #PID<0.74.0>): {:request, %{"jsonrpc" => "2.0", "method" => "eth_getBlockByNumber", "params" => ["0x5D782", true]}, []}
State: {Ethereumex.IpcClient, 914}
Client #PID<0.74.0> is alive
(kernel) code_server.erl:140: :code_server.call/1
(kernel) error_handler.erl:41: :error_handler.undefined_function/3
(elixir) lib/kernel/cli.ex:75: Kernel.CLI.format_error/3
(elixir) lib/kernel/cli.ex:157: Kernel.CLI.print_error/3
(elixir) lib/kernel/cli.ex:119: anonymous fn/3 in Kernel.CLI.exec_fun/2
address = Application.get_env(:ethereumex, :owner) |> String.slice(2..-1) |> Base.decode16!(case: :mixed)
contract_address = Application.get_env(:ethereumex, :contract_address)
abi_encoded_data = ABI.encode("setTest(uint, string)", [200, "foo"]) |> Base.encode16(case: :lower)
balance_bytes = Ethereumex.HttpClient.eth_send_transaction(%{
data: "0x" <> abi_encoded_data,
to: contract_address,
from: address,
gas: "0xc350",
gasPrice: nil
})
Do I need to do a transaction first?
I got:
** (Jason.EncodeError) invalid byte 0xFC in <<252, 83, 33, 125, 207, 109, 79, 21, 193, 116, 8, 255, 147, 246, 107, 252, 40, 46, 165, 230>>
The README has not a clear example of sending transactions. I would be charmed to add it, but first I should know how to do that :)
The library is purely documented. Fix that
Hi,
For the HttpClient module, should we set a default Content-Type
header?
It can either be in the default config or it can be merged with the default config to make it more convenient.
What do you think?
iex(9)> ABI.encode("balanceOf(address)", [address]) |> Base.encode16(case: :lower)
** (FunctionClauseError) no function clause matching in ABI.TypeEncoder.maybe_encode_unsigned/1
The following arguments were given to ABI.TypeEncoder.maybe_encode_unsigned/1:
# 1
{:ok,
<<91, 29, 177, 3, 188, 236, 87, 218, 123, 135, 200, 13, 34, 181, 46, 75, 64,
12, 91, 43>>}
Attempted function clauses (showing 2 out of 2):
defp maybe_encode_unsigned(bin) when is_binary(bin)
defp maybe_encode_unsigned(int) when is_integer(int)
(ex_abi 0.5.9) lib/abi/type_encoder.ex:272: ABI.TypeEncoder.maybe_encode_unsigned/1
(ex_abi 0.5.9) lib/abi/type_encoder.ex:224: ABI.TypeEncoder.encode_uint/2
(ex_abi 0.5.9) lib/abi/type_encoder.ex:100: ABI.TypeEncoder.do_encode_type/4
(ex_abi 0.5.9) lib/abi/type_encoder.ex:83: ABI.TypeEncoder.do_encode/4
(ex_abi 0.5.9) lib/abi/type_encoder.ex:21: ABI.TypeEncoder.encode/3
Could you please provide an example of sending ERC20 tokens using Ethereumex or ExW3? Do I need to use some ABI like this one?
// transfer
{
"constant": false,
"inputs": [
{
"name": "_to",
"type": "address"
},
{
"name": "_value",
"type": "uint256"
}
],
"name": "transfer",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"type": "function"
}
I would like to send USDT from one Ethereum address to another.
It makes more sense to configure RPC client using parameters (not in config)
Otherwise it's impossible to use it as real RPC client.
For example right now it's not possible to connect to 2 different EVMs at same time or reconfigure it without pain.
What do you think about something like this:
iex> config = %Ethereumex.Config{http_url: "https://localhost:8545"}
iex> Ethereumex.HttpClient.web3_client_version(config)
{:ok, "Parity//v1.7.2-beta-9f47909-20170918/x86_64-macos/rustc1.19.0"}
Hi there,
After upgrading ethereumex
from v0.6.0 to v0.6.1 my test suite fails/gets stuck with some odd error. After some debugging I find error to be a very implicit/fundamental change in the way http pool is handled due to internal httpoison
upgrade:
https://github.com/izelnakri/eth/blob/master/test/support/eth_client.exs#L13
https://github.com/izelnakri/eth/blob/master/test/support/eth_client.exs#L42 -> following code first returns hundreds/thousands of {:error, :econnrefused}
while waiting for the spawned processes/ganache-cli http server to start and then it subsequent requests dont seem to get sent, and I assume the previous ones on the pool now returns {:error, :checkout_timeout}
after ganache-cli starts serving the requests.
I'm still digging to see what has changed, the test suite doesnt work on the latest ethereumex
as well. Most likely, our http pooling logic has changed and needs adjustments. I'll share more information once I have better understanding.
More info on the {:error, :checkout_timeout}
: edgurgel/httpoison#359
Looks like telemetry was added recently as an optional dependency - but it doesn't look optional
iex(1)> Ethereumex.HttpClient.web3_client_version
** (UndefinedFunctionError) function :telemetry.execute/3 is undefined (module :telemetry is not available)
:telemetry.execute([:ethereumex], %{counter: 1}, %{method_name: "web3_clientVersion"})
(ethereumex 0.6.1) lib/ethereumex/counter.ex:59: Ethereumex.Counter.inc/2
(ethereumex 0.6.1) lib/ethereumex/client/base_client.ex:505: Ethereumex.HttpClient.prepare_request/1
(ethereumex 0.6.1) lib/ethereumex/client/base_client.ex:485: Ethereumex.HttpClient.server_request/2```
I've tried to follow the document but got this error. So need in detail example for this.
iex(7)> abi_encoded_data = ABI.encode("transferFrom(address,address,uint)", ["0x7e5f4552091a69125d5dfcb7b8c2659029395bdf", "0x7e5f4552091a69125d5dfcb7b8c2659029395bdf", 123])
** (RuntimeError) Data overflow encoding uint, data `0x7e5f4552091a69125d5dfcb7b8c2659029395bdf` cannot fit in 160 bits
It looks like request ids are being incremented by the number of parameters in the request. It's possible this is happening for batch requests. Should this happen for single requests as well? It's possible this is set up correctly. If so we can close this. If not we should update it to increment the request id by 1.
Is there any plan to include support for websocket?
I saw there is PR for this, but unfortunately the owner has removed it.
If necessary I have time to contribute to it. Please give me a guidance
geth
returns an empty-body HTTP response, when it happens to be brought down when a request is being handled (so it seems at least). Such response is coded 200
and as such is passed onto Jason.decode
which gives a Jason.DecodeError
for ""
.
Firstly: maybe you are aware of why would geth
be doing this to me or perhaps have run into the same issue? Asking in case this is purely a geth
issue (or myself erring somewhere).
Secondly: in case geth's behavior makes at least some sense, maybe ethereumex
's HttpClient
could interpret ""
response body as something that it shouldn't pass onto Jason
for a more sensible error message?
Steps to reproduce
1/ IO.inspect
here
2/ run geth
, connect to it and run a lot of JSON rpc queries against it (I'm giving one which should actually work, when connected to Rinkeby, in the console dump below)
3/ Ctrl+C the geth
4/ get this (I'm pattern matching an {:ok...
response from HttpClient
), mind the empty body and error:
{:ok,
%HTTPoison.Response{
body: "",
headers: [
{"Content-Type", "application/json"},
{"Date", "Wed, 06 Feb 2019 11:10:42 GMT"},
{"Content-Length", "0"}
],
request: %HTTPoison.Request{
body: "{\"id\":32523,\"jsonrpc\":\"2.0\",\"method\":\"eth_getTransactionReceipt\",\"params\":[\"0x058af4226c60a5fd54158d7e36d7614d445d071ecc595206abc44b0938272491\"]}",
headers: [{"Content-Type", "application/json"}],
method: :post,
options: [hackney: [pool: :default], recv_timeout: 60000],
params: %{},
url: "http://localhost:8546"
},
request_url: "http://localhost:8546",
status_code: 200
}}
<snip>
** (EXIT) an exception was raised:
** (MatchError) no match of right hand side value: {:error, {:invalid_json, %Jason.DecodeError{data: "", position: 0, token: nil}}}
<snip>
I see this is slightly along the lines of #19, just my issue here is that ""
could perhaps be treated as a special case?
I'm curious how you guys are calling smart contracts and executing functions, I'm bit struggling with compiling and functions from smart contracts. I'm working on an oracle service which should use eth_call
function and I need to set some params on data
params. Any tutorial or advice would be awesome.
Currently, the only way send requests to a node is to set the configuration variable as such: Application.put_env(:ethereumex, :url, node)
Would it be possible to have an option to pass the node on the function call instead of having it at the application level. Something like this: Ethereumex.HttpClient.eth_gas_price(node: "http://localhost:8545")
I can help with a PR if you are ok with this option.
Hello friends, we have a requirement for a project to integrate with multiple networks/endpoints (we need ethereum and polygon). I understand that this is not supported. If you think it would be useful for the project I could try to implement that functionality as a PR with a care to be backwards compatible with the single provider version (i.e people that use a single provider won't need to change anything to their code). I'd be happy to see your opinions.
Kind regards,
Serafeim
Sorry if I am missing some page on issue guidelines or something. I looked around a bit but couldn't find one. Here is the TL;DR
When running Ethereumex.HttpClient.web3_client_version()
against an Anvil node, Anvil will respond with missing Content-Type header. It seems that somewhere along the way the library drops that header from this request at least. I know this because you can make this call Ethereumex.HttpClient.web3_client_version(http_headers: [{"Content-Type", "application/json"}])
and Anvil accepts the request. Since this request is a POST, and you are POSTing JSON, it seems appropriate to add the Content-Type header to the HTTP Request. Other RPCs like Alchemy happen to be a bit more lenient.
Reference:
It would be great if there was a generic client protocol that could be implemented by specific providers, like http, websocket, and ipc. It seems that a lot of the code for this to happen already exists, but a found a few difficulties when trying to extend this library with a websocket client.
WebSockex.start_link("ws://*url*", __MODULE__, state)
since the GenServer details are abstracted away by Ethereumex.Client.ServerThis issue probably relates to #8 as well, in that a generic client would allow for a client to simply implement the protocol. Also, I have Ethereumex.HttpClient hardcoded in a bunch of places in ExW3, which isn't ideal.
Ideally the API would be similar to web3.js where the user can manually set the provider url and type.
Implement Ipc Client like HttpClient is implemented (with Ethereumex.Client.Macro)
defmodule Ethereumex.IpcClient do
use Ethereumex.Client.Macro
def request(payload) do
// request with Unix socket
end
end
Currently there is no decent adapter for Unix sockets in Elixir ecosystem. I think we should try to use procket but its api is too ugly and low level.
After finishing ipcClient HttpClient in SmartContract modules should be replaced with the new client.
I'm interested in starting a PR to add the websocket transport protocol to this package. Is there anyone else that might want to help me with this?
We can use blockscout's JSON-RPC module for reference.
I'm trying to connect to geth
and receiving following error:
23:29:41.836 [info] Application ethereumex exited: Ethereumex.start(:normal, []) returned an error: shutdown: failed to start child: :worker
** (EXIT) an exception was raised:
** (MatchError) no match of right hand side value: {:error, {:bad_return_value, {:error, :enoent}}}
(poolboy) src/poolboy.erl:275: :poolboy.new_worker/1
(poolboy) src/poolboy.erl:296: :poolboy.prepopulate/3
(poolboy) src/poolboy.erl:145: :poolboy.init/3
(stdlib) gen_server.erl:374: :gen_server.init_it/2
(stdlib) gen_server.erl:342: :gen_server.init_it/6
(stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3
23:29:41.836 [error] GenServer #PID<0.242.0> terminating
** (MatchError) no match of right hand side value: {:error, {:bad_return_value, {:error, :enoent}}}
(poolboy) src/poolboy.erl:275: :poolboy.new_worker/1
(poolboy) src/poolboy.erl:296: :poolboy.prepopulate/3
(poolboy) src/poolboy.erl:145: :poolboy.init/3
(stdlib) gen_server.erl:374: :gen_server.init_it/2
(stdlib) gen_server.erl:342: :gen_server.init_it/6
(stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3
Last message: {:EXIT, #PID<0.241.0>, {{:badmatch, {:error, {:bad_return_value, {:error, :enoent}}}}, [{:poolboy, :new_worker, 1, [file: 'src/poolboy.erl', line: 275]}, {:poolboy, :prepopulate, 3, [file: 'src/poolboy.erl', line: 296]}, {:poolboy, :init, 3, [file: 'src/poolboy.erl', line: 145]}, {:gen_server, :init_it, 2, [file: 'gen_server.erl', line: 374]}, {:gen_server, :init_it, 6, [file: 'gen_server.erl', line: 342]}, {:proc_lib, :init_p_do_apply, 3, [file: 'proc_lib.erl', line: 249]}]}}
State: {:state, {#PID<0.242.0>, :poolboy_sup}, :simple_one_for_one, {[Ethereumex.IpcServer], %{Ethereumex.IpcServer => {:child, :undefined, Ethereumex.IpcServer, {Ethereumex.IpcServer, :start_link, [[path: "/home/den/home/den/.ethereum/geth.ipc", ipc_request_timeout: 60000]]}, :temporary, 5000, :worker, [Ethereumex.IpcServer]}}}, {:sets, {:set, 0, 16, 16, 8, 80, 48, {[], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []}, {{[], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []}}}}, 0, 1, [], 0, :poolboy_sup, {Ethereumex.IpcServer, [path: "/home/den/home/den/.ethereum/geth.ipc", ipc_request_timeout: 60000]}}
** (Mix) Could not start application ethereumex: Ethereumex.start(:normal, []) returned an error: shutdown: failed to start child: :worker
** (EXIT) an exception was raised:
** (MatchError) no match of right hand side value: {:error, {:bad_return_value, {:error, :enoent}}}
(poolboy) src/poolboy.erl:275: :poolboy.new_worker/1
(poolboy) src/poolboy.erl:296: :poolboy.prepopulate/3
(poolboy) src/poolboy.erl:145: :poolboy.init/3
(stdlib) gen_server.erl:374: :gen_server.init_it/2
(stdlib) gen_server.erl:342: :gen_server.init_it/6
(stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3
Config file:
config :ethereumex,
client_type: :ipc,
ipc_path: "/home/den/.ethereum/geth.ipc",
ipc_worker_size: 5,
ipc_max_worker_overflow: 2,
ipc_request_timeout: 60_000
Geth:
den@nb:~$ .config/Ethereum\ Wallet/binaries/Geth/unpacked/geth --syncmode light
...
INFO [12-21|23:36:21.485] IPC endpoint opened url=/home/den/.ethereum/geth.ipc
...
I'm trying to call one of such functions:
Ethereumex.HttpClient.eth_get_block_by_number("0x0", false)
(boolean at 2nd argument) and all works fine but dialyzer complains:
apps/omg_eth/lib/eth.ex:95: The call 'Elixir.Ethereumex.HttpClient':eth_get_block_by_number(<<_:16,_:_*8>>,'false') will never return since it differs in the 2nd argument from the success typing arguments: (binary() | [binary()] | map(),binary() | [binary()] | map())
looking at
it looks like the specs havebinary()
in that spot.
The function doesn't work with "false":
iex(37)> Ethereumex.HttpClient.eth_get_block_by_number("0x0", "false")
{:error,
%{
"code" => -32602,
"message" => "invalid argument 1: json: cannot unmarshal string into Go value of type bool"
}}
I'm not entirely sure if I'm missing something or the specs should be updated for all boolean arguments like this
https://github.com/plataformatec/mox
Jose Valim's post about behaviours will be helpful - http://blog.plataformatec.com.br/2015/10/mocks-and-explicit-contracts/
Not sure if this belongs in ABI or here, but it would be helpful if there was an example for encoding arguments for a transaction.
You're ABI library outputs binaries, but http poison does not have a protocol for bit strings. I am relatively new to Elixir so perhaps there is something I'm missing.
I see there is a sign method, but I don't see a verify method. How can I verify a signature?
Thanks for writing an API that one-to-one matches the Ethereum JSON RPC. The send transaction doesnt seem to work though:
error code:
{:error,
%{"code" => -32000,
"message" => "TypeError: callback is not a function\n at StateManager.queueTransaction (/usr/local/lib/node_modules/ethereumjs-testrpc/build/cli.node.js:83065:5)\n at GethApiDouble.eth_sendTransaction (/usr/local/lib/node_modules/ethereumjs-testrpc/build/cli.node.js:82631:14)\n at GethApiDouble.handleRequest (/usr/local/lib/node_modules/ethereumjs-testrpc/build/cli.node.js:82434:10)\n at next (/usr/local/lib/node_modules/ethereumjs-testrpc/build/cli.node.js:52153:18)\n at VmSubprovider.handleRequest (/usr/local/lib/node_modules/ethereumjs-testrpc/build/cli.node.js:59291:12)\n at next (/usr/local/lib/node_modules/ethereumjs-testrpc/build/cli.node.js:52153:18)\n at GethDefaults.handleRequest (/usr/local/lib/node_modules/ethereumjs-testrpc/build/cli.node.js:82339:12)\n at next (/usr/local/lib/node_modules/ethereumjs-testrpc/build/cli.node.js:52153:18)\n at FilterSubprovider.handleRequest (/usr/local/lib/node_modules/ethereumjs-testrpc/build/cli.node.js:58850:7)\n at next (/usr/local/lib/node_modules/ethereumjs-testrpc/build/cli.node.js:52153:18)"}}
I'm having some issues using a pretty loaded geth-node as the rpc server. It seems like sometimes it doesn't return a json body, causing https://github.com/exthereum/ethereumex/blob/master/lib/ethereumex/http_client.ex#L24 to error out with an Undefined token on position 0
error in Poison.decode!
, crashing the server. If ethereumex
is under load and crashes more than 5 times in a quick succession, it brings down the whole ethereumex
application (and the whole VM if it's running in MIX_ENV=prod
).
One solution would be using the with
macro in decode_body
and post_request
, gracefully propagating http- as well as parsing errors. This would make the whole system much more robust.
Hi! Thanks for the great work.
Running ethereum
0.7.1, httppoison
1.8.0
I keep getting this error
{:error,
{:invalid_json,
%Jason.DecodeError{
data: "<html>\r\n<head><title>400 Bad Request</title></head>\r\n<body>\r\n<center><h1>400 Bad Request</h1></center>\r\n<hr><center>openresty</center>\r\n</body>\r\n</html>\r\n",
position: 0,
token: nil
}}}
for whatever request I make, using the HTTPClient provided. I'm using a blockdaemon node.
I cloned the repo and added a IO.inspect
after the httppoison
request:
{:ok,
%HTTPoison.Response{
body: "<html>\r\n<head><title>400 Bad Request</title></head>\r\n<body>\r\n<center><h1>400 Bad Request</h1></center>\r\n<hr><center>openresty</center>\r\n</body>\r\n</html>\r\n",
headers: [
{"Date", "Tue, 09 Nov 2021 00:10:54 GMT"},
{"Content-Type", "text/html"},
{"Content-Length", "154"},
{"Connection", "close"},
{"Server", "nginx"}
],
request: %HTTPoison.Request{
body: "{\"id\":1,\"jsonrpc\":\"2.0\",\"method\":\"eth_getBlockByNumber\",\"params\":[\"0x0\",false]}",
headers: [{"Content-Type", "application/json"}],
method: :post,
options: [],
params: %{},
url: "https://ethshared.bdnodes.net?auth={{KEY}}"
},
request_url: "https://ethshared.bdnodes.net?auth={{KEY}}",
status_code: 400
}}
I suspect this may be a problem with HTTPoison but couldn't figure out why. I wrote a BaseClient
implementation that's almost the same as HttpClient
, but uses Tesla
instead, and it works, so the problem is probably not my node.
defmodule MyEthereumexClient do
@moduledoc false
use Ethereumex.Client.BaseClient
alias Ethereumex.Config
@type opt :: {:url, String.t()}
@type empty_response :: :empty_response
@type invalid_json :: {:invalid_json, any()}
@type http_client_error :: {:error, empty_response() | invalid_json() | any()}
@spec post_request(binary(), [opt()]) :: {:ok, any()} | http_client_error()
def post_request(payload, opts) do
url = Keyword.get(opts, :url) || Config.rpc_url()
[base_url, query] = String.split(url, "?")
%{"auth" => auth} = URI.decode_query(query)
client =
Tesla.client([
{Tesla.Middleware.BaseUrl, base_url},
{Tesla.Middleware.Headers, [{"content-type", "application/json"}]}
])
case Tesla.post(client, "", payload, query: [auth: auth]) do
{:ok, response} ->
%Tesla.Env{body: body, status: code} = response
decode_body(body, code)
error ->
error
end
end
@spec decode_body(binary(), integer()) :: {:ok, any()} | http_client_error()
defp decode_body(body, code) do
case Jason.decode(body) do
{:ok, decoded_body} ->
case {code, decoded_body} do
{200, %{"error" => error}} -> {:error, error}
{200, result = [%{} | _]} -> {:ok, format_batch(result)}
{200, %{"result" => result}} -> {:ok, result}
_ -> {:error, decoded_body}
end
{:error, %Jason.DecodeError{data: ""}} ->
{:error, :empty_response}
{:error, error} ->
{:error, {:invalid_json, error}}
end
end
end
I'm quite happy with the custom impl, but perhaps other people are having the same problems as me, and we could try to solve it.
if you have a eth_call like this in the code:
Ethereumex.HttpClient.eth_call(%{to: contract, data: data}, "latest", opts)
where opts = [url: "http://localhost:8545"]
dialyzer will yield:
The function call will not succeed.
will never return since it differs in arguments with
positions 3rd from the success typing arguments:
(binary(), any(), [{:batch, true}])
#todo need to include a small example project
Most tests are currently not getting run on CI and there are several that don't run locally.
CI currently only runs eth
& batch
mix test --include eth --include batch
https://github.com/mana-ethereum/ethereumex/blob/master/.circleci/config.yml#L41
I've opened up a draft PR to highlight the tests that are currently broken
Many thanks for the amazing library!
I was thinkiing a way to have a really robust way to be in synch with Ethereum events, so let me hear if it makes sense:
If a single request fails in a batched http request, it will throw the following error
** (FunctionClauseError) no function clause matching in anonymous fn/1 in Ethereumex.HttpClient.format_batch/1
(ethereumex 0.8.0) lib/ethereumex/client/base_client.ex:472: anonymous fn(%{"error" => %{"code" => -32000, "message" => "execution reverted"}, "id" => 86, "jsonrpc" => "2.0"}) in Ethereumex.HttpClient.format_batch/1
(elixir 1.12.3) lib/enum.ex:1582: Enum."-map/2-lists^map/1-0-"/2
(elixir 1.12.3) lib/enum.ex:1582: Enum."-map/2-lists^map/1-0-"/2
(ethereumex 0.8.0) lib/ethereumex/http_client.ex:34: Ethereumex.HttpClient.decode_body/2
This is because the batch is implicitly assumed to be completely successful and the results will be returned in a tuple {:ok, results = []}
.
Unfortunately handling failing requests is going to require an api change, as currently the only way to map the response back to the request from application code is via the index of the response within the result list - i.e. it's not possible to return anything other than a single list and keep the order intact.
Currently I handle this by including the error map in the response list, but this is pretty dirty and requires custom parsing.
Thanks for this your hard work. I've been following this project's progress. Do you have a roadmap document or other release schedule we can look to determine the client's level of maturity? For instance is the JSON RPC implementation fully compatible with https://github.com/ethereum/wiki/wiki/JSON-RPC ?
Currently we make contract calls by encoding ABI manually.
abi_encoded_data = ABI.encode("transferFrom(address,address,uint)",[from_address, to_address, token_id])
contract_address = "0x123" |> String.slice(2..-1) |> Base.decode16(case: :mixed)
transaction_data = %Blockchain.Transaction{
data: abi_encoded_data,
gas_limit: 100_000,
gas_price: 16_000_000_000,
init: <<>>,
nonce: 5,
to: contract_address,
value: 0
}
|> Blockchain.Transaction.Signature.sign_transaction(private_key)
|> Blockchain.Transaction.serialize()
|> ExRLP.encode()
|> Base.encode16(case: :lower)
Ethereumex.HttpClient.eth_send_raw_transaction("0x" <> transaction_data)
I think we could make a cleaner interface.
Here’s a sketch of the API I propose:
abi = File.read!(“ERC20.abi”)
contract_address = "0x123" |> String.slice(2..-1) |> Base.decode16(case: :mixed)
contract = Ethereumex.Contract.new(%{abi: abi, address: contract_address})
client = Ethereumex.Client.new(%{gas_limit: 100_000, gas_price: 16_000_000_000})
account = Ethereumex.Client.new(%{private_key: private_key, nonce: 5})
{:ok, account, transaction_hash} = contract
|> Ethereumex.call(:transfer_to, from_address, to_address, amount)
|> client.send(account)
This would take some refactoring but I think it would make calling contracts in client code much cleaner.
It’s possible that this is a higher level abstraction than this library is meant to provide. If that’s the case we/I can pull it into a new package. web3ex
?
To recap:
If anyone has any thoughts feel free to comment here or join the mana gitter.
Hi,
I was trying to use the eth_estimate_gas
function, but it looks like it has not been tested and all calls to it were failing.
Here's the error:
{:error, %{"code" => -32602, "message" => "too many arguments, want at most 1"}}
So after looking at the Ethereum JSON RPC specs, I figured that default block parameters can be specified only for certain calls. However, when scrolling through the actual examples, some of them still specify the quantity/tag
param.
But back to the issue at hand.
I removed the default block parameter from the spec, from the function signature and body params and it works as expected. Am I doing something wrong (with your initial implementation) or is it an actual bug and I should submit a PR?
Thanks!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.